sprintf_s的缓冲区太小。

时间:2022-10-21 20:22:01

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. If it returns a -1 value then the buffer was not updated.

阅读下面的评论后,我使用了_snprintf_s。如果它返回-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 

6 个解决方案

#1


5  

Instead of sprintf_s, you could use snprintf (a.k.a _snprintf on windows).

您可以使用snprintf (a.k)代替sprintf_s。在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 */
}

#2


16  

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. 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. In that case, use strlen first to check and decide whether you need to trim.

它的设计。sprintf_s的整个要点,以及*_s家族的其他功能,就是捕获缓冲区溢出错误,并将它们视为先决条件的违规。这意味着它们并不是真的可以恢复的。这是为了捕获错误而设计的——如果您知道字符串对于目标缓冲区来说太大了,就不应该调用sprintf_s。在这种情况下,使用strlen首先检查并决定是否需要修剪。

#3


5  

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. 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/

_TRUNCATE标记指示字符串应该被截断。在这种形式下,缓冲区的大小实际上并没有传递进来,这(自相矛盾地)是使它如此安全的原因。编译器使用模板魔法来推断缓冲区的大小,这意味着它不能被错误地指定(这是一个非常常见的错误)。这一技术可以应用于创建其他安全的字符串包装器,如我在博客文章中所描述的:https://randomascii.wordpress.com/2013/04/03/stop-strncpy -already/。

#4


0  

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. 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).

sprintf_s和sprintf的另一个主要区别是sprintf_s采用长度参数,指定字符的输出缓冲区的大小。如果缓冲区太小,不能将文本打印出来,那么缓冲区将被设置为空字符串,并调用无效的参数处理程序。与snprintf不同,sprintf_s保证缓冲区将被空终止(除非缓冲区大小为零)。

So ideally what you've written should work correctly.

所以理想情况下,你写的应该正确。

#5


0  

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所建议的,使用管理字符串的类会更好。

#6


0  

See section 6.6.1 of TR24731 which is the ISO C Committee version of the functionality implemented by Microsoft. It provides functions set_constraint_handler(), abort_constraint_handler() and ignore_constraint_handler() functions.

查看TR24731的第6.6.1节,它是由Microsoft实现的功能的ISO C委员会版本。它提供函数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. For that, scrutinize MSDN.

帕维尔·米纳耶夫(Pavel Minaev)的评论指出,微软的实施并没有遵循TR24731的提议(这是一份“2型技术报告”),因此你可能无法进行干预,或者你可能不得不做一些与TR指示应该做的不同的事情。为此,细看MSDN。

#1


5  

Instead of sprintf_s, you could use snprintf (a.k.a _snprintf on windows).

您可以使用snprintf (a.k)代替sprintf_s。在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 */
}

#2


16  

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. 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. In that case, use strlen first to check and decide whether you need to trim.

它的设计。sprintf_s的整个要点,以及*_s家族的其他功能,就是捕获缓冲区溢出错误,并将它们视为先决条件的违规。这意味着它们并不是真的可以恢复的。这是为了捕获错误而设计的——如果您知道字符串对于目标缓冲区来说太大了,就不应该调用sprintf_s。在这种情况下,使用strlen首先检查并决定是否需要修剪。

#3


5  

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. 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/

_TRUNCATE标记指示字符串应该被截断。在这种形式下,缓冲区的大小实际上并没有传递进来,这(自相矛盾地)是使它如此安全的原因。编译器使用模板魔法来推断缓冲区的大小,这意味着它不能被错误地指定(这是一个非常常见的错误)。这一技术可以应用于创建其他安全的字符串包装器,如我在博客文章中所描述的:https://randomascii.wordpress.com/2013/04/03/stop-strncpy -already/。

#4


0  

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. 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).

sprintf_s和sprintf的另一个主要区别是sprintf_s采用长度参数,指定字符的输出缓冲区的大小。如果缓冲区太小,不能将文本打印出来,那么缓冲区将被设置为空字符串,并调用无效的参数处理程序。与snprintf不同,sprintf_s保证缓冲区将被空终止(除非缓冲区大小为零)。

So ideally what you've written should work correctly.

所以理想情况下,你写的应该正确。

#5


0  

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所建议的,使用管理字符串的类会更好。

#6


0  

See section 6.6.1 of TR24731 which is the ISO C Committee version of the functionality implemented by Microsoft. It provides functions set_constraint_handler(), abort_constraint_handler() and ignore_constraint_handler() functions.

查看TR24731的第6.6.1节,它是由Microsoft实现的功能的ISO C委员会版本。它提供函数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. For that, scrutinize MSDN.

帕维尔·米纳耶夫(Pavel Minaev)的评论指出,微软的实施并没有遵循TR24731的提议(这是一份“2型技术报告”),因此你可能无法进行干预,或者你可能不得不做一些与TR指示应该做的不同的事情。为此,细看MSDN。