關鍵詞: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檢查使用未初始化內存問題。
內核Sanitizer包括KASAN和KMSAN,分別提供內核中動態內存錯誤檢查和未初始化內存使用問題檢查。
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》
