內存寫越界導致破環堆結構引起的崩潰問題定位經驗[如報錯malloc(): memory corruption或free(): invalid next size]



轉載自:點擊打開鏈接http://blog.csdn.net/slvher/article/details/9144161



               前段時間開發的一個後端C模塊上線後,線上出core,初始時,由於訪問壓力不大,所以崩潰是上線3天左右出現的。當時用gdb跟進調用堆棧並檢查源碼,發現出core位置的代碼沒有啥問題。由於當時開發任務較重,且該模塊不保存狀態(崩潰重啓不影響對外服務),所以沒有深入跟進。後來隨着客戶端版本逐漸放量導致訪問壓力上升,噩夢開始了。。。
        該模塊會不定時core掉,而且幾乎每次崩潰時的調用堆棧都不一樣,關鍵是最後幾層堆棧很多都位於幾乎不可能出問題的代碼中,比如庫函數或廠裏的公共庫。
        好在在衆多core文件中發現規律:每次基本都是在對內存動態操作時掛掉,比如malloc/realloc/free/new/delete都引起了崩潰。而且幸運的是,崩潰進程還是輸出了一些關鍵信息,比如下面這些(這些是在不同的崩潰時刻分別輸出的):
*** glibc detected *** malloc(): memory corruption: 0x0000002a95c1ff10 ***
*** glibc detected *** double free or corruption (out): 0x0000000000f0d910 ***
*** glibc detected *** free(): invalid next size (normal): 0x0000002a96103b00 ***
*** glibc detected *** free(): invalid next size (fast): 0x0000000000f349d0 ***
*** glibc detected *** corrupted double-linked list: 0x0000002a95f062e0 ***
        從上面的日誌也可以看到,每次引起崩潰的直接原因都可能不同。用gdb又仔細查看core文件發現,有時進程是收到SIGABRT信號後退出,有時又是收到SIGSEGV信號後退出。
        由此,基本定位了崩潰原因:內存訪問越界導致破壞了heap的數據結構。用valgrind在線下環境啓動進程,試圖重現崩潰或定位越界訪問的代碼,遺憾的是,腳本壓了1個小時也沒出現崩潰,而valgrind的輸出報告也沒有越界代碼位置的提示。
        最終,仔細檢查源碼後發現,在某個回調函數中,new出來的buffer接收完通過http post方式發送過來的2進制數據後,我又多寫了1行代碼,類似於:recv_buf[data_len] = '\0',導致越界多寫1個字節,最終引起各種莫名其妙的內存崩潰。

經驗教訓:
        1)調用堆棧信息對定位問題幫助很大,但也不可盡信。比如這次遇到的情況,每次出core的調用堆棧幾乎都不一樣,而且最後幾層棧幀都是不可能出現問題的庫函數或久經考驗的公司公共庫,這種情況下,思維需要跳出局部,在更高的層次尋找規律或原因
        2)一旦定位崩潰屬於堆內存讀寫越界問題,就仔細檢查自己的代碼吧,因爲庫函數或公共庫出問題的概率太小了,所以不要存在僥倖心理,這個時候,盲目的自信要不得
        3)本來自認爲對內存操作已經很小心了,沒想到還是在想當然的瞬間寫下犯錯的代碼,導致最終花費很多時間和精力去“捉蟲”。不過好在跟進崩潰的過程中增加了一點分析/定位問題的經驗,也算有些收穫吧

=============== EOF =================

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章