GDB分析數組索引錯誤導致Segmentation fault

GDB分析數組索引錯誤導致Segmentation fault

最近學習gdb調試的知識,使用gdb調試core文件分析C語言數組索引錯誤所引發的segmentation fault原因。

分析前準備

設置core dump的大小。默認的core dump大小是0字節,也就是出現了segmentation fault是看不到Core轉儲文件的。

查看core默認大小
ulimit -c

在這裏插入圖片描述
修改core dump的轉儲大小爲無限制,也可以指定爲固定大小,這裏使用無限制的方式

ulimit -c unlimited
編寫測試的程序,如下

在這裏插入圖片描述
從源碼中可以看到,這裏的calc_index返回了負值的索引,根據數組的索引規則,下標從0開始,最大索引值爲數組長度-1。這裏的-7不符合數組索引的規則,因此程序運行起來將會導致內存錯誤。主要是數組訪問或修改了不屬於自己的內存信息,可能是修改了某地址或者棧數據導致。下面就通過gdb具體看看失敗的原始是什麼。

編譯程序並執行
gcc -g source.c
執行
./a.out

使用-g 選項在適應gdb調試時可以獲取符號信息。執行之後會得到segmentation fault錯誤信息,如下
在這裏插入圖片描述
查看當前目錄下會生成一個core文件,如下

ls 
file core

在這裏插入圖片描述

使用gdb調試core文件

使用gdb加載調試

gdb -c core ./a.out

加載成功後,會提示出現失敗的原因,如下
在這裏插入圖片描述
這時看一下棧回溯,發現gdb也不能識別到原因,如下
在這裏插入圖片描述

調試看看具體原因

由於此時的棧回溯不能得到有用信息,說明棧的數據被破壞了,因此只能調試的方式分析看看是哪裏導致的失敗,如果能看到棧回溯信息,可以快速定位到具體位置查看。在main下設置斷點,讓程序運行起來,分析下彙編指令,如下
在這裏插入圖片描述
由於不知道在哪裏運行失敗,通過單步執行的方式看一下修改了數組my_data時是否會造成內存程序運行失敗,根據常識,一般數組訪問了不屬於它的內存可能會導致內存錯誤。關鍵指令
在這裏插入圖片描述
當程序來到修改my_data的第一個位置,先看一下原始數組my_data的內存和eax的值,這裏eax是index的值
在這裏插入圖片描述
當繼續往下後發現程序沒有停止,再次觀察my_data指向的內存,數據沒有並改變。
那麼0xa這個值是被寫到了哪裏?看到指令

movl 0xa,0x804a020(,%eax,4)

換算之後addr = 0x804a020+eax4,eax=-7,因此最後結果爲
addr=0x804a020+(-7x4)=0xFFFFFFE4+0x804a020=0x804A004
可以使用p/x (0x804a020+$eax
4) 得到相同的值
看一下addr的內存,如下
在這裏插入圖片描述
同樣可以知道,0x08會寫到0x804A008(addr=0x804a020+eax * 4,eax=-6)的內存,如下
在這裏插入圖片描述
發現到目前爲止,程序還沒有出現失敗,繼續往下運行,來到call puts函數位置,繼續執行後,程序出現了一開始看到的SIGSEGV信號導致的segmentation fault
在這裏插入圖片描述
居然是在調用了call puts之後導致了,按照猜想,應該是在修改數組時可能誘發內存錯誤但是真實的錯誤是在這個函數內部。重新運行之後,直接單步來到這裏,進入後如下
在這裏插入圖片描述
進來後看到這裏是got@plt函數表調用
在這裏插入圖片描述
首先函數調用了0x804a00c,直接看一下這裏的彙編指令,如下
在這裏插入圖片描述
只是校驗了一下數據。
繼續單步執行,執行了push 0 之後,執行了跳轉函數,觀察另一個0x804a82e0地址處的指令時提示沒有函數
在這裏插入圖片描述
單步進入到0x804a82e0後,繼續執行兩次觸發了SIGSEGV這個異常信號。此時出現了相同的棧信息
在這裏插入圖片描述
到這裏就能明確了,執行put函數時,程序上的某個位置棧信息被破壞掉,原本要修改數組的數據被寫到了別的棧位置,導致了運行的數據棧數據被破壞導致segmentation fault。(好吧,和我預想的不太一樣,原來程序運行起來後如果修改的不是自己當前內存區域,而是別的內存,在定位問題時就不太好明確具體位置。)

那麼原本正確的數據該是如何

根據上面的調試可以發現,數據被寫到了別的位置破壞了別的數據棧,剛好那個棧數據可以被寫入,導致了運行失敗,那麼如果是正確的應該是如何的?這裏做了一個對比,將上面calc_index函數返回0,然後觀察call puts函數內部的指令時,如下
在這裏插入圖片描述
編譯運行後,正常修改的數組
在這裏插入圖片描述
直接來到call指令位置,進入後如下
在這裏插入圖片描述
從截圖中可以看到,這裏的指令和錯誤的情況一樣,進入到0x804a82e0內部觀察到如下
在這裏插入圖片描述
如上可以看到,正常的程序會調用_dl_runtime_resolve函數,通過_dl_runtime_resolve獲取到_IO_puts函數,最後輸出"This is a message"如下
在這裏插入圖片描述
執行了很多次單步運行後,看到了輸出字符串信息
在這裏插入圖片描述

總結

當程序發生了segmentation fault後,通過core dump文件可以得到運行時發生錯誤的信息,使用gdb的backtrace的方式看看函數調用關係,如果能得到有用信息就從backtrace中定位,如果沒有得到有效的backtrace信息,則通過在函數入口設置斷點方式調試分析。在調試過程中理解數組索引錯誤所引起的內存失敗問題,逐步理解一個簡單的Bug在實際的彙編指令運行原理。

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