C語言並不是一種很方便的語言,它的字串就是一例。按照C語言的定義,“字串就是一段記憶體空間,裡麵包含ASCII字元,並且,以“\0”結尾,總共能存放n-1個字元。”按照這個描述,字串處理確實很麻煩,還很容易出錯。
為了方便使用者,C語言標準庫向使用者提供了一些字串函數,如字串拷貝、構造、清空等函數,在一定程度上方便了使用者的使用。但是,我無意中發現,這些函數還是有些隱患的。
事情很簡單,我注意到我寫的一些程式,老是有記憶體讀寫錯誤,但是,經過仔細檢查我所有的資料Buffer,以及相關的處理函數,又沒有找到什麼錯誤。於是我把懷疑的目光投向我常用的一些字串處理函數上,如strcpy、sprintf等。在經過幾次仔細地跟蹤之後,我發現記憶體錯誤出自於此。於是,我開始研究如何安全地使用字串這個話題。
1.字串拷貝函數
1.1 不安全的strcpy
首先,我寫了這樣一個測試函數:
void strcpyTest0()
{
int i;
char szBuf[128];
for(i=0;i<128;i++) szBuf[i]='*';
szBuf[127]='\0'; //構造一個全部是*的字串
char szBuf2[256];
for(i=0;i<256;i++) szBuf2[i]='#';
szBuf2[255]='\0'; //構造一個全部是#的字串
strcpy(szBuf,szBuf2);
printf("%s\n",szBuf);
}
很簡單,把一個字串拷貝到另外一個空間,但是,很不幸,源字串比目標地址要長,因此,程式很悲慘地死去了。
1.2 還是不安全的strncpy
通過上例,我發現我需要在拷貝時多輸入一個參數,來標明目的地址有多長,檢查C語言的庫函數說明,有一個strncpy可以達到這個目的,這個函數的原型如下:
char *strncpy( char *strDest, const char *strSource, size_t count );
好了,這下我們的問題解決了,我寫出了如下代碼:
void strcpyTest1()
{
int i;
char szBuf[128];
for(i=0;i<128;i++) szBuf[i]='*';
szBuf[127]='\0';
char szBuf2[256];
for(i=0;i<256;i++) szBuf2[i]='#';
szBuf2[255]='\0';
strncpy(szBuf,szBuf2,128);
printf("%s\n",szBuf);
}
一切都顯得很好,但是,當我輸出結果的時候,發現了問題,字串後面有時會跟幾個奇怪的字元,好像沒有用“\0”結束,於是我把上面的拷貝語句改成“strncpy(szBuf,szBuf2,8);”,只拷貝8個字元,問題出現了,程式輸出如下:
########***********************************************************************************************************************
果然,當請求的目標地址空間比源字串空間要小的時候,strncpy將不再用“\0”來結束字串。巨大的隱患。