【问题标题】:Is sprintf(buffer, "%s […]", buffer, […]) safe?sprintf(buffer, "%s [...]", buffer, [...]) 安全吗?
【发布时间】:2010-11-19 23:31:53
【问题描述】:

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

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

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

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

我还记得一个关于speculative writing in 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);

替换这个。

【问题讨论】:

  • 即使它是安全(不会崩溃等),我可以想象它会产生与预期不同的结果。
  • @AndrewMedico 怎么样?

标签: c printf c-strings


【解决方案1】:

来自glibc sprintf() documentation

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

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

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

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

【讨论】:

  • 好的,只是想要一个健全的检查。 POSIX says the same thing: > 如果复制发生在由于调用 sprintf() 或 snprintf() 而重叠的对象之间,结果是未定义的。
  • 实际上,我的第二个缓冲区并没有重叠——它是一个完全不同的缓冲区。我不使用原版。
  • 除非你看到我没有看到的东西,这是完全可能的。
  • 好的,我明白了;我想唯一有风险的情况是如果你想在连接字符串的某个点重用原始字符串。
【解决方案2】:

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

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

sprintf(&amp;buffer[strlen(buffer)], " &lt;input type='file' name='%s' /&gt;\r\n", id);

【讨论】:

    【解决方案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++ 中构建格式化字符串的合理方法。

    【讨论】:

    • 我认为您的建议在功能上与我提议的替换相同,但使用的符号略有不同。不过,我明白为什么有些人可能更喜欢这样写。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    • 2022-12-02
    • 2017-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多