Page 1 of 1

【分享】内存错误分析 - AddressSanitizer

Posted: 2023年 Dec 1日 11:26
by Kyson

C/C++ 通常作为嵌入式开发的首选编程语言,在开发过程中,碰到内存错误是很常见的。内存错误是非常致命的问题,一旦发生通常会引起程序奔溃,并且排查起来极其困难。

幸运的是,我们可以借助一些工具来检测此类错误,使用编译工具链内置的 AddressSanitize 功能是最佳的解决方案。

简介

AddressSanitizer(简称 ASan)是一种内存错误检测器,主要用于 C/C++ 程序。它可以在运行时检测出诸如缓冲区溢出、使用未初始化的内存、使用已释放的内存等常见内存错误。从 GCC 4.8 开始,AddressSanitizer 成为 GCC 的一部分。通过使用 AddressSanitizer,我们可以在开发过程中及时发现并修复这些内存错误的问题,从而提高软件的稳定性和安全性。

基本使用

由于编译工具内置了 AddressSanitizer,所以使用上非常方便,只需要在编译时添加特定的编译选项就可以启用 AddressSanitizer。

编译程序的时候,添加 -fsanitize=address-fno-omit-frame-pointer 编译选项即可。示例:

Code: Select all

$ gcc -fsanitize=address -fno-omit-frame-pointer -g -o my_program my_program.c

把编译好的程序放到板子上运行,如果程序中存在内存错误,AddressSanitizer 将在检测到错误时终止程序,并输出详细的错误信息,我们可以根据这些信息来定位问题。

注意,AddressSanitizer 依赖 libasan 运行库,如果板子上的固件没有包含该库,需要从交叉编译工具链里面查找,把工具链中的动态库上传到板子上。

访问释放的内存

Code: Select all

/**
 * $ gcc -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -g example_UseAfterFree.c -o example_UseAfterFree
 * $ ./example_UseAfterFree
 */
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    int *array = (int *)malloc(100 * sizeof(int));
    free(array);
    return array[0];
}

Code: Select all

=================================================================
==22429==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000040 at pc 0x555c1cd8b22d bp 0x7ffda0b6ffc0 sp 0x7ffda0b6ffb0
READ of size 4 at 0x614000000040 thread T0
    #0 0x555c1cd8b22c in main /tmp/test/prog/asan_demo/example_UseAfterFree.c:11
    #1 0x7f68ae183082 in __libc_start_main ../csu/libc-start.c:308
    #2 0x555c1cd8b10d in _start (/tmp/test/prog/asan_demo/example_UseAfterFree+0x110d)

0x614000000040 is located 0 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)
freed by thread T0 here:
    #0 0x7f68ae45e40f in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:122
    #1 0x555c1cd8b1f5 in main /tmp/test/prog/asan_demo/example_UseAfterFree.c:10
    #2 0x7f68ae183082 in __libc_start_main ../csu/libc-start.c:308

previously allocated by thread T0 here:
    #0 0x7f68ae45e808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x555c1cd8b1e5 in main /tmp/test/prog/asan_demo/example_UseAfterFree.c:9
    #2 0x7f68ae183082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-use-after-free /tmp/test/prog/asan_demo/example_UseAfterFree.c:11 in main
Shadow bytes around the buggy address:
  0x0c287fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  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[fd]fd fd fd fd fd fd fd
  0x0c287fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8030: fd fd fd fd fd fd fd fd fd fd 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
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
==22429==ABORTING

检测到错误:

Code: Select all

... ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000040 ...

紧接着是发生错误地方的栈回溯信息:

Code: Select all

...
    #0 0x555c1cd8b22c in main /tmp/test/prog/asan_demo/example_UseAfterFree.c:11
    #1 0x7f68ae183082 in __libc_start_main ../csu/libc-start.c:308
    #2 0x555c1cd8b10d in _start (/tmp/test/prog/asan_demo/example_UseAfterFree+0x110d)
...

堆溢出

Code: Select all

/**
 * $ gcc -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -g example_HeapOutOfBounds.c -o example_HeapOutOfBounds
 * $ ./example_HeapOutOfBounds
 */
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    int *array = (int *)malloc(100 * sizeof(int));
    array[0] = 0;
    int res = array[100]; // BOOM
    free(array);
    return 0;
}

Code: Select all

=================================================================
==22873==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000001d0 at pc 0x55c246779288 bp 0x7ffcec56dff0 sp 0x7ffcec56dfe0
READ of size 4 at 0x6140000001d0 thread T0
    #0 0x55c246779287 in main /tmp/test/prog/asan_demo/example_HeapOutOfBounds.c:11
    #1 0x7f0c6bd3a082 in __libc_start_main ../csu/libc-start.c:308
    #2 0x55c24677912d in _start (/tmp/test/prog/asan_demo/example_HeapOutOfBounds+0x112d)

0x6140000001d0 is located 0 bytes to the right of 400-byte region [0x614000000040,0x6140000001d0)
allocated by thread T0 here:
    #0 0x7f0c6c015808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x55c246779205 in main /tmp/test/prog/asan_demo/example_HeapOutOfBounds.c:9
    #2 0x7f0c6bd3a082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/test/prog/asan_demo/example_HeapOutOfBounds.c:11 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
  Shadow gap:              cc
==22873==ABORTING

栈溢出

Code: Select all

/**
 * $ gcc -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -g example_StackOutOfBounds.c -o example_StackOutOfBounds
 * $ ./example_StackOutOfBounds
 */
#include <stdio.h>

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

Code: Select all

=================================================================
==23572==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd47c64410 at pc 0x558901e0931b bp 0x7ffd47c64230 sp 0x7ffd47c64220
READ of size 4 at 0x7ffd47c64410 thread T0
    #0 0x558901e0931a in main /tmp/test/prog/asan_demo/example_StackOutOfBounds.c:10
    #1 0x7f02e25d7082 in __libc_start_main ../csu/libc-start.c:308
    #2 0x558901e0912d in _start (/tmp/test/prog/asan_demo/example_StackOutOfBounds+0x112d)

Address 0x7ffd47c64410 is located in stack of thread T0 at offset 448 in frame
    #0 0x558901e091f8 in main /tmp/test/prog/asan_demo/example_StackOutOfBounds.c:7

  This frame has 1 object(s):
    [48, 448) 'stack_array' (line 8) <== Memory access at offset 448 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 /tmp/test/prog/asan_demo/example_StackOutOfBounds.c:10 in main
Shadow bytes around the buggy address:
  0x100028f84830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028f84840: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 f1 f1
  0x100028f84850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028f84860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028f84870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100028f84880: 00 00[f3]f3 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00
  0x100028f84890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028f848a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028f848b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028f848c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100028f848d0: 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
==23572==ABORTING

全局缓冲区溢出

Code: Select all

/**
 * $ gcc -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -g example_GlobalOutOfBounds.c -o example_GlobalOutOfBounds
 * $ ./example_GlobalOutOfBounds
 */
#include <stdio.h>

int global_array[100] = {-1};

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

Code: Select all

=================================================================
==24295==ERROR: AddressSanitizer: global-buffer-overflow on address 0x56444bf4c1b0 at pc 0x56444bf4920e bp 0x7ffe94aecd20 sp 0x7ffe94aecd10
READ of size 4 at 0x56444bf4c1b0 thread T0
    #0 0x56444bf4920d in main /tmp/test/prog/asan_demo/example_GlobalOutOfBounds.c:11
    #1 0x7f84d799b082 in __libc_start_main ../csu/libc-start.c:308
    #2 0x56444bf4910d in _start (/tmp/test/prog/asan_demo/example_GlobalOutOfBounds+0x110d)

0x56444bf4c1b0 is located 0 bytes to the right of global variable 'global_array' defined in 'example_GlobalOutOfBounds.c:7:5' (0x56444bf4c020) of size 400
SUMMARY: AddressSanitizer: global-buffer-overflow /tmp/test/prog/asan_demo/example_GlobalOutOfBounds.c:11 in main
Shadow bytes around the buggy address:
  0x0ac9097e17e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ac9097e17f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ac9097e1800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ac9097e1810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ac9097e1820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0ac9097e1830: 00 00 00 00 00 00[f9]f9 f9 f9 f9 f9 00 00 00 00
  0x0ac9097e1840: f9 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00
  0x0ac9097e1850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ac9097e1860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ac9097e1870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ac9097e1880: 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
==24295==ABORTING

返回后使用堆栈内存

Code: Select all

/**
 * $ gcc -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -g example_UseAfterReturn.c -o example_UseAfterReturn
 * $ ASAN_OPTIONS=detect_stack_use_after_return=1 && ./example_UseAfterReturn
 */
#include <stdio.h>

int *p = NULL;

void foo(void)
{
    int local[100];
    p = &local[0];
}

int main(int argc, char **argv)
{
    foo();
    return p[0];
}

Code: Select all

=================================================================
==25711==ERROR: AddressSanitizer: stack-use-after-return on address 0x7f8a84f0a030 at pc 0x5578a77e03a4 bp 0x7ffddc5d4890 sp 0x7ffddc5d4880
READ of size 4 at 0x7f8a84f0a030 thread T0
    #0 0x5578a77e03a3 in main /tmp/test/prog/asan_demo/example_UseAfterReturn.c:18
    #1 0x7f8a88430082 in __libc_start_main ../csu/libc-start.c:308
    #2 0x5578a77e014d in _start (/tmp/test/prog/asan_demo/example_UseAfterReturn+0x114d)

Address 0x7f8a84f0a030 is located in stack of thread T0 at offset 48 in frame
    #0 0x5578a77e0218 in foo /tmp/test/prog/asan_demo/example_UseAfterReturn.c:10

  This frame has 1 object(s):
    [48, 448) 'local' (line 11) <== Memory access at offset 48 is inside 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-use-after-return /tmp/test/prog/asan_demo/example_UseAfterReturn.c:18 in main
Shadow bytes around the buggy address:
  0x0ff1d09d93b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ff1d09d93c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ff1d09d93d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ff1d09d93e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ff1d09d93f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0ff1d09d9400: f5 f5 f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0ff1d09d9410: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0ff1d09d9420: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0ff1d09d9430: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x0ff1d09d9440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ff1d09d9450: 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
==25711==ABORTING

注意,默认是不检测返回后使用栈内存,需要在运行时设置环境变量 ASAN_OPTIONS=detect_stack_use_after_return=1。

使用超出范围的堆栈内存

Code: Select all

/**
 * $ gcc -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -g example_UseAfterScope.c -o example_UseAfterScope
 * $ ./example_UseAfterScope
 */
#include <stdio.h>

volatile int *p = 0;

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

Code: Select all

=================================================================
==29749==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc6c8de0b0 at pc 0x563079d0f320 bp 0x7ffc6c8de080 sp 0x7ffc6c8de070
WRITE of size 4 at 0x7ffc6c8de0b0 thread T0
    #0 0x563079d0f31f in main /tmp/test/prog/asan_demo/example_UseAfterScope.c:14
    #1 0x7fefabddd082 in __libc_start_main ../csu/libc-start.c:308
    #2 0x563079d0f14d in _start (/tmp/test/prog/asan_demo/example_UseAfterScope+0x114d)

Address 0x7ffc6c8de0b0 is located in stack of thread T0 at offset 32 in frame
    #0 0x563079d0f218 in main /tmp/test/prog/asan_demo/example_UseAfterScope.c:9

  This frame has 1 object(s):
    [32, 36) 'x' (line 11) <== 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, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope /tmp/test/prog/asan_demo/example_UseAfterScope.c:14 in main
Shadow bytes around the buggy address:
  0x10000d913bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10000d913c10: 00 00 f1 f1 f1 f1[f8]f3 f3 f3 00 00 00 00 00 00
  0x10000d913c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000d913c60: 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
==29749==ABORTING

内存泄露

Code: Select all

/**
 * $ gcc -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -g memory-leak.c -o memory-leak
 * $ ASAN_OPTIONS=detect_leaks=1 ./memory-leak
 */
#include <stdio.h>
#include <stdlib.h>

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

Code: Select all

=================================================================
==30333==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 7 byte(s) in 1 object(s) allocated from:
    #0 0x7fa1e02c1808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x5616de82d1a5 in main /tmp/test/prog/asan_demo/memory-leak.c:10
    #2 0x7fa1dffe6082 in __libc_start_main ../csu/libc-start.c:308

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

注意,可以在运行时设置环境变量 ASAN_OPTIONS=detect_leaks=1 开启内存泄露检测。

基本原理

AddressSanitizer 是一种基于阴影内存(shadow memory)的调试工具,用于检测内存错误。它的主要工作原理是通过使用特殊的内存分配器和代码注入来实现。

AddressSanitizer 在编译时对代码进行修改,以便在运行时能够检测到内存错误。它会在每个内存访问之前检查“影子状态”(shadow state),并创建红色区域(red zones)来检测溢出和未定义行为。影子状态是一种映射机制,它可以将物理内存地址映射到虚拟地址空间中的特定位置,从而使得 AddressSanitizer 能够跟踪所有内存操作。它将虚拟地址空间分成两部分:应用程序内存和阴影内存。应用程序内存是实际使用的内存区域,而阴影内存则是附加的内存区域,用于存储每个内存地址是否可访问的信息。

当应用程序尝试访问某个内存地址时,AddressSanitizer 会检查该地址对应的阴影字节值。如果该值为零,则表示该地址不可访问;否则,AddressSanitizer 将继续执行应用程序代码并记录该内存访问。

如果应用程序试图访问不可访问的内存地址,AddressSanitizer 将生成一个崩溃报告,并停止程序运行。这有助于开发人员及时发现和修复内存错误。