LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

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

posted on 2020-02-02 00:00  ArnoldLu  阅读(5732)  评论(0编辑  收藏  举报

导航