2011-06-27 82 views
8

一些靜態代碼分析工具建議所有的strcat使用應該用strncat替換以達到安全目的?strcat vs strncat - 什麼時候應該使用哪個函數?

在程序中,如果我們清楚地知道目標緩衝區和源緩衝區的大小,是否仍然推薦使用strncat?

另外,鑑於靜態工具的建議,應該使用strcat嗎?

回答

10

如果您完全確定源緩衝區的大小,並且源緩衝區包含終止字符串的NULL字符,那麼當目標緩衝區足夠大時,您可以安全地使用strcat。

我還是建議使用strncat函數,並給它大小的目標緩衝區 - 長度的目標字符串 - 1

注:我編輯了這個,因爲評論指出的是,我以前的答案是可怕的錯誤。

+0

1。我會強調,在某些系統(例如BSD)中,如果使用'size-generic'字符串操作函數(strcat,strcpy等),編譯器會發出警告 – dave

+1

我認爲你的意思是「......給它的大小的目標緩衝區_minus它已經包含的字符串的大小_「 – Nemo

+3

這個答案是非常危險的錯誤。 @Nemo更接近了,但我認爲仍然是一個。 [Pankaj的回答](http://stackoverflow.com/a/6502352/228539)顯示準確,並且非常詳細。它必須是'目標緩衝區的大小 - 目標字符串的長度 - 1'以適當地避免緩衝區溢出。雖然在設計這個函數時看起來確實是一個愚蠢的選擇,因爲它在開始複製之前必須固有地尋找空終止符,並且可以計算一直沿途的長度,所以我們不必這樣做兩次。 – altendky

1

他們不這樣做,所以他們不能被替換。兩者都有不同的數據模型。

  • 一個字符串strcat是空 結束的字符串爲您(作爲程序員)保證它有足夠的空間。
  • 甲字符串strncat是一個序列的char要麼是終止 在長度你被空終止指示 如果它是 應該比 長度短 。

因此,這些函數的使用僅取決於您可能(或想要)對數據所做的假設。

0

靜態工具通常很難理解使用函數的情況。我敢打賭,他們中的大多數只是爲每個遇到的strcat發出警告,而不是實際查看傳遞給函數的數據是否是確定性的。如前所述,如果您可以控制輸入數據,那麼這兩項功能都不安全。

雖然注意strncat()稍微慢一些,因爲它必須檢查'\ 0'終止和計數器,並且明確地將其添加到最後。 strcat()另一方面只檢查'\ 0',並通過從第二個字符串複製終止符以及所有數據,將尾部'\ 0'添加到新字符串中。

11

將兩個字符串連接成一個字符串。

原型

#include <string.h> 

char * strcat(char *restrict s1, const char *restrict s2); 

char * strncat(char *restrict s1, const char *restrict s2, size_t n); 

說明

strcat()strncat()功能追加空終止 字符串的副本S2到的空終止的端字符串s1,然後添加終止\ 0'。字符串s1必須有足夠的空間來保存 結果。

strncat()函數從s2追加不超過n個字符,然後 添加終止\ 0'。

源和目標字符串不應該重疊,因爲行爲是 未定義。

返回值

The `strcat()` and `strncat()` functions return the pointer s1. 

安全考慮

strcat()功能很容易,其方式使得惡意用戶通過任意地改變正在運行的程序的功能 濫用緩衝區溢出攻擊。

避免使用strcat()。相反,使用strncat()strlcat()並確保 沒有更多的字符複製到目標緩衝區,而不是它可以保留的 。

請注意,strncat()也可能有問題。對於一個字符串來說,截斷可能是一個安全問題。由於截斷的字符串 的長度不會與原始長度相同,因此它可能會引用完全不同的資源,並且截斷的資源的使用可能會導致非常不正確的行爲。例如:

void 
foo(const char *arbitrary_string) 
{ 
     char onstack[8] = ""; 

#if defined(BAD) 
     /* 
      * This first strcat is bad behavior. Do not use strcat! 
      */ 
     (void)strcat(onstack, arbitrary_string);  /* BAD! */ 
#elif defined(BETTER) 
     /* 
      * The following two lines demonstrate better use of 
      * strncat(). 
      */ 
     (void)strncat(onstack, arbitrary_string, 
      sizeof(onstack) - strlen(onstack) - 1); 
#elif defined(BEST) 
     /* 
      * These lines are even more robust due to testing for 
      * truncation. 
      */ 
     if (strlen(arbitrary_string) + 1 > 
      sizeof(onstack) - strlen(onstack)) 
       err(1, "onstack would be truncated"); 
     (void)strncat(onstack, arbitrary_string, 
      sizeof(onstack) - strlen(onstack) - 1); 
#endif 
} 

例如

char dest[20] = "Hello"; 
char *src = ", World!"; 
char numbers[] = "12345678"; 

printf("dest before strcat: \"%s\"\n", dest); // "Hello" 

strcat(dest, src); 
printf("dest after strcat: \"%s\"\n", dest); // "Hello, World!" 

strncat(dest, numbers, 3); // strcat first 3 chars of numbers 
printf("dest after strncat: \"%s\"\n", dest); // "Hello, World!123" 
+0

嗨,邊註釋並不反映該程序的實際輸出,因爲src被定義爲世界(大寫字母W)和輸出狀態世界(小寫字母w)。 – Eduardo

+0

@Eduardo :)謝謝。現在更正。 –

+0

「strcat()和strncat()函數將以空字符結尾的字符串s2的副本附加到結尾」對於'strncat()不是這樣「。 's2'不一定是以_null結尾的string_。 's2'是一個可能缺少_null字符的字符數組就足夠了。 – chux

相關問題