2009-10-01 12 views
9

다음 코드는 오류를 발생시키고 응용 프로그램을 종료합니다. 버퍼는 단지 10 바이트 길이이고 텍스트는 22 바이트 길이 (버퍼 오버 플로우)이므로 의미가 있습니다.버퍼가 너무 작은 sprintf_s

char buffer[10];  
int length = sprintf_s(buffer, 10, "1234567890.1234567890."); 

이 오류를 어떻게 잡아 내 애플리케이션을 중단시키지 않고보고 할 수 있습니까?

편집 :

내가 _snprintf_s 갔다 아래 주석을 읽은 후. -1 값을 반환하면 버퍼가 업데이트되지 않습니다. MSDN에서

length = _snprintf_s(buffer, 10, 9, "123456789"); 
printf("1) Length=%d\n", length); // Length == 9 

length = _snprintf_s(buffer, 10, 9, "1234567890.1234567890."); 
printf("2) Length=%d\n", length); // Length == -1 

length = _snprintf_s(buffer, 10, 10, "1234567890.1234567890."); 
printf("3) Length=%d\n", length); // Crash, it needs room for the NULL char 
+0

버퍼 크기 및 버퍼 크기에서 1을 뺀 값은 오르지 만 오류가 발생하기 쉽습니다. 아래에서 설명하는 변형을 선호해야합니다. 길이 = _snprintf_s (버퍼, _TRUNCATE, "1234567890.1234567890."); 첫 번째 크기 매개 변수가 생략되었으므로 컴파일러는 크기를 유추하는 템플릿 오버로드를 사용합니다. _TRUNCATE는 그것이 말하는 것을 수행하는 특별한 값입니다. 매직 넘버는 없으며 이제 코드는 안전하고 유지 보수가 잘되며 좋은 예입니다. 이 주석과 _snprintf_s가 마음에 들면 위험한 snprintf/_snprintf 대답 대신 내 대답을 선택해야합니다. –

답변

5

sprintf_s 대신 snprintf (a.k.a Windows의 경우 _snprintf)을 사용할 수 있습니다.

#ifdef WIN32 
#define snprintf _snprintf 
#endif 

char buffer[10];  
int length = snprintf(buffer, 10, "1234567890.1234567890."); 
// unix snprintf returns length output would actually require; 
// windows _snprintf returns actual output length if output fits, else negative 
if (length >= sizeof(buffer) || length<0) 
{ 
    /* error handling */ 
} 
+4

snprintf_s도 있습니다. – Joe

+2

참고 : 보안상의 이유로 충분한 공간이 없으면 버퍼의 내용이 null로 종료되지 않을 수 있습니다. – Managu

+2

@Managu : MS가 C99에 대한 적합성을 주장한다면 - 그 주장은 가짜 일 것입니다. C99 표준은 문자열의 길이가 0이 아닌 한 문자열을 종료하도록 snprintf()를 요구합니다. 7.19.6.5 : n이 0이면 아무것도 쓰지 않습니다. 그렇지 않으면 n-1st를 넘는 출력 문자는 이됩니다. 배열에 쓰여진 것보다 더 길고 null 문자는 실제로 배열에 쓰여진 문자의 끝에 끝에 쓰여집니다. 오버랩하는 객체 사이에서 복사가 발생하면 동작은 정의되지 않습니다. –

0

는 :

sprintf_s과의 sprintf 간의 다른 주요 차이점은 sprintf_s 문자의 출력 버퍼의 사이즈를 지정하는 길이 매개 변수를 사용한다는 것이다. 버퍼가 인쇄중인 텍스트에 비해 너무 작은 경우 버퍼가 빈 문자열로 설정되고 유효하지 않은 매개 변수 처리기가 호출됩니다. snprintf와 달리 sprintf_s는 버퍼 크기가 0이 아닌 한 버퍼가 null로 종료되도록합니다.

이상 적으로 작성한 내용이 올바르게 작동해야합니다.

+4

기본 "잘못된 매개 변수 처리기"는 프로세스를 종료합니다. –

+0

사실이지만 그렇지 않은 경우 설치하기 쉽습니다. 버퍼가 너무 작 으면 sprintf_s가 -1을 반환합니다. – stijn

0

MSVC에서 일종의 작업을하고있는 것처럼 보입니까?

sprintf_s에 대한 MSDN 문서에 따르면 어설 션이 어설 션된다고해서 프로그래밍 방식으로 catch 할 수 있는지 확실하지 않습니다.

LBushkin에서 제안했듯이 문자열을 관리하는 클래스를 사용하는 것이 훨씬 낫습니다.

16

의도적으로 설계된 것입니다. sprintf_s의 전체 점과 *_s 패밀리의 다른 기능은 버퍼 오버런 오류를 catch하여 사전 조건 위반으로 처리하는 것입니다. 이것은 복구 할 수있는 것이 아니라는 것을 의미합니다. 이 오류는 오류 만 catch하도록 설계되었습니다. 문자열이 대상 버퍼에 비해 너무 클 수 있다는 것을 알고있는 경우 sprintf_s이라고해서는 안됩니다. 이 경우 먼저 strlen을 사용하여자를 필요가 있는지 확인하고 결정하십시오.

+0

동의하지 않습니다. _TRUNCATE 플래그를 사용하는 한 너무 작은 대상 버퍼로 sprintf_s를 호출하는 것은 전적으로 합리적입니다. 좋습니다, 기술적으로 _TRUNCATE는 sprintf_s 대신 snprintf_s를 사용해야합니다. 그러나 요점은 대부분 의미합니다. strlen을 사용하는 것은 종종 적용 할 수 없거나 불편하지만 _TRUNCATE를 사용하면 종종 사소하고 적절합니다. –

+0

'snprintf_s '를 사용하는 것이 결정적인 차이이며 실제로는 단순한 전문성이 아니라고 생각합니다. –

+0

확실히 중요한 차이입니다. 하지만 당신의 대답은 함수의 계열이자를 수 없기 때문에 오도 될 수 있다고 생각합니다. –

0

Microsoft에서 구현 한 기능 중 ISO C Committee 버전 인 TR24731의 6.6.1 절을 참조하십시오. 함수 set_constraint_handler(), abort_constraint_handler()ignore_constraint_handler() 기능을 제공합니다.

Microsoft 구현이 TR24731 제안서 ('유형 2 기술 보고서')를 따르지 않아 개입 할 수 없거나 뭔가해야 할 수도 있음을 제안하는 Pavel Minaev의 의견이 있습니다 TR이 지시해야하는 것과는 다릅니다. 이를 위해 MSDN을 면밀히 조사하십시오.

+1

불행히도 MSVC는 TR24731을 완벽하게 구현하지 못합니다. 특히, 참조하는 함수를 특별히 구현하지 않습니다. 또한 이름도'_s '로 끝납니다 (즉,'set_constraint_handler_s'). –

+1

하지만 http://msdn.microsoft.com/en-us/library/ksazx244%28VS.80%29.aspx에 따르면 프로그램을 중단 할 때의 기본 동작을 변경하는 데 사용할 수있는 _set_invalid_parameter_handler() 함수가 있습니다 . –

5

이 VC와 함께 작동 ++ 및 (_snprintf보다 확실히 안전) 현재 snprintf를 사용하여보다 안전하다 :

void TestString(const char* pEvil) 
{ 
    char buffer[100]; 
    _snprintf_s(buffer, _TRUNCATE, "Some data: %s\n", pEvil); 
} 

_TRUNCATE 플래그가 문자열을 절약 할 수 있음을 나타냅니다. 이 형식에서 버퍼의 크기는 실제로 전달되지 않습니다 (역설적으로!) 그게 그렇게 안전합니다. 컴파일러는 템플리트 매직을 사용하여 버퍼 크기를 유추합니다. 이는 잘못 지정 될 수 없음을 의미합니다 (놀랍게도 일반적인 오류). 이 기술은 다음 블로그 블로그 게시물에 설명 된대로 다른 안전한 문자열 래퍼를 만드는 데 적용 할 수 있습니다. https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

+0

_snprintf_s의 MSDN 설명서를 보면 _snprintf_s 호출에서 인수를 잊어 버린 것으로 보입니다. 이 인수는 buffer와 _TRUNCATE 사이에 나타나야하며 sizeOfBuffer라고합니다. – user1741137

+1

코드를 컴파일하고 완벽하게 안전하다는 논쟁을 잊지 않았습니다. 문서를 다시 읽어야합니다. 컴파일러가 버퍼 크기를 추측하도록 지시하는 _snprintf_s에 대한 템플릿 재정의를 사용하고 있습니다. 저는 프로그래머가 버퍼 크기를 명시 적으로 통과하고 * 잘못된 크기로 전달 된 수백 개의 장소를 보았습니다. 컴파일러가 버퍼 크기를 추론함으로써 만 심각한 버그를 피할 수 있습니다. 필자는 내 솔루션에 링크 된 기사에서이 기술에 대해 언급했습니다. 권장. –

관련 문제