我需要一個真正的C古魯的幫助來分析我的代碼崩潰。不是爲了修理墜機;我可以很容易地解決這個問題,但在此之前,我想了解這次崩潰甚至有可能發生,因爲這對我來說似乎完全不可能。如何在C中取消引用NULL指針不會導致程序崩潰?
這個崩潰只發生在一個客戶機上,我不能重現它在本地(所以我無法通過代碼使用調試器步驟),因爲我無法獲得該用戶的數據庫的副本。我的公司也不會允許我在代碼中修改幾行代碼,併爲此客戶定製構建(所以我不能添加一些printf行並讓他再次運行代碼),當然,客戶的構建沒有調試符號。換句話說,我的debbuging能力非常有限。儘管如此,我可以確定崩潰並獲得一些調試信息。然而,當我看到這些信息,然後在代碼中,我無法理解程序流如何能夠到達所討論的線路。在進入該行之前,代碼應該已經崩潰了很久。我完全迷失在這裏。
讓我們開始與相關的代碼。這是非常小的代碼:
// ... code above skipped, not relevant ...
if (data == NULL) return -1;
information = parseData(data);
if (information == NULL) return -1;
/* Check if name has been correctly \0 terminated */
if (information->kind.name->data[information->kind.name->length] != '\0') {
freeParsedData(information);
return -1;
}
/* Copy the name */
realLength = information->kind.name->length + 1;
*result = malloc(realLength);
if (*result == NULL) {
freeParsedData(information);
return -1;
}
strlcpy(*result, (char *)information->kind.name->data, realLength);
// ... code below skipped, not relevant ...
已經是這樣了。它崩潰了。我甚至可以告訴你在運行時調用真的。 strlcpy實際上被稱爲以下參數:
strlcpy (0x341000, 0x0, 0x1);
瞭解這一點很明顯,爲什麼strlcpy崩潰。它試圖從NULL指針讀取一個字符,這當然會崩潰。而且由於最後一個參數的值爲1,原始長度必須爲0.我的代碼在這裏顯然有一個錯誤,它無法檢查名稱數據是否爲NULL。我可以解決這個問題,沒問題。
我的問題是:
這段代碼如何能夠首先到達strlcpy?
爲什麼這個代碼不能在if語句崩潰?
我嘗試了我的本地機器上:
int main (
int argc,
char ** argv
) {
char * nullString = malloc(10);
free(nullString);
nullString = NULL;
if (nullString[0] != '\0') {
printf("Not terminated\n");
exit(1);
}
printf("Can get past the if-clause\n");
char xxx[10];
strlcpy(xxx, nullString, 1);
return 0;
}
此代碼永遠不會被通過的if語句。它在if語句中崩潰,這絕對是預期的。
因此,誰能想到的任何理由,第一個代碼可以獲得通過,如果語句沒有如果的名字 - >數據是真的NULL崩潰?這對我來說是完全神祕的。它似乎並不確定。
重要的額外信息:
兩種意見之間的代碼是真的完整,一切都沒有被排除在外。此外,該應用程序是單線程,所以沒有其他線程可能會意外改變背景中的任何記憶。發生這種情況的平臺是PPC CPU(一個G4,以防可能發揮任何作用)。如果有人想知道「kind」,這是因爲「information」包含一個名爲「kind」的「union」,name又是一個結構(kind是一個union,每個可能的union值都是一個不同類型的struct);但這一切都不應該在這裏真正重要。
我在這裏的任何想法感謝。如果這不僅僅是一個理論,我更感激,但是如果有辦法,我可以證實這個理論對於客戶是真的。
解決方案
我接受了正確的答案了,但以防萬一有人發現在谷歌這個問題,這裏到底發生了什麼:
指針均指向記憶,已經被釋放。釋放內存不會使其全部爲零或導致進程一次性將其返回給系統。所以即使內存被錯誤地釋放,它也包含了正確的值。在執行「如果檢查」時,問題指針不爲NULL。
之後檢查我分配一些新的內存,調用malloc。不確定malloc究竟在做什麼,但每次調用malloc或free都會對進程的虛擬地址空間的所有動態內存產生深遠影響。在malloc調用之後,指針實際上是NULL。不知何故,malloc(或某些系統調用malloc使用)將已釋放的指針本身所在的內存(不是指向它的數據,指針本身位於動態內存中)置零。對內存進行調零,指針現在的值爲0x0,在我的系統上等於NULL,當調用strlcpy時,它當然會崩潰。
所以,真正的錯誤造成這種奇怪的行爲是在我的代碼完全不同的位置。永遠不要忘記:釋放記憶保持它的價值,但它超出你的控制能力多長時間。要檢查您的應用程序是否存在訪問已釋放內存的內存錯誤,請確保已釋放內存在釋放之前始終爲零。在OS X中,您可以通過在運行時設置環境變量來完成此操作(無需重新編譯任何內容)。當然,這會讓程序變慢,但是你會很早就發現這些錯誤。
你可以問你的客戶核心轉儲並在調試器中調查它 – qrdl 2009-08-26 14:27:41
@qrdl:我有一個進程的崩潰日誌。這是Mac OS X,崩潰過程總是會創建這樣的崩潰日誌。我有堆棧回溯,這就是爲什麼我知道它崩潰的原因,並且我在崩潰時在所有寄存器中都有值;以及知道這個崩潰是由訪問內存位置0x0(NULL指針)引起的。在這樣的日誌中沒有其他有用的信息。 – Mecki 2009-08-26 14:35:24
請說明你聲明'結果'的地方以及你如何分配它指向的內存?您顯示了'* result'的設置位置,但未顯示'result'的分配位置。 – NVRAM 2009-08-26 14:57:56