简体   繁体   English

sprintf_s,缓冲区太小

[英]sprintf_s with a buffer too small

The following code causes an error and kills my application. 以下代码导致错误并导致我的应用程序死亡。 It makes sense as the buffer is only 10 bytes long and the text is 22 bytes long (buffer overflow). 这是有道理的,因为缓冲区只有10个字节长,文本长度为22个字节(缓冲区溢出)。

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

How do I catch this error so I can report it instead of crashing my application? 我如何捕获此错误,以便我可以报告它而不是崩溃我的应用程序?

Edit: 编辑:

After reading the comments below I went with _snprintf_s. 阅读下面的评论后,我选择_snprintf_s。 If it returns a -1 value then the buffer was not updated. 如果它返回-1值,则缓冲区未更新。

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 

It's by design. 这是设计的。 The entire point of sprintf_s , and other functions from the *_s family, is to catch buffer overrun errors and treat them as precondition violations . sprintf_s的整个点和*_s系列中的其他函数是捕获缓冲区溢出错误并将它们视为先决条件违规 This means that they're not really meant to be recoverable. 这意味着它们并非真正意味着可以恢复。 This is designed to catch errors only - you shouldn't ever call sprintf_s if you know the string can be too large for a destination buffer. 这只是为了捕获错误 - 如果你知道字符串对于目标缓冲区来说太大,你就不应该调用sprintf_s In that case, use strlen first to check and decide whether you need to trim. 在这种情况下,首先使用strlen检查并确定是否需要修剪。

Instead of sprintf_s , you could use snprintf (aka _snprintf on windows). 您可以使用snprintf (在Windows上也称为_snprintf )而不是sprintf_s

#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 */
}

This works with VC++ and is even safer than using snprintf (and certainly safer than _snprintf): 这适用于VC ++,甚至比使用snprintf更安全(当然比_snprintf更安全):

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

The _TRUNCATE flag indicates that the string should be truncated. _TRUNCATE标志表示该字符串应该被截断。 In this form the size of the buffer isn't actually passed in, which (paradoxically!) is what makes it so safe. 在这种形式下,缓冲区的大小实际上并没有传入,(矛盾的是!)是它如此安全的原因。 The compiler uses template magic to infer the buffer size which means it cannot be incorrectly specified (a surprisingly common error). 编译器使用模板魔术来推断缓冲区大小,这意味着它不能被错误地指定(一个令人惊讶的常见错误)。 This technique can be applied to create other safe string wrappers, as described in my blog post here: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/ 这个技术可以应用于创建其他安全字符串包装器,如我的博客文章中所述: https//randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

From MSDN: 来自MSDN:

The other main difference between sprintf_s and sprintf is that sprintf_s takes a length parameter specifying the size of the output buffer in characters. sprintf_s和sprintf之间的另一个主要区别是sprintf_s采用一个长度参数来指定输出缓冲区的大小(以字符为单位)。 If the buffer is too small for the text being printed then the buffer is set to an empty string and the invalid parameter handler is invoked. 如果缓冲区对于正在打印的文本而言太小,则将缓冲区设置为空字符串并调用无效参数处理程序。 Unlike snprintf, sprintf_s guarantees that the buffer will be null-terminated (unless the buffer size is zero). 与snprintf不同,sprintf_s保证缓冲区将以空值终止(除非缓冲区大小为零)。

So ideally what you've written should work correctly. 理想情况下,你所写的应该正常工作。

Looks like you're writing on MSVC of some sort? 看起来你在某种MSVC上写作?

I think the MSDN docs for sprintf_s says that it assert dies, so I'm not too sure if you can programmatically catch that. 我认为sprintf_s的MSDN文档说它断言死了,所以我不太确定你是否能以编程方式捕获它。

As LBushkin suggested, you're much better off using classes that manage the strings. 正如LBushkin建议的那样,使用管理字符串的类会更好。

See section 6.6.1 of TR24731 which is the ISO C Committee version of the functionality implemented by Microsoft. 请参阅TR24731的 6.6.1节,它是Microsoft实施的功能的ISO C委员会版本。 It provides functions set_constraint_handler() , abort_constraint_handler() and ignore_constraint_handler() functions. 它提供了函数set_constraint_handler()abort_constraint_handler()ignore_constraint_handler()函数。

There are comments from Pavel Minaev suggesting that the Microsoft implementation does not adhere to the TR24731 proposal (which is a 'Type 2 Tech Report'), so you may not be able to intervene, or you may have to do something different from what the TR indicates should be done. Pavel Minaev的评论暗示微软的实施不符合TR24731提案(这是'2型技术报告'),因此您可能无法进行干预,或者您可能不得不采取与TR表示应该完成。 For that, scrutinize MSDN. 为此,仔细检查MSDN。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM