2009-08-16 34 views
19

我看到使用这种模式来连接到在一些代码字符串我工作:sprintf(buffer,“%s [...]”,buffer,[...])是否安全?

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

即使它*安全*(不会崩溃等)我可以想象它产生的结果不是预期的结果。 – 2009-08-16 03:08:58

+0

@AndrewMedico那是怎么回事? – cat 2016-09-27 21:49:56

回答

18

glibc sprintf() documentation

此函数的行为是 未定义如果进行复制操作,重叠换 例如如果s也作为 参数给定的对象之间的地方 到下控制被打印'%s'转换的 。

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

我不确定您的提案在任何情况下都是安全的。你仍然可能是重叠的缓冲区。现在已经很晚了,我的妻子还在纠缠我,但我认为你仍然可能会想要在连接字符串中再次使用原始字符串,并覆盖空字符,因此sprintf实现可能不知道重新使用的位置字符串结束。

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

+1

好的,只需要一个完整的检查。 [POSIX说同样的事情](http://www.opengroup.org/onlinepubs/9699919799/functions/sprintf.html): >如果复制发生在由于调用sprintf()而重叠的对象之间,或snprintf(),结果是不确定的。 – 2009-08-16 03:00:24

+0

事实上,我在第二个缓冲区中不重叠 - 这是一个严格不同的缓冲区。我不使用原版。 – 2009-08-16 03:14:08

+0

除非你看到我没有的东西,这是完全可能的。 – 2009-08-16 03:15:50

4

在这个特定的情况下,它将工作,因为buffer中的字符串将是第一个要输入buffer(再次无用)的字符串,所以您应该使用strcat()来代替[几乎相同]影响。

但是,如果你正在尝试strcat()sprintf()的格式化可能性结合起来,你可以试试这个:如果你想连接格式化的文本用printf一个缓冲区的末尾()

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

,我建议你使用整数来跟踪结束位置。

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>"); 

而且,人们发狂downvoting这个(“这是不是安全的,您可以缓冲区溢出!”)之前,我只是解决建立一个合理的方式C/C++中的格式化字符串。

+0

我认为你的建议在功能上与我建议的更换相同,但使用的记号略有不同。不过,我可以看出为什么有些人可能更喜欢以这种方式来看待它。 – 2009-08-16 03:47:56