2009-08-16 66 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