MSVC 의 _vsnprintf 버그와 수정 방법

체스맨의 이미지

MSVC 6.0(서비스팩5 - 확실히 설치했었는지는 기억이 안나네요.)에서
_vsnprintf 함수가 vsnprintf 표준과 호환성을 유지하는데 일부 문제가
있는 것 같습니다.

1. 종료 문자를 감안해서 버퍼 길이를 입력해야 한다. 예를 들어 버퍼 길이가
len 이면, len-1 을 입력해줘야 한다.

2. 문자열이 종료 되지 않아도 음수값(실패)을 리턴하지 않는다. 예를 들어
입력된 버퍼길이가 len 일 때, 문자를 len 만큼 쓰고 len+1 에 문자열을
종료시켜야 하는데, 일단 len 만큼만 써지면 len 이 리턴된다.

다음과 같이 수정할 수 있습니다. 1은 간단히 해결되구요.
...
조금 더 생각을 해 봤습니다. 맨 아래 처음 올린 코드를 올려놨는데,
굳이 memset 할 필요 없이, 무조건 리턴값에 따라 문자열을 종료시키면
되는군요. vsnprintf 표준은 버퍼가 모자르면, 쓰는데까지 쓰고,
리턴값은 모두 성공적으로 쓸 수 있는데 필요한 버퍼 길이를 리턴하도록
돼 있는데, 지금으로서는 리턴값까지 동일하게 맞춰주긴 어려울 것 같네요.

#include <stdio.h>
#include <stdarg.h>
#include <memory.h>

int 
vsnprintf( char* buf, int len, const char* fmt, va_list arg ) 
{ 
	int result = _vsnprintf( buf, len-1, fmt, arg ); 
	if( result>=0 ) buf[result] = 0; 
	else buf[len-1]=0;
	return result; 
} 

int 
snprintf( char* buf, int len, const char* fmt, ... ) 
{ 
	int result; 
	va_list arg; 
	va_start(arg,fmt); 
	result = vsnprintf( buf, len, fmt, arg ); 
	va_end(arg); 
	return result; 
} 

=================================

다음은 처음 올렸던 memset 을 사용하는 좀 비효율적인 코드입니다.

2는 memset 으로 일단 버퍼를 0이 아닌 값으로 채워준 뒤,
_vsnprintf 를 호출한 후 문자열이 잘 종료됐는지 확인합니다.

#include <stdio.h>
#include <stdarg.h>
#include <memory.h>

int
vsnprintf( char* buf, int len, const char* fmt, va_list arg )
{
	int result;
	memset( buf, 0xff, len );
	result = _vsnprintf( buf, len-1, fmt, arg );
	if( result>=0 && buf[result] ) buf[result] = 0;
	return result;
}
Forums: 
체스맨의 이미지

여전히 리턴값을 vsnprintf 표준과 맞추진 못하지만, 다음과 같이 해서
버퍼 부족을 판단하는 코드가 동일해지도록 할 수 있습니다.

int 
vsnprintf( char* buf, int len, const char* fmt, va_list arg ) 
{ 
	int result = _vsnprintf( buf, len-1, fmt, arg );
	if( result>=0 ) {
		buf[result] = 0;
		return result;
	}
	buf[len-1]=0;
	return len;
}

다음과 같이 버퍼 부족을 판단하며 이것은 표준함수를 호출할 때와 동일하게
작성됩니다.

result = snprintf( buf, len, "%s\n", "Hello, world." );
if( result>=len ) {
	/* 버퍼 부족 */
}

Orion Project : http://orionids.org

체스맨의 이미지

다음은 MSVC 의 _vsnprintf 가 문제가 있는지 알아보는 예제입니다.

#include <stdio.h>

int
main( void )
{
	static char buf[] = "Hello, world\n";
	int result = _snprintf( buf, 5, "Hello" );
	if( result>0 )
		printf( "return value=%d\n", result );
	printf( buf );
	return 0;
}

문자열 길이가 5인 "Hello" 를 버퍼(길이 5)에 쓰면 오류를 내거나,
"Hell" 까지만 써져야 하는데, "Hello" 까지 쓰고, null terminate
되지 않아서 출력이 다음과 같이 나오면, 문제가 있는 것입니다.

Quote:

return value=5
Hello, world

Orion Project : http://orionids.org