开启VS2010的警告之后,将项目中所有的sprintf都换成了sprintf_s,以为这样的话程序在字符串copy时就安全了,结果在实际环境中程序还是崩溃了。
自己在VS中尝试了这样的代码:
char teststr[10] = {0};
char inputstr[21] = {"12345678901234567890"};
sprintf_s(teststr,10,"%s",inputstr);
在之前以为是teststr中应该是123456789,实际上不是这样的,程序直接就崩溃掉了,使用try,catch根本无法抓到这样的异常。
查询MSDN才知道,需要使用_snprintf_s来代替sprintf_s,并且可以捕捉到这样的异常。msdn上提供的例子如下:
// crt_snprintf_s.cpp
// compile with: /MTd
// These #defines enable secure template overloads
// (see last part of Examples() below)
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <crtdbg.h> // For _CrtSetReportMode
#include <errno.h>
// This example uses a 10-byte destination buffer.
int snprintf_s_tester( const char * fmt, int x, int count )
{
char dest[10];
printf( "\n" );
if ( count == _TRUNCATE )
printf( "%d-byte buffer; truncation semantics\n",
_countof(dest) );
else
printf( "count = %d; %d-byte buffer\n",
count, _countof(dest) );
int ret = _snprintf_s( dest, _countof(dest), count, fmt, x );
printf( " new contents of dest: '%s'\n", dest );
return ret;
}
void Examples()
{
// formatted output string is 9 characters long: "<<<123>>>"
snprintf_s_tester( "<<<%d>>>", 121, 8 );
snprintf_s_tester( "<<<%d>>>", 121, 9 );
snprintf_s_tester( "<<<%d>>>", 121, 10 );
printf( "\nDestination buffer too small:\n" );
snprintf_s_tester( "<<<%d>>>", 1221, 10 );
printf( "\nTruncation examples:\n" );
int ret = snprintf_s_tester( "<<<%d>>>", 1221, _TRUNCATE );
printf( " truncation %s occur\n", ret == -1 ? "did"
: "did not" );
ret = snprintf_s_tester( "<<<%d>>>", 121, _TRUNCATE );
printf( " truncation %s occur\n", ret == -1 ? "did"
: "did not" );
printf( "\nSecure template overload example:\n" );
char dest[10];
_snprintf( dest, 10, "<<<%d>>>", 12321 );
// With secure template overloads enabled (see #defines
// at top of file), the preceding line is replaced by
// _snprintf_s( dest, _countof(dest), 10, "<<<%d>>>", 12345 );
// Instead of causing a buffer overrun, _snprintf_s invokes
// the invalid parameter handler.
// If secure template overloads were disabled, _snprintf would
// write 10 characters and overrun the dest buffer.
printf( " new contents of dest: '%s'\n", dest );
}
void myInvalidParameterHandler(
const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
wprintf(L"Invalid parameter handler invoked: %s\n", expression);
}
int main( void )
{
_invalid_parameter_handler oldHandler, newHandler;
newHandler = myInvalidParameterHandler;
oldHandler = _set_invalid_parameter_handler(newHandler);
// Disable the message box for assertions.
_CrtSetReportMode(_CRT_ASSERT, 0);
Examples();
}
在后来的日子中又尝试了自己项目中其他的字符串copy,结果让我大吃一惊,可能是以前就理解不深入,才导致程序崩溃.
char teststrcat[10] = {0};
char inputstrcat[21] = {"12345678901234567890"};
strcat_s(teststrcat,inputstrcat);
使用strcat_s程序直接崩溃掉,使用上面的_set_invalid_parameter_handler可以捕捉到该异常。
然而使用memcpy时结果却不是那么乐观,
char teststr[10] = {0};
char inputstr[21] = {"12345678901234567890"};
memcpy(teststr,inputstr,strlen(inputstr));
程序直接通过了,而且teststr中包含有inputstr中的数据和乱码。
这样开发人员就再不知情的情况下将该代码执行了。
安全的使用memcpy的方式为
void test(char *pbData, unsigned int cbData) {
char buf[BUFFER_SIZE];
memcpy(buf, pbData, min(cbData,BUFFER_SIZE-1));
}