前言:glibc在free一塊堆內存的時候會檢查堆頭,如果堆頭有異常,就報free err、double free等問題,然而實際上這可能是另外一個地方的堆溢出導致的本堆塊堆頭被踩導致的,並不是什么double free。這個時候就有個簡單的定位方法去定位這種問題。
1. 現象:
Continuing.
*** glibc detected *** ...: double free or corruption (out): 0x0000000000627f90 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x76628)[0x7ffff720b628]
/lib64/libc.so.6(cfree+0x6c)[0x7ffff72105cc]
...
======= Memory map: ========
...
0061c000-00682000 rw-p 00000000 00:00 0 [heap]
7ffff0000000-7ffff0021000 rw-p 00000000 00:00 0
7ffff0021000-7ffff4000000 ---p 00000000 00:00 0
7ffff6d67000-7ffff6d7d000 r-xp 00000000 08:07 803123 /usr/local/lib64/libgcc_s.so.1
2. 定位方法:
gdb拉入目標程序,先在free上下斷點,制作command另其自動打印調用棧、自動繼續,這樣我們可以看到glibc報錯時候的調用棧:
b free
command 1
>silent
>bt
>c
>end
運行crash樣本,發現free出錯點和釋放出錯的堆結構體:
(gdb) bt
#0 0x00007ffff7210560 in free () from /lib64/libc.so.6
#1 0x00000000004140c0 in av_free (ptr=<optimized out>) at libavutil/mem.c:282
#2 av_freep (arg=arg@entry=0x61d178) at libavutil/mem.c:289
#3 0x00000000004048c1 in hevc_decode_free (avctx=<optimized out>) at libavcodec/hevc.c:3281
#4 0x00000000004137ad in avcodec_close (avctx=0x61cbf0) at libavcodec/utils.c:106
#5 0x0000000000402ba7 in hevc_decode_end (s=s@entry=0x61c010) at libbpg.c:564
#6 0x0000000000403edc in bpg_decoder_decode (img=img@entry=0x61c010, buf=buf@entry=0x61c250 "BPG\373 ", buf_len=buf_len@entry=2450) at libbpg.c:1882
#7 0x0000000000401410 in main (argc=<optimized out>, argv=<optimized out>) at bpgdec.c:332
*** glibc detected *** ...: double free or corruption (out): 0x0000000000627f90 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x76628)[0x7ffff720b628]
/lib64/libc.so.6(cfree+0x6c)[0x7ffff72105cc]
源碼定位到該結構體申請時的代碼,下斷點並重新運行crash樣本,在斷點處獲取到該結構體的內存地址addr(此處malloc返回的地址為0x627f90),然后在addr - 8地址處(chunk head)下硬件斷點(為了檢測堆頭被破壞的過程):
(gdb) watch *0x627f90 - 8
Hardware watchpoint 8: *0x627f88
(gdb) c
Continuing.
Hardware watchpoint 8: *0x627f88
Old value = 209
New value = 0
restore_tqb_pixels (s=s@entry=0x61d050, src1=src1@entry=0x64b010 "M", dst1=dst1@entry=0x63a7c6 "M", stride_src=256, stride_dst=132, x0=<optimized out>, y0=192, width=64, height=64, c_idx=0) at libavcodec/hevc_filter.c:228
228 memcpy(src, dst, len);
(gdb)
3. 成功抓到罪魁禍首:堆溢出。