2011-09-06 44 views
27

我想知道這兩個選項是更安全的一個使用方法:哪個sprintf/snprintf更安全?

#define MAXLEN 255 
char buff[MAXLEN + 1] 
  1. sprintf(buff, "%.*s", MAXLEN, name)

  2. snprintf(buff, MAXLEN, "%s", name)

我的理解是,兩者都是相同。請建議。

+0

將#2更改爲'MAXLEN + 1',它們在所有情況下寫入「buff」的內容都相同(如果'strlen(name)> 255',返回值將會不同)。 –

回答

0

兩個會給你想要的結果,但snprintf是更通用的,並會保護你的字符串從超支無論格式字符串給出。

另外,由於snprintf(或sprintf對這個問題)增加了最終\0,你應該做的字符串緩衝區一個字節大,char buff[MAXLEN + 1]

+1

事實證明,情況並非如此。在snprintf文檔中:「函數snprintf()和vsnprintf()將大部分字節(包括終止空字節('\ 0'))寫入到str中。」 – Chriszuma

25

你給出的兩個表達式是而不是等價物:sprintf沒有指定要寫入的最大字節數的參數;它只需要一個目標緩衝區,一個格式字符串和一堆參數。因此,它可能會寫入比緩衝區有更多空間的字節,並在此寫入任意代碼。該%.*s是不令人滿意的解決方案,因爲:

  1. 當格式說明是指長度,它指的是strlen相當於;這是字符串中字符數量的一種度量,而不是它在內存中的長度(即它不計算空終止符)。
  2. 在格式字符串(添加一個新行,例如)的任何變化將改變的sprintf版本的行爲相對於緩衝區溢出。使用snprintf,無論格式字符串或輸入類型如何變化,都會設置固定的清除最大值。
+0

實際上,大概1,我被誤認了 - 它確定了最大字符數(但不包括'\ 0')。我相應地編輯了我的答案。 –

+0

謝謝 - 我記得那個格式說明符存在一些問題,並且認爲你是對的:--P – azernik

+1

嗯,這一切都取決於OP的問題是關於什麼樣的安全性。形式上正確使用的'sprintf'在'snprintf'這個特殊情況下是安全的。你在這個答案中談論的是懶惰/無能的程序員缺乏保護。 OP是否詢問這個安全方面我不知道。 – AnT

2

你的sprintf語句是正確的,但我不會有足夠的自信以用於安全目的(例如,缺少一個神祕的字符而你是無屏蔽的),同時有可用於snprintf的snprintf任何格式...哦,等等的snprintf是不是在ANSI C。它是(僅)?C99。這可能是一個偏好另一個的(弱)理由。

嘛。你也可以使用strncpy,對不對?

例如

char buffer[MAX_LENGTH+1]; 
    buffer[MAX_LENGTH]=0;    // just be safe in case name is too long 
    strncpy(buffer,MAX_LENGTH,name); // strncpy will never overwrite last byte 
+0

你確定'%。* s'在ANSI C中有效嗎?我試圖找到規範,但只能找到一個(不可信的)引用,沒有指定'。*'。 –

+0

@Eli:自從原始標準(1989/1990)以來,在ANSI C中指定精度(如此處)或字段寬度作爲星號。在C99標準中增加了'snprintf()'。 –

+0

@Michael - 對於字符串('%s')也?很高興知道,謝謝。 –

8

對於問題中的簡單示例,兩個調用之間的安全性可能沒有太大差異。但是,在一般情況下,snprintf()可能更安全。一旦你有一個更復雜的格式字符串與多個規格的轉換可能很難(或者幾乎是不可能的),以確保您的緩衝區長度佔準確跨越不同的轉換 - 尤其是因爲以前的轉換並不一定產生一個固定數量的輸出字符。

所以,我會用snprintf()堅持。

snprintf()(儘管與安全無關)的另一個小優點是它會告訴您需要多大的緩衝區。

最後需要說明的 - 你應該在指定的snprintf()通話的實際緩衝區大小 - 這會處理佔比爲你空終止:

snprintf(buff, sizeof(buff), "%s", name); 
2

我想說snprintf()得多好,直到我讀這樣一段話:

https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html

簡短的總結是:從系統snprintf()不便於攜帶其行爲變化的系統。 snprintf()最嚴重的問題可能發生在snprintf()簡單地通過調用sprintf()實現。您可能認爲它可以保護您免受緩衝區溢出並讓您警惕,但它可能不會。

所以現在我仍然說snprintf()更安全,但在使用它時也很謹慎。

1

最好也是最靈活的方法是使用snprintf

size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */ 
char *str = malloc(nbytes); 
snprintf(str, nbytes, "%s", name); 

在C99,snprintf返回寫到不包括'\0'串的字節數。如果少於必需的字節數,則snprintf返回擴展格式所需的字節數(仍然不包括'\0')。通過傳遞snprintf長度爲0的字符串,您可以提前發現擴展字符串已經存在多長時間,並使用它來分配必要的內存。

1

這兩者之間有一個重要區別 - snprintf調用將掃描name參數到最後(終止NUL)以便找出正確的返回值。另一方面,sprintf調用將從name讀取AT MOST 255個字符。

所以如果name是一個指向非NUL終止緩衝與至少255個字符,則snprintf呼叫可能流掉的緩衝區的末尾,並觸發未定義的行爲(如崩潰),而sprintf版本將不會。

相關問題