2015-04-03 89 views
1

我創建了一個程序,它定期從網站下載一個文本文件,格式爲csv,並對其進行解析,然後提取相關數據,然後顯示。防止字符串操作崩潰導致整個應用崩潰

我注意到,偶爾每隔幾個月左右就會崩潰。考慮到數據下載和解析的週期可能每5分鐘甚至更少發生,崩潰很少見。我很確定它在解析字符串並提取數據的函數內崩潰。當它崩潰時,會發生擁塞的互聯網連接,即大量下載和/或連接速度較慢。有時遠程站點可能正在處理損壞或不完整的數據。

我使用了一個測試應用程序,它在處理之前保存要處理的數據,並且確實發現崩潰發生時它並未完成。

我調整了函數來適應一些無效或不完整數據的情況,以及檢查所有返回值。我還檢查用於連接遠程站點並下載數據的各種函數的返回值。如果返回值表示沒有成功,則不會再繼續。

功能的核心使用strsep()通過數據走路,提取出來的信息:

/ * 
    * delimiters typically contains: <;>, <">, < > 
    * strsep() is used to split part of the string using delimiter 
    * and copy into token which then is copied into the array 
    * normally the function stops way before ARRAYSIZE which is just a safeguard 
    * it would normally stop when the end of file is reached, i.e. \0 
    */ 
for(n=0;n<ARRAYSIZE;n++) 
{ 
    token=strsep(&copy_of_downloaded_data, delimiters); 
    if (token==NULL) 
    break; 

    data->array[n].example=strndup(token, strlen(token)); 

    if (data->array[n].example!=NULL) 
    { 
    token=strsep(&copy_of_downloaded_data, delimiters); 
    if (token==NULL) 
     break; 

    (..) 

    copy_of_downloaded_data=strchr(copy_of_downloaded_data,'\n'); /* find newline */ 
    if (copy_of_downloaded_data==NULL) 
    break; 

    copy_of_downloaded_data=copy_of_downloaded_data+1; 
    if (copy_of_downloaded_data=='\0') /* find end of text */ 
    break; 
} 

因爲我懷疑我能不能解釋其中的數據可以被破壞我想所有的方法想知道是否有一種方法來編程,以便在運行時的函數在數據損壞的情況下不會使整個應用程序崩潰。

如果這是不可能的,我可以做些什麼來使它更健壯。

編輯:當數據突然結束,其中一個場的中間被切割的,碰撞的一個可能的實例是即

「測試」,「示例」,「這個數據是布洛克

至少我注意到它通過保存的數據看,但我發現它不是一致的。會不會有壓力測試它是以下建議。

回答

6

做會找出最好的事情什麼輸入導致函數崩潰,並修復功能,使其不會崩潰。由於該函數正在執行字符串處理,因此應該可以通過爲其提供大量虛擬/測試數據(或者如果它是導致崩潰的特定輸入,則將其「喂」給它「正確的」測試數據)。你基本上想要對功能進行折磨 - 測試,直到你找到如何讓它按需崩潰;那時你就可以開始調查崩潰的確切位置和原因了,一旦你明白了,修復崩潰的必要改變對你來說可能會變得很明顯。

在valgrind下運行該程序也可能會導致您發現該錯誤。

如果由於某種原因您無法修復該錯誤,則另一種選擇是產生子進程並在子進程內運行錯誤代碼。這樣,如果它崩潰,只有子進程丟失,而不是父進程。 (你可以通過調用fork()在大多數操作系統下產生子進程;當然,你需要爲子進程提供一些方法來將結果傳回給父進程)。(請注意,這樣做是一種混亂,並且可能效率不高,並且如果有人能夠發送程序輸入的惡意程序可以找出如何操作錯誤的方法,也可能會在應用程序中引入安全漏洞去控制孩子的過程 - 所以我不推薦這種方法!)

+1

如果有人可以控制孩子的過程,他們現在也可以控制過程 – 2015-04-03 22:19:30

+0

這是一個非常好的點科爾! – 2015-04-03 22:51:02

+0

非常好的建議,謝謝。我一直在考慮餵它測試數據。在開發的早期階段,我確實使用了valgrind,並且修復了所有可以解決的問題。這個錯誤每隔幾個月就會觸發一次,雖然有時候會在一天內出現幾次,然後在幾周內沒有出現,這使得它在調試器和類似工具中運行變得不太實際。 – aseq 2015-04-03 23:24:28

1

coredump指向什麼?

strsep - 沒有內存同步機制,因此將其保護爲關鍵部分(當您執行strsep時將其鎖定)?

看看strsep是否可以處理大塊(ARRAYSIZE不會幫你在這裏)。線程/程序接收copy_of_downloaded_data的

堆棧大小(我知道你只是引用它,看看它接收它的功能。)

+0

當我通過gdb運行應用程序時,它會在我上面提到的函數中崩潰。 copy_of_downloaded_data是原始下載數據的實際單獨副本,因爲strsep()實際上會更改數據。鎖定它意味着什麼? – aseq 2015-04-03 23:17:11

1

我會建議人們應該嘗試寫,保持跟蹤代碼字符串長度是故意的,並不關心字符串是否是零終止的。即使空指針被稱爲「十億美元的錯誤」(*),我認爲零終止的字符串更糟糕。雖然在某些情況下,使用零終止字符串的代碼可能比跟蹤字符串長度的代碼「更簡單」,但確保沒有任何東西可以導致字符串處理代碼超出緩衝區邊界的額外工作量超出了處理已知長度的字符串。

如果,例如,一個人想長度length1length2的字符串的串聯存儲到緩衝器中,如果長度BUFF_SIZE,可以測試是否容易length1+length2 <= BUFF_SIZE如果一個不期望的字符串是空終止,或length1+length2 < BUFF_SIZE如果有人期望每個字符串都有一個無用的空字節。當使用以零結尾的字符串時,必須在連接之前確定兩個字符串的長度,並且這樣做可以使用memcpy()而不是strcpy()或無用strcat()。 (*)在許多情況下,擁有一個可識別的無效指針要比不要指向任何有意義的指針必須指向無意義的指針要好得多。許多與空指針相關的問題實際上源於實現失敗以捕獲具有空指針的算術;將空指針歸咎於本來可能會發生但未被避免的問題是不公平的。

+0

空終止和零終止是一回事嗎? – 2015-04-03 22:20:49

+0

@ColeJohnson:我更喜歡術語「零終止」,因爲終止符是一個值爲零的字節。在ASCII碼中,零碼被稱爲「空字節」,因爲它不會導致電傳打字機做任何事情,並且其紙帶表示是空白的,因此字符串末尾的零字節通常稱爲「空字節「,但字符串方法的重要性不在於字符代碼表示」空字符「,而是數字值爲零。即使C實現使用空字符爲42的字符集,strcpy仍會複製到第一個零字節。 – supercat 2015-04-03 22:29:19

+1

我認爲它可能會使函數過於複雜,以考慮字符串大小,而不是空終止。然而,你可能有一個問題,因爲一個潛在的錯誤很可能在'/ 0'測試中。順便說一句,你會不會更好地使用strncpy()和strncat()?據瞭解,由於緩衝區溢出問題,strcpy()和strcat()以及其他類似軟件在當今被人們所詬病。 – aseq 2015-04-03 23:34:52