ASAN内存校验器

本文介绍了如何使用AddressSanitizer(ASAN)工具检测C++程序中的内存错误,包括堆溢出、栈溢出、使用释放后的内存、使用未初始化的内存等问题。通过编译时插桩和运行时检测,ASAN能提供详细的错误信息和内存状态,帮助开发者定位和修复问题。
摘要由CSDN通过智能技术生成

1. 简介

Windows wdk提供了verifier校验器,用于排查内存泄漏等不好发现和定位的问题。

Linux上的校验器就比较多了,如:

  • Dr.Memory, 检测未初始化的内存访问、double free、use after free 等错误;
  • Mudflap, 检测指针的解引用,静态插桩;
  • Insure, 检测内存泄漏;
  • Valgrind, 慢;
  • ASAN,本文主角。

如果只是检测 memcpy/memset/strcpy 危险函数,可以使用 _FORTIFY_SOURCE机制。

Google的ASAN(AddressSanitizer)应该是最给力的,结合编译器插桩和运行时快速内存检测,包括缓冲区溢出、空指针引用、野指针、Double Free等。

ASAN 不支持检测【使用未初始化的内存】,MSAN(MemorySanitizer)可以。Google其实是提供了一整套校验程序。

项目地址:https://github.com/google/sanitizers

工具原理wiki介绍得很详细。本问主要记录一下ASAN的用法。

ASAN原理

AddressSanitizer is a part of LLVM starting with version 3.1 and a part of GCC starting with version 4.8。

ASAN由两部分组成:

  1. 编译时插桩模块;
  2. 运行时库,替换一些内存操作函数,比如用__asan_malloc替换malloc。

检测原理是在变量的左右内存区域下毒,这些区域又叫雷区/redzone:

void *__asan_malloc(size_t sz) {
    void *rz = malloc(RED_SZ);  // 上接雷区
    Poison(rz, RED_SZ);
    void *addr = malloc(sz);    // 真正分配的内存
    UnPoison(addr, sz);
    rz = malloc(RED_SZ);        // 下接雷区
    Poison(rz, RED_SZ);
    return addr;                // 返回分配的内存首地址
}

内存空间会被划分为以下两个类别:

  • Main application memory ,应用程序代码所使用的内存,后面简称应用内存。
  • Shadow memory, 应用内存的元数据,每8字节应用内存对应1字节影子内存。

影子内存的这 1 字节应用内存的状态:

  • 0,表示整个 8 字节内存都正常;
  • 0n表示有n个字节被下毒,表示前 n 个字节正常、后 8-n 个字节被投毒了;
  • 负数,如fa,表示整个 8 字节都被投毒了,不可访问。

0xfa 表示堆左边的 redzone、0xf1 表示栈左边的 redzone, 还有0xf2, 0xf3等含义,可以在报错信息下面的Shadow byte legend说明部分查看。

报错时,引起报错的memory byte会用方括号括起来。

gdb里也可以看malloc堆空间的信息:

(gdb) p __asan_describe_address

2. Demo

UAF

这是ASAN wiki的第一个例子

源码:

#include <stdlib.h>
int main() {
	char *x = (char*)malloc(10 * sizeof(char*));
	printf("malloc:0x%p\n", array);
	free(x);
	return x[5];
}

编译一下,需要有-fsanitize=address -O1 -fno-omit-frame-pointer这几个编译参数,带上-g则可以定位到源码行数。

可以看到依赖库中有libasan.so。

$ gcc -fsanitize=address -O1 -fno-omit-frame-pointer -g use-after-free.c -o uaf
$ ldd uaf
        linux-vdso.so.1 (0x00007ffe8658f000)
        libasan.so.4 => /usr/lib/x86_64-linux-gnu/libasan.so.4 (0x00007fb511c27000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb511836000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb511632000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fb51142a000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb51120b000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb510e6d000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fb510c55000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fb512de4000)

运行一下,报错信息很详细,一般就是关注爆粗原因、出错的代码行数(需要-g参数编译):

$ ./uaf
malloc:0x0x607000000090
=================================================================
==22264==ERROR: AddressSanitizer: heap-use-after-free on address 0x607000000095 at pc 0x55f9433bfa71 bp 0x7ffe9554f380 sp 0x7ffe9554f370
READ of size 1 at 0x607000000095 thread T0
    #0 0x55f9433bfa70 in main /home/starr/Documents/CProject/use-after-free.c:7
    #1 0x7f3545125c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
    #2 0x55f9433bf929 in _start (/home/starr/Documents/CProject/uaf+0x929)

0x607000000095 is located 5 bytes inside of 80-byte region [0x607000000090,0x6070000000e0)
freed by thread T0 here:
    #0 0x7f35455d37a8 in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xde7a8)
    #1 0x55f9433bfa40 in main /home/starr/Documents/CProject/use-after-free.c:6
    #2 0x7f3545125c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)

previously allocated by thread T0 here:
    #0 0x7f35455d3b40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
    #1 0x55f9433bfa1c in main /home/starr/Documents/CProject/use-after-free.c:4
    #2 0x7f3545125c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)

SUMMARY: AddressSanitizer: heap-use-after-free /home/starr/Documents/CProject/use-after-free.c:7 in main
Shadow bytes around the buggy address:
  0x0c0e7fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c0e7fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c0e7fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c0e7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c0e7fff8000: fa fa fa fa 00 00 00 00 00 00 00 00 00 fa fa fa
=>0x0c0e7fff8010: fa fa[fd]fd fd fd fd fd fd fd fd fd fa fa fa fa
  0x0c0e7fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8060: 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
  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
==22264==ABORTING

[fa]正好是我们申请的、用0xfd填充的内存偏移为5的字节。

HeapOutOfBounds

https://github.com/google/sanitizers/wiki/AddressSanitizerExampleHeapOutOfBounds

源码:

#include <stdio.h>

int main(int argc, char **argv) {
  int *array = new int[100];
  printf("new:0x%p\n", array);
  array[0] = 0;
  int res = array[argc + 100];  // BOOM
  delete [] array;
  return res;
}

编译运行:

$ g++ -fsanitize=address -O1 -fno-omit-frame-pointer -g asan.cpp -o heapOutBound
$ ./heapOutBound
new:0x0x614000000040
=================================================================
==29504==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000001d4 at pc 0x5589e84b8afe bp 0x7fffed2e8950 sp 0x7fffed2e8940
READ of size 4 at 0x6140000001d4 thread T0
    #0 0x5589e84b8afd in main /home/starr/Documents/CProject/asan.cpp:7
    #1 0x7f9226ae4c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
    #2 0x5589e84b8979 in _start (/home/starr/Documents/CProject/heapOutBound+0x979)

0x6140000001d4 is located 4 bytes to the right of 400-byte region [0x614000000040,0x6140000001d0)
allocated by thread T0 here:
    #0 0x7f9226f94608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608)
    #1 0x5589e84b8a6d in main /home/starr/Documents/CProject/asan.cpp:4
    #2 0x7f9226ae4c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/starr/Documents/CProject/asan.cpp:7 in main
Shadow bytes around the buggy address:
  0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c287fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c287fff8030: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
  0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8080: 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
  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
==29504==ABORTING

StackOutOfBounds

AddressSanitizerExampleStackOutOfBounds · google/sanitizers Wiki (github.com)

源码:

#include <stdio.h>

int main(int argc, char **argv) {
  int stack_array[100];
  stack_array[1] = 0;
  printf("new:0x%p\n", stack_array);

  return stack_array[argc + 100];  // BOOM
}

编译运行:

$ g++ -fsanitize=address -O1 -fno-omit-frame-pointer -g asan.cpp -o stackOutBound
$ ./stackOutBound
new:0x0x7ffc3b934e80
=================================================================
==31438==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc3b935014 at pc 0x55fd7c7f0caf bp 0x7ffc3b934e40 sp 0x7ffc3b934e30
READ of size 4 at 0x7ffc3b935014 thread T0
    #0 0x55fd7c7f0cae in main /home/starr/Documents/CProject/asan.cpp:8
    #1 0x7ff69b30ec86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
    #2 0x55fd7c7f0a19 in _start (/home/starr/Documents/CProject/heapOutBound+0xa19)

Address 0x7ffc3b935014 is located in stack of thread T0 at offset 436 in frame
    #0 0x55fd7c7f0b09 in main /home/starr/Documents/CProject/asan.cpp:3

  This frame has 1 object(s):
    [32, 432) 'stack_array' <== Memory access at offset 436 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/starr/Documents/CProject/asan.cpp:8 in main
Shadow bytes around the buggy address:
  0x10000771e9b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771e9c0: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
  0x10000771e9d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771e9e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771e9f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10000771ea00: 00 00[f2]f2 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771ea10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771ea20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771ea30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771ea40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000771ea50: 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
==31438==ABORTING

最后一个0xf1距离方括号字节8*(16*3+2) == 400字节,正好是stack_array的大小。

useAfterReturn

https://github.com/google/sanitizers/wiki/AddressSanitizerExampleUseAfterReturn

这种错有时可以检查出来。

源码:

// By default, AddressSanitizer does not try to detect
// stack-use-after-return bugs.
// It may still find such bugs occasionally
// and report them as a hard-to-explain stack-buffer-overflow.

// You need to run the test with ASAN_OPTIONS=detect_stack_use_after_return=1

int *ptr;
__attribute__((noinline))
void FunctionThatEscapesLocalObject() {
  int local[100];
  ptr = &local[0];
}

int main(int argc, char **argv) {
  FunctionThatEscapesLocalObject();
  return ptr[argc];
}

编译运行:

$ g++ -fsanitize=address -O1 -fno-omit-frame-pointer -g asan.cpp -o useAfterRet
$ ASAN_OPTIONS=detect_stack_use_after_return=1 ./useAfterRet
\=================================================================
==584==ERROR: AddressSanitizer: stack-use-after-return on address 0x7f17e0700024 at pc 0x560a7f92ac12 bp 0x7ffde4dbabc0 sp 0x7ffde4dbabb0
READ of size 4 at 0x7f17e0700024 thread T0
    #0 0x560a7f92ac11 in main /home/starr/Documents/CProject/asan.cpp:17
    #1 0x7f17e4566c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
    #2 0x560a7f92a999 in _start (/home/starr/Documents/CProject/useAfterRet+0x999)

Address 0x7f17e0700024 is located in stack of thread T0 at offset 36 in frame
    #0 0x560a7f92aa89 in FunctionThatEscapesLocalObject() /home/starr/Documents/CProject/asan.cpp:10

  This frame has 1 object(s):
    [32, 432) 'local' <== Memory access at offset 36 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-return /home/starr/Documents/CProject/asan.cpp:17 in main
Shadow bytes around the buggy address:
  0x0fe37c0d7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe37c0d7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe37c0d7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe37c0d7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe37c0d7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0fe37c0d8000: f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0fe37c0d8010: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0fe37c0d8020: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0fe37c0d8030: f5 f5 f5 f5 f5 f5 f5 f5 00 00 00 00 00 00 00 00
  0x0fe37c0d8040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe37c0d8050: 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
==584==ABORTING

useAfterScope

先复习一下volatile关键字:

volatile int i=10;
int j = i;

volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,所以看生成的汇编代码的话,j的赋值是从i的地址读取数据的。

测试源码:

// RUN: clang -O -g -fsanitize=address -fsanitize-address-use-after-scope \
//    use-after-scope.cpp -o /tmp/use-after-scope
// RUN: /tmp/use-after-scope

// Check can be disabled in run-time:
// RUN: ASAN_OPTIONS=detect_stack_use_after_scope=0 /tmp/use-after-scope

volatile int *p = 0;

int main() {
  {
    int x = 0;
    p = &x;
  }
  *p = 5;
  return 0;
}

编译运行:

$ g++ -fsanitize=address -O1 -fno-omit-frame-pointer -fsanitize-address-use-after-scope -g asan.cpp -o useAfterScope
$ ASAN_OPTIONS=detect_stack_use_after_scope=0 ./useAfterScope
=================================================================
==1596==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffdbdcf4b20 at pc 0x5581327a9b99 bp 0x7ffdbdcf4af0 sp 0x7ffdbdcf4ae0
WRITE of size 4 at 0x7ffdbdcf4b20 thread T0
    #0 0x5581327a9b98 in main /home/starr/Documents/CProject/asan.cpp:15
    #1 0x7f75550ecc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
    #2 0x5581327a9999 in _start (/home/starr/Documents/CProject/useAfterScope+0x999)

Address 0x7ffdbdcf4b20 is located in stack of thread T0 at offset 32 in frame
    #0 0x5581327a9a89 in main /home/starr/Documents/CProject/asan.cpp:10

  This frame has 1 object(s):
    [32, 36) 'x' <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope /home/starr/Documents/CProject/asan.cpp:15 in main
Shadow bytes around the buggy address:
  0x100037b96910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b96920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b96930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b96940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b96950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100037b96960: f1 f1 f1 f1[f8]f2 f2 f2 00 00 00 00 00 00 00 00
  0x100037b96970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b96980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b96990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b969a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100037b969b0: 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
==1596==ABORTING

c++静态初始化顺序问题

源码:

// a.cc
#include <stdio.h>
extern int extern_global;
int __attribute__((noinline)) read_extern_global() {
  return extern_global;
}
int x = read_extern_global() + 1;
int main() {
  printf("%d\n", x);
  return 0;
}

// b.cc
int foo() { return 42; }
int extern_global = foo();

编译顺序问题导致的错误:

$ clang++ tmp/init-order/example/a.cc tmp/init-order/example/b.cc && ./a.out 
1
$ clang++ tmp/init-order/example/b.cc tmp/init-order/example/a.cc && ./a.out 
43

用ASAN检查:

$ clang++ -fsanitize=address -g tmp/init-order/example/a.cc tmp/init-order/example/b.cc
$ ASAN_OPTIONS=check_initialization_order=true ./a.out
=================================================================
==26772==ERROR: AddressSanitizer: initialization-order-fiasco on address 0x000001068820 at pc 0x427e74 bp 0x7ffff8295010 sp 0x7ffff8295008
READ of size 4 at 0x000001068820 thread T0
    #0 0x427e73 in read_extern_global() tmp/init-order/example/a.cc:4
    #1 0x42806c in __cxx_global_var_init tmp/init-order/example/a.cc:7
    ...

内存泄露

AddressSanitizerLeakSanitizer · google/sanitizers Wiki (github.com)

这本来是LeakSanitizer的功能,ASAN也继承了这个功能,不过在x86_64 Linux和OS X上才支持。

源码:

#include <stdlib.h>
#include <stdio.h>
void *p;

int main() {
  p = malloc(7);
  printf("malloc:0x%p\n", p);
  p = 0; // The memory is leaked here.
  return 0;
}

编译参数有所改变,不能-O优化.

$ g++ -fsanitize=address -fno-omit-frame-pointer -g asan.cpp -o memLeak
$ ASAN_OPTIONS=detect_leaks=1 ./memLeak
malloc:0x0x602000000010

=================================================================
==3405==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 7 byte(s) in 1 object(s) allocated from:
    #0 0x7fec26de3b40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
    #1 0x56255e6149e7 in main /home/starr/Documents/CProject/asan.cpp:6
    #2 0x7fec26935c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)

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

也可以用LeakSanitizer 试试,wiki给出了一个设置已知泄露并忽略的例子。

suppr.txt:

# This is a known leak.
leak:FooBar

源码:

#include <stdlib.h>

// ignored
void FooBar() {
  malloc(7);
}

void Baz() {
  malloc(5);
}

int main() {
  FooBar();
  Baz();
  return 0;
}

编译后可以看到,依赖的库从libasan.so变成了liblsan.so:

$ g++ -fsanitize=leak -fno-omit-frame-pointer -g asan.cpp -o memLeak
$ ldd memLeak
        linux-vdso.so.1 (0x00007ffca0f6c000)
        liblsan.so.0 => /usr/lib/x86_64-linux-gnu/liblsan.so.0 (0x00007f0c97480000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0c9708f000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f0c96e8b000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f0c96c83000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0c96a64000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f0c9852a000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0c9684c000)

运行:

$ ASAN_OPTIONS=detect_leaks=1  LSAN_OPTIONS=suppressions=suppr.txt ./memLeak

=================================================================
==4583==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 5 byte(s) in 1 object(s) allocated from:
    #0 0x7fde99343acb in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/liblsan.so.0+0xeacb)
    #1 0x55b15a195728 in Baz() /home/starr/Documents/CProject/asan.cpp:9
    #2 0x55b15a195739 in main /home/starr/Documents/CProject/asan.cpp:14
    #3 0x7fde98f65c86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)

-----------------------------------------------------
Suppressions used:
  count      bytes template
      1          7 FooBar
-----------------------------------------------------

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

可以看到FooBar函数的泄露被忽略了。

其它用法用到了再说:

flagdefaultdescription
exitcode23If non-zero, LSan will call _exit(exitcode) upon detecting leaks. This can be different from the exit code used to signal ASan errors.
max_leaks0If non-zero, report only this many top leaks.
suppressions(none)Path to file containing suppression rules (see below)
print_suppressions1If 1, print statistics for matched suppressions.
report_objects0If 1, LSan will report the addresses of individual leaked objects.
use_unaligned0If 0, LSan will only consider properly aligned 8-byte patterns when looking for pointers. Set to 1 to include unaligned patterns. This refers to the pointer itself, not the memory being pointed at.

3. 其它参考文章

https://zhuanlan.zhihu.com/p/382994002

https://zhuanlan.zhihu.com/p/390555316

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值