使用snprintf避免缓冲区溢出

ant*_*009 17 c printf

我这样使用snprintf以避免缓冲区溢出:

char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");
Run Code Online (Sandbox Code Playgroud)

-1如果字符串长度超过32个字节,我为null终止符添加了保留空间.

我的想法是否正确?

平台:

  • GCC 4.4.1
  • C99

D.S*_*ley 28

正如其他人所说,在这种情况下你不需要-1.如果数组是固定大小,我会strncpy改为使用.它是为复制字符串sprintf而制作的- 是为了进行难以格式化而制作的.但是,如果数组的大小未知或您正在尝试确定格式化字符串需要多少存储空间.这是我真正喜欢标准指定版本的snprintf:

char* get_error_message(char const *msg) {
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
    char  *buffer = malloc(needed+1);
    sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}
Run Code Online (Sandbox Code Playgroud)

结合此功能,va_copy您可以创建非常安全的格式化字符串操作.  

  • 如果字符串可能太大而无法放入目标,请不要使用strncpy(); strncpy()确实**NOT**null - 如果它太长则终止它复制的内容.此外,它总是复制N个字符 - 即使源是1个字节而目标是1兆字节. (4认同)
  • 此代码有一个错误:“成功完成后,snprintf() 函数应返回如果 n 足够大(不包括终止空字节)将写入 s 的字节数。” 因此,您必须为终止空值分配一个额外的字节。 (2认同)
  • @JeremySalwen - 感谢抓住那......这就是我写Java的原因=) (2认同)
  • 你可以简单地使用`sprintf(buffer,...`,因为你保留了大小合适的`buffer`.这样可以避免这个bug. (2认同)
  • @PascalCuoq 非常好的一点......不知道我是如何错过这个的,也不知道它是如何在四年半的时间里没有被注意到的 (2认同)

Eli*_*sky 12

您不需要-1,因为参考说明:

函数snprintf()和vsnprintf()写入的字节数不超过大小(包括尾随的'\ 0').

请注意"包括尾随'\ 0'"部分


Ale*_*x B 9

不需要-1.C99 snprintf 始终为零终止.Size参数指定输出缓冲区的大小,包括零终止符.因此,代码变为

char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");
Run Code Online (Sandbox Code Playgroud)

ret包含打印的实际字符数(不包括零终结符).

但是,不要与微软的混淆_snprintf(预C99),这确实不是空终止,并且,对于这个问题,有完全不同的行为(例如返回-1,而不是想成为印刷长度的情况下,如果缓冲区不够大).如果使用_snprintf,您应该使用与问题相同的代码.