24

我看到在我正在处理的一些代码中使用这种模式连接到一个字符串:

sprintf(buffer, "%s <input type='file' name='%s' />\r\n", buffer, id);
sprintf(buffer, "%s</td>", buffer);

我相当肯定它是不安全的 C。你会注意到buffer它既是输出又是第一个输入。

除了缓冲区溢出的明显可能性之外,我相信不能保证缓冲区在函数的开始和结束之间不会改变(即,不能保证缓冲区的状态在函数的执行)。sprintf 的签名还指定目标字符串是restricted。

我还记得一篇关于memcpy 中投机性写作的报告,我看不出为什么某些 C 库可能会在 sprintf 中做同样的事情。当然,在这种情况下,它将写入其源。那么这种行为安全吗?

仅供参考,我建议:

char *bufEnd = buffer + strlen(buffer);
/* sprintf returns the number of f'd and print'd into the s */
bufEnd += sprintf(bufEnd, " <input type='file' name='%s' />\r\n", id);

替换这个。

4

3 回答 3

24

glibc sprintf() 文档

如果复制发生在重叠的对象之间,则此函数的行为是未定义的——例如,如果 s 也作为要在 '%s' 转换控制下打印的参数给出。

在特定的实现中可能是安全的;但你不能指望它是便携的。

我也不确定你的提议在所有情况下都是安全的。您仍然可能会重叠缓冲区。已经很晚了,我的妻子正在打扰我,但我认为您仍然可能会遇到想要在连接字符串中再次使用原始字符串并覆盖空字符的情况,因此 sprintf 实现可能不知道在哪里重用字符串结束。

您可能只想将 snprint() 粘贴到临时缓冲区,然后将 strncat() 粘贴到原始缓冲区。

于 2009-08-16T02:58:21.423 回答
5

在这种特定情况下,它会起作用,因为字符串 inbuffer将是第一个要输入的东西buffer(同样,无用),所以你应该使用它strcat()来获得 [几乎] 相同的效果。

但是,如果您尝试结合strcat()的格式化可能性sprintf(),您可以试试这个:

sprintf(&buffer[strlen(buffer)], " <input type='file' name='%s' />\r\n", id);
于 2009-08-16T03:13:00.047 回答
3

如果您想使用 printf() 将格式化文本连接到缓冲区的末尾,我建议您使用整数来跟踪结束位置。

int i = strlen(buffer);
i += sprintf(&buffer[i], " <input type='file' name='%s' />\r\n", id);
i += sprintf(&buffer[i], "</td>");

或者:

int i = strlen(buffer);
i += sprintf(&buffer[i], " <input type='file' name='%s' />\r\n", id);
strcat(&buffer[i], "</td>");

在人们疯狂地否决这个(“这不安全!你可以超出缓冲区!”)之前,我只是在寻找一种合理的方法来在 C/C++ 中构建格式化字符串。

于 2009-08-16T03:27:58.560 回答