GCC編譯選項Sanitier問題定位記錄


關鍵詞:Address sanitizer、Use after free、Heap buffer overflow、Stack buffer overflow、Memory leak等等。

操作系統:Ubuntu 16.04;g++ (Ubuntu 4.8.5-4ubuntu2) 4.8.5;clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)。

1. Sanitizer簡介

Sanitizers是谷歌發起的開源工具集,包括了AddressSanitizer, MemorySanitizer, ThreadSanitizer, LeakSanitizer,Sanitizers項目本是LLVM項目的一部分,但GNU也將該系列工具加入到了自家的GCC編譯器中。GCC從4.8版本開始支持Address和Thread Sanitizer,4.9版本開始支持Leak Sanitizer和UB Sanitizer,這些都是查找隱藏Bug的利器。

AddressSanitizer檢查地址相關問題,包括釋放后使用、重復釋放、堆溢出、棧溢出等等問題。

LeakSanitizer檢查內存泄漏問題。

ThreadSanitizer檢查線程數據競爭和死鎖問題。

MemorySanitizer檢查使用未初始化內存問題。

其他還包括HWSANUBSan

內核Sanitizer包括KASANKMSAN,分別提供內核中動態內存錯誤檢查和未初始化內存使用問題檢查。

2. AddressSanitizer

由於編譯器版本不同,Address Sanitizer的支持不盡相同。下面記錄部分功能測試結果。

2.1 Use after free

Use after free檢測到使用的內存已經被釋放導致的錯誤類型。當內存被釋放后,所有數據置成0xfd。如果對應內存是0xfd表示可能使用了被釋放的內存。

使用g++ use_after_free.cc -o use_after_free -ggdb -fsanitize=address編譯:

// RUN: g++ use_after_free.cc -o use_after_free -ggdb -fsanitize=address

int main(int argc, char **argv) {
  int *array = new int[100];
  delete [] array;
  return array[argc];  // BOOM
}

執行結果如下,其中地址和文件對應轉換通過addr2line進行。

比如0x4007f8地址,addr2line -a 0x4007f8 -e use_after_free得到結果在use_after_free.cc的第6行。

=================================================================
==2708== ERROR: AddressSanitizer: heap-use-after-free on address 0x602e0001fc64 at pc 0x4007f9 bp 0x7ffd8b8b0830 sp 0x7ffd8b8b0828----錯誤類型說明是對堆的釋放后使用,調用地址在0x4007f9.
READ of size 4 at 0x602e0001fc64 thread T0
    #0 0x4007f8 (/home/al/temp/address_sanitizer/use_after_free+0x4007f8)---------------------------錯誤現場在use_after_free的0x4007f8,在第6行。
    #1 0x7fcbf515582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
    #2 0x4006b8 (/home/al/temp/address_sanitizer/use_after_free+0x4006b8)
0x602e0001fc64 is located 4 bytes inside of 400-byte region [0x602e0001fc60,0x602e0001fdf0)
freed by thread T0 here:
    #0 0x7fcbf551083a (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1183a)
    #1 0x4007ac (/home/al/temp/address_sanitizer/use_after_free+0x4007ac)---------------------------釋放現場,通過addr2line得到對應第5行。
    #2 0x7fcbf515582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
previously allocated by thread T0 here:
    #0 0x7fcbf551067a (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1167a)
    #1 0x400795 (/home/al/temp/address_sanitizer/use_after_free+0x400795)---------------------------申請現場,通過addr2line得到對應第4行。
    #2 0x7fcbf515582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
Shadow bytes around the buggy address:
  0x0c063fffbf30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbf40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbf50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbf60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbf70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c063fffbf80: fa fa fa fa fa fa fa fa fa fa fa fa[fd]fd fd fd------------------------------------[]表示異常點,0xfd表示此段內存已經被釋放。 0x0c063fffbf90: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c063fffbfa0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c063fffbfb0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa------------------------------------一個shadow字節表示8個字節,共50個0xfd,對應400個字節,也即分配的array大小。 0x0c063fffbfc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbfd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap righ redzone:     fb
 Freed Heap region: fd   Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==2708== ABORTING

可見AddressSanitizer對內存區域進行特殊標記,如果和預期不符合則表示內存被破壞。

2.2 Heap buffer overflow

Heap buffer overflow表示堆內存溢出,使用0xfa和0xfb表示堆的左右邊界。如果對應內存為0xfa或者0xfb表示可能是堆溢出。

// RUN: g++ heap_buffer_overflow.cc -o heap_buffer_overflow -ggdb -fsanitize=address

int main(int argc, char **argv) {
  int *array = new int[100];
  array[0] = 0;
  int res = array[argc + 100];  // BOOM
  delete [] array;
  return res;
}

 結果如下: 

=================================================================
==4852== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602e0001fdf4 at pc 0x40087b bp 0x7fff2f0d10c0 sp 0x7fff2f0d10b8
READ of size 4 at 0x602e0001fdf4 thread T0
    #0 0x40087a (/home/al/temp/address_sanitizer/heap_buffer_overflow+0x40087a)-------------------異常地址在0x40087a,對應第6行。
    #1 0x7fadfc16582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
    #2 0x400708 (/home/al/temp/address_sanitizer/heap_buffer_overflow+0x400708)
0x602e0001fdf4 is located 4 bytes to the right of 400-byte region [0x602e0001fc60,0x602e0001fdf0)
allocated by thread T0 here:
    #0 0x7fadfc52067a (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1167a)
    #1 0x4007e5 (/home/al/temp/address_sanitizer/heap_buffer_overflow+0x4007e5)
    #2 0x7fadfc16582f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
Shadow bytes around the buggy address:
  0x0c063fffbf60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbf70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbf80: fa fa fa fa fa fa fa fa fa fa fa fa 00 00 00 00
  0x0c063fffbf90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c063fffbfa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c063fffbfb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[fa]fa------------------------0xfa是堆的redzone,訪問到這里的內存表示堆越界訪問了。 0x0c063fffbfc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbfd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbfe0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffbff0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c063fffc000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa   Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==4852== ABORTING

2.3 Stack buffer overflow

 Stack buffer overflow表示棧的訪問超出邊界,通過0xf1/0xf3/0xf4來判定。如果訪問到0xf1/0xf3/0xf4則表示有可能超出棧邊界的訪問。

// RUN: g++ stack_buffer_overflow.cc -o stack_buffer_overflow -ggdb -fsanitize=address

int main(int argc, char **argv) {
  int stack_array[100];
  stack_array[1] = 0;
  return stack_array[argc + 100];  // BOOM
}

 結果如下: 

=================================================================
==6196== ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe96edf094 at pc 0x400821 bp 0x7ffe96edeec0 sp 0x7ffe96edeeb8
READ of size 4 at 0x7ffe96edf094 thread T0
    #0 0x400820 (/home/al/temp/address_sanitizer/stack_buffer_overflow+0x400820)
    #1 0x7f9f0ba9e82f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
    #2 0x400678 (/home/al/temp/address_sanitizer/stack_buffer_overflow+0x400678)
Address 0x7ffe96edf094 is located at offset 436 in frame <main> of T0's stack:
  This frame has 1 object(s):
    [32, 432) 'stack_array'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:
  0x100052dd3dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100052dd3dd0: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
  0x100052dd3de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100052dd3df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100052dd3e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100052dd3e10: 00 00[f4]f4 f3 f3 f3 f3 00 00 00 00 00 00 00 00
  0x100052dd3e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100052dd3e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100052dd3e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100052dd3e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100052dd3e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
 Stack left redzone: f1   Stack mid redzone:     f2
 Stack right redzone: f3 Stack partial redzone: f4   Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==6196== ABORTING

2.4 Global buffer overflow

Global buffer overflow表示超出全局變量訪問區域,通過0xf9進行判斷。如果訪問的區域是0xf9則表示可能超出了全局變量訪問區域。

// RUN: g++ global_buffer_overflow.cc -o global_buffer_overflow -ggdb -fsanitize=address

int global_array[100] = {-1};
int main(int argc, char **argv) {
  return global_array[argc + 100];  // BOOM
}

 結果如下:

=================================================================
==7681== ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000006011f4 at pc 0x4007f3 bp 0x7ffeb4f10630 sp 0x7ffeb4f10628
READ of size 4 at 0x0000006011f4 thread T0
    #0 0x4007f2 (/home/al/temp/address_sanitizer/global_buffer_overflow+0x4007f2)
    #1 0x7fad3bdf982f (/lib/x86_64-linux-gnu/libc-2.23.so+0x2082f)
    #2 0x4006d8 (/home/al/temp/address_sanitizer/global_buffer_overflow+0x4006d8)
0x0000006011f4 is located 4 bytes to the right of global variable 'global_array (global_buffer_overflow.cc)' (0x601060) of size 400
Shadow bytes around the buggy address:
  0x0000800b81e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b81f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0000800b8230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9
  0x0000800b8240: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
 Global redzone: f9 Global init order: f6
  Poisoned by user:      f7
  ASan internal:         fe
==7681== ABORTING

2.5 Use after return

2.6 Use after scope

2.7 Initialization order bugs

2.8 Memory leaks

 Memory leaks用於檢查內存泄漏,結果會告知泄漏的內存在何處申請。

//RUN: clang -fsanitize=address -g memory-leak.c -o memory-leak
#include <stdlib.h>

void *p;

int main() {
  p = malloc(7);
  p = 0; // The memory is leaked here.
  return 0;
}

 結果如下,會顯示泄漏點的棧,以及一個總結:泄漏多少內存、在哪幾處泄漏。

=================================================================
==9089==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 7 byte(s) in 1 object(s) allocated from:
    #0 0x40794f  (/home/al/temp/address_sanitizer/memory-leak+0x40794f)
    #1 0x427f6a  (/home/al/temp/address_sanitizer/memory-leak+0x427f6a)
    #2 0x7fcaaef9a82f  (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

SUMMARY: LeakSanitizer: 7 byte(s) leaked in 1 allocation(s).

3. 參考資料

Fuzzing初學者指南:利用Address Sanitizer找到更多BUG

gcc AddressSanitizer

sanitizers


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM