我看到使用这种模式连接到我正在处理的一些代码中的字符串:
sprintf(buffer, "%s <input type='file' name='%s' />\r\n", buffer, id);
sprintf(buffer, "%s</td>", buffer);
我相当肯定它不安全C.你会注意到buffer
既是输出又是第一个输入。
除了显然存在缓冲区溢出的可能性之外,我相信不能保证缓冲区在函数的开始和结束之间不会发生变化(即,不能保证在执行函数期间缓冲区的状态)。 sprintf的签名还指定目标字符串为restrict
ed。
我还记得一个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);
替换它。
答案 0 :(得分:19)
来自glibc sprintf() documentation:
此功能的行为是 如果发生复制,则为undefined 在重叠的对象之间 例如,如果s也作为一个给出 在控制下打印的论据 '%s'转换。
在特定实施中可能是安全的;但你不能指望它是便携式的。
我不确定你的建议在所有情况下都是安全的。你仍然可以重叠缓冲区。现在已经很晚了,而且我的妻子是我的错误,但我认为您仍然可以在连接字符串中再次使用原始字符串并覆盖空字符,因此sprintf实现可能不知道重用的位置字符串结束。
您可能只想将snprint()粘贴到临时缓冲区,然后将strncat()粘贴到原始缓冲区上。
答案 1 :(得分:4)
在这种特定情况下,它会起作用,因为buffer
中的字符串将是buffer
中的第一个输入(再次,无用),所以你应该使用{{而不是获得[几乎]相同的效果。
但是,如果您尝试将strcat()
与strcat()
的格式化可能性结合起来,您可以尝试这样做:
sprintf()
答案 2 :(得分: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 ++中构建一个格式化的字符串。