[GDB] 使用 GDB 找尋造成 strlen() 當掉的原因

[GDB] 使用 GDB 找尋造成 strlen() 當掉的原因

最近專案的 python 程式常常出現當掉的狀況,

用 gdb debug 了一下,看來是 python 用到我們的 C++ 函式庫的問題~

來記錄一下 debug 的過程吧~

 

首先用 gdb /usr/bin/python coredump 指令載入 gdb,

可以看到程式死在一個很怪的 __strlen_sse2_pminub(),

看起來像是 strlen() 的 SSE 版本:

Core was generated by `python -u -m /opt/testd'.
Program terminated with signal 11, Segmentation fault.
#0  0x00007ff5d2da2811 in __strlen_sse2_pminub () from /lib64/libc.so.6

 

先用 py-bt 看一下 python 部分的 call stack,列出目前是在 scan() 這個函式:

(gdb) py-bt
#22 Frame 0x7ff5a806dea0, for file ./wrapper.py, line 422, in scan

 

再用 bt 看一下 call stack,python 程式後來會呼叫到我們寫的 C++ 函式庫

的 Scanner::Callback() 函式,而這函式後來又呼叫到 __strlen_sse2_pminub() 後掛了:

(gdb) bt
#0  0x00007ff5d2da2811 in __strlen_sse2_pminub () from /lib64/libc.so.6
#1  0x00007ff5c0ce9843 in Scanner::Callback(CONNECTION*, unsigned int, void*, void*) () from /opt/libscan.so

 

想知道 __strlen_sse2_pminub() 傳入的參數是什麼的話,

就要先了解一下 calling convention…

從 wikipedia: x86 calling conventions: System V AMD64 ABI 這邊可以知道,

x64 的函式呼叫,參數會依序放在 RDI, RSI, RDX, RCX,

而 strlen() 只有一個參數,所以應該是放在 RDI 這個暫存器上~

用 info reg 看一下目前暫存器的值,RDI 的內容是 0x7ff5aa402008,

另外也可以注意到程式最終的 IP 值是指向 __strlen_sse2_pminub+17 的位置:

(gdb) info reg
rax            0x0  0
rbx            0x7ff5b168d700   140693220153088
rcx            0x8  8
rdx            0x7ff590005fb0   140692659658672
rsi            0x7ff5a8061d60   140693062688096
rdi            0x7ff5aa402008   140693100044296
rbp            0x7ff5c0eee3b0   0x7ff5c0eee3b0
rsp            0x7ff5b168a628   0x7ff5b168a628
r8             0x0  0
r9             0x0  0
r10            0xffffff00   4294967040
r11            0x7ff5d2dc17b0   140693781354416
r12            0x3e323  254755
r13            0x136faf0    20380400
r14            0x7ff5b168a6a0   140693220140704
r15            0x7ff590005fb0   140692659658672
rip            0x7ff5d2da2811   0x7ff5d2da2811 <__strlen_sse2_pminub+17>
eflags         0x10287  [ CF PF SF IF RF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0

 

用 disas 看一下 __strlen_sse2_pminub() 的組合語言碼,

+17 的位置的指令是用 movdqu 將 RDI 暫存器指到的記憶體內容,

複製到 XMM1 暫存器上,而 RDI 又正好是 __strlen_sse2_pminub() 的參數,

所以應該是想要把輸入的字串複製到 XMM1 暫存器上時出了問題:

(gdb) disas __strlen_sse2_pminub
Dump of assembler code for function __strlen_sse2_pminub:
0x00007ff5d2da2800 <+0>: xor    %rax,%rax
0x00007ff5d2da2803 <+3>: mov    %edi,%ecx
0x00007ff5d2da2805 <+5>: and    $0x3f,%ecx
0x00007ff5d2da2808 <+8>: pxor   %xmm0,%xmm0
0x00007ff5d2da280c <+12>:    cmp    $0x30,%ecx
0x00007ff5d2da280f <+15>:    ja     0x7ff5d2da282e <__strlen_sse2_pminub+46>
=> 0x00007ff5d2da2811 <+17>:    movdqu (%rdi),%xmm1
0x00007ff5d2da2815 <+21>:    pcmpeqb %xmm1,%xmm0
0x00007ff5d2da2819 <+25>:    pmovmskb %xmm0,%edx
0x00007ff5d2da281d <+29>:    test   %edx,%edx
0x00007ff5d2da281f <+31>:    jne    0x7ff5d2da2a73 <__strlen_sse2_pminub+627>

 

試著瞄一下 RDI 指到的字串內容… 這邊寫 out of bounds,看來不是個有效的位址:

(gdb) x/s $rdi
0x7ff5aa402008: <Address 0x7ff5aa402008 out of bounds>

 

再用 info proc mappings 查查看… 不過這指令似乎只能列出 DLL 的載入位址,

沒辦法顯示出 stack/heap 的區段:

(gdb) info proc mappings
Mapped address spaces:
Start Addr           End Addr       Size     Offset objfile
...................
0x7ff5a1e67000     0x7ff5a1e69000     0x2000    0x12000 /usr/lib64/python2.7/lib-dynload/_curses.so
0x7ff5b0114000     0x7ff5b0264000   0x150000        0x0 /opt/worker.so
...................

 

改用 maintenance info sections 來看,可以看到 0x7ff5aa402008 這位址,

正好是夾在下面兩個區段的中間,應該蠻有可能是被釋放掉的記憶體:

(gdb) maintenance info sections
Core file:
.....................
0x7ff5aa402000->0x7ff5aa402000 at 0x1aa1f000: load110 ALLOC READONLY
0x7ff5ac000000->0x7ff5ac422000 at 0x1aa1f000: load111 ALLOC LOAD HAS_CONTENTS
.....................

 

回到 C++ 程式庫去查 Scanner 相關的程式,

後來找到了一個地方,使用了 STL vector,

但可能索引到了一個之前被釋放掉的元素,因而導致了 crash~

 

這次雖然不是直接指出造成程式當掉的元兇,但 gdb 提供的資訊給了我們一些線索,

讓我們知道要去查程式裡是否有用到被釋放掉記憶體的部分,

算是蠻有收穫的囉~^^

 

參考資料:stackoverflow: can I find via GDB if a variable belong to heap or stack? 

(本頁面已被瀏覽過 2,940 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料