使用AddressSanitizer做内存分析

发布于 2019-05-29  141 次阅读


介绍

AddressSanitizer简称asan或者sanitizer.
lvm提供了内存调试工具,例如地址sanitizer、内存sanitizer。
目前AddressSanitizer能够发现如下问题:

  • Out-of-bounds accesses to heap, stack and globals
  • Use-after-free
  • Use-after-return (runtime flag ASAN_OPTIONS=detect_stack_use_after_return=1)
  • Use-after-scope (clang flag -fsanitize-address-use-after-scope)
  • Double-free, invalid free
  • Memory leaks (experimental)

准备工作

要使用sanitizer,需要从llvm svn中把compiler-rt下载下来
···
cd llvm/project
svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
···
然后重新编译构建llvm(正如之前的编译安装),就能获得所需要的运行时库了。

详细步骤

我们以下的程序为例

int main(){
    int a[5];
    int index=6;
    int retval=a[index];
    return retval;
}

使用在编译程序时加上-fsanitize=address -fno-omit-frame-pointer两个编译选项即可,需要说明的是要使用系统自带的内存管理库,不能使用第三方的内存管理库,因为这个功能要拦截malloc,free等标准函数。
gcc几个常用编译选项如下:

  • -fsanitize=address    开启地址越界检查功能
  • -fno-omit-frame-pointer  开启后,可以出界更详细的错误信息
  • -fsanitize=leak   开启内存泄露检查功能
    我们此处使用
g++ mem.c -fsanitize=address -g

也可以使用clang -fsanitize=address mem.c(但好像需要debug版本,以及compiler-rt)
设置环境变量(实测非必要)

export ASAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer
export ASAN_OPTIONS=use_sigaltstack=1:verbosity=0:handle_segv=1:allow_user_segv_handler=1:detect_leaks=1

然后使用如下执行地址sanitizer

./a.out

执行以后可以看到详细的报告

./a.out
=================================================================
==7494==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe7d214fd8 at pc 0x559f0634e23e bp 0x7ffe7d214f80 sp 0x7ffe7d214f70
READ of size 4 at 0x7ffe7d214fd8 thread T0
    #0 0x559f0634e23d in main /home/happy/work/mem.c:4
    #1 0x7ff0949a7b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
    #2 0x559f0634e0b9 in _start (/home/happy/work/a.out+0x10b9)

Address 0x7ffe7d214fd8 is located in stack of thread T0 at offset 56 in frame
    #0 0x559f0634e184 in main /home/happy/work/mem.c:1

  This frame has 1 object(s):
    [32, 52) 'a' <== Memory access at offset 56 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/happy/work/mem.c:4 in main
Shadow bytes around the buggy address:
  0x10004fa3a9a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3a9b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3a9c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3a9d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3a9e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10004fa3a9f0: 00 00 00 00 f1 f1 f1 f1 00 00 04[f2]00 00 00 00
  0x10004fa3aa00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3aa10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3aa20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3aa30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004fa3aa40: 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==7494==ABORTING