Skip to content

Address Sanitizer 初体验

Posted on:2021.08.14

TOC

Open TOC

Address Sanitizer 初体验

相关资源

https://github.com/google/sanitizers

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

https://clang.llvm.org/docs/AddressSanitizer.html

使用方法

man gcc 是个好东西 🤣

预备知识:

1、一些编译选项

2、编译优化

-O -O0 -O1 -O2 -O3 -Os -Ofast -Og

-O0
Reduce compilation time and make debugging produce the expected results. This is the default.
-Og
Optimize debugging experience.
-Og should be the optimization level of choice for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience. It is a better choice than -O0 for producing debuggable code because some compiler passes that collect debug information are disabled at -O0.

进入正题,为了使用 Address Sanitizer,我们需要加上选项 -fsanitize=address

下面是与其相关的选项:

显示更多的调试信息,如出错行

不要忽略栈指针

-fomit-frame-pointer
Omit the frame pointer in functions that don't need one. This avoids the instructions to save, set up and restore the frame pointer; on many targets it also makes an extra register available.
On some targets this flag has no effect because the standard calling sequence always uses a frame pointer, so it cannot be omitted.
Note that -fno-omit-frame-pointer doesn't guarantee the frame pointer is used in all functions. Several targets always omit the frame pointer in leaf functions.
Enabled by default at -O and higher.

不要优化尾递归

-foptimize-sibling-calls
Optimize sibling and tail recursive calls.
Enabled at levels -O2, -O3, -Os.

示例

这里是官方文档中的示例:

下面自己写几个 bug

heap-buffer-overflow

示例程序 demo.cpp

#include <cstdio>
void foo() {
int *arr = new int[100];
printf("%d", arr[100]);
}
int main() {
foo();
}

编译并运行

$ g++ -fsanitize=address demo.cpp -o demo
$ ./demo
=================================================================
==7761==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000001d0 at pc 0x55dfa0709265 bp 0x7ffdb7db5a90 sp 0x7ffdb7db5a80
READ of size 4 at 0x6140000001d0 thread T0
#0 0x55dfa0709264 in foo() (/home/vgalaxy/Templates/demo+0x1264)
#1 0x55dfa0709289 in main (/home/vgalaxy/Templates/demo+0x1289)
#2 0x7fc17d587564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
#3 0x55dfa070914d in _start (/home/vgalaxy/Templates/demo+0x114d)
0x6140000001d0 is located 0 bytes to the right of 400-byte region [0x614000000040,0x6140000001d0)
allocated by thread T0 here:
#0 0x7fc17d801717 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
#1 0x55dfa070921e in foo() (/home/vgalaxy/Templates/demo+0x121e)
#2 0x55dfa0709289 in main (/home/vgalaxy/Templates/demo+0x1289)
#3 0x7fc17d587564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/vgalaxy/Templates/demo+0x1264) in foo()
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
==7761==ABORTING

若加上 -g 选项显示出错行,SUMMARY 那一行会有所变化:

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/vgalaxy/Templates/demo.cpp:5 in foo()

heap-use-after-free

略加修改 demo.cpp

#include <cstdio>
void foo() {
int *arr = new int[100];
delete[] arr;
printf("%d", arr[0]);
}
int main() {
foo();
}

编译并运行

$ g++ -fsanitize=address -g demo.cpp -o demo
$ ./demo
=================================================================
==10254==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000040 at pc 0x55b97834728d bp 0x7ffc32cff230 sp 0x7ffc32cff220
READ of size 4 at 0x614000000040 thread T0
#0 0x55b97834728c in foo() /home/vgalaxy/Templates/demo.cpp:6
#1 0x55b9783472b5 in main /home/vgalaxy/Templates/demo.cpp:10
#2 0x7f2194670564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
#3 0x55b97834716d in _start (/home/vgalaxy/Templates/demo+0x116d)
0x614000000040 is located 0 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)
freed by thread T0 here:
#0 0x7f21948eb217 in operator delete[](void*) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:163
#1 0x55b978347255 in foo() /home/vgalaxy/Templates/demo.cpp:5
#2 0x55b9783472b5 in main /home/vgalaxy/Templates/demo.cpp:10
#3 0x7f2194670564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
previously allocated by thread T0 here:
#0 0x7f21948ea717 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
#1 0x55b97834723e in foo() /home/vgalaxy/Templates/demo.cpp:4
#2 0x55b9783472b5 in main /home/vgalaxy/Templates/demo.cpp:10
#3 0x7f2194670564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
SUMMARY: AddressSanitizer: heap-use-after-free /home/vgalaxy/Templates/demo.cpp:6 in foo()
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
==10254==ABORTING

stack-buffer-overflow

略加修改 demo.cpp

#include <cstdio>
void foo() {
int arr[100];
printf("%d", arr[100]);
}
int main() {
foo();
}

编译并运行:

$ g++ -fsanitize=address -g demo.cpp -o demo
$ ./demo
=================================================================
==8870==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd2217c150 at pc 0x559bb8437304 bp 0x7ffd2217bf80 sp 0x7ffd2217bf70
READ of size 4 at 0x7ffd2217c150 thread T0
#0 0x559bb8437303 in foo() /home/vgalaxy/Templates/demo.cpp:5
#1 0x559bb84373c5 in main /home/vgalaxy/Templates/demo.cpp:9
#2 0x7f2e00477564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
#3 0x559bb843716d in _start (/home/vgalaxy/Templates/demo+0x116d)
Address 0x7ffd2217c150 is located in stack of thread T0 at offset 448 in frame
#0 0x559bb8437238 in foo() /home/vgalaxy/Templates/demo.cpp:3
This frame has 1 object(s):
[48, 448) 'arr' (line 4) <== 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 /home/vgalaxy/Templates/demo.cpp:5 in foo()
Shadow bytes around the buggy address:
0x1000244277d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000244277e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000244277f0: 00 00 f1 f1 f1 f1 f1 f1 00 00 00 00 00 00 00 00
0x100024427800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100024427810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100024427820: 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3 f3 f3
0x100024427830: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100024427840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100024427850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100024427860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100024427870: 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
==8870==ABORTING

memory leaks

略加修改 demo.cpp

void foo() {
int *arr = new int[100];
}
int main() {
foo();
}

编译并运行:

$ g++ -fsanitize=address -g demo.cpp -o demo
$ ./demo
=================================================================
==9562==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x7f19e192c717 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
#1 0x55b83ce4219e in foo() /home/vgalaxy/Templates/demo.cpp:4
#2 0x55b83ce421b2 in main /home/vgalaxy/Templates/demo.cpp:8
#3 0x7f19e16b2564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s).

Sanitizer 其他成员

Sanitizer 作为 LLVM Clang 的一部分,还有其他一些成员

gcc 只是蹭了别人的东西……

ThreadSanitizer

https://clang.llvm.org/docs/ThreadSanitizer.html

选项 -fsanitize=thread

由于不支持非位置无关的可执行文件,所以需要加上选项 -fPIE -pie

下面是选项之间的细微区别

-fpie
-fPIE
These options are similar to -fpic and -fPIC, but the generated position-independent code can be only linked into executables.
Usually these options are used to compile code that will be linked using the -pie GCC option.
-fpic
Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine.
-pie
Produce a dynamically linked position independent executable on targets that support it. For predictable results, you must also specify the same set of options used for compilation (-fpie, -fPIE, or model suboptions) when you specify this linker option.

MemorySanitizer

MemorySanitizer (MSan) is a detector of uninitialized memory reads in C/C++ programs.

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

https://clang.llvm.org/docs/MemorySanitizer.html

选项 -fsanitize=memory

g++ 不支持

LeakSanitizer

https://clang.llvm.org/docs/LeakSanitizer.html

LeakSanitizer is a run-time memory leak detector. It can be combined with AddressSanitizer to get both memory error and leak detection, or used in a stand-alone mode. LSan adds almost no performance overhead until the very end of the process, at which point there is an extra leak detection phase.

已经内置在 Address Sanitizer 里面了

使用选项 -fsanitize=leak 可以单独开启

UndefinedBehaviorSanitizer

https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html

UndefinedBehaviorSanitizer (UBSan) is a fast undefined behavior detector. UBSan modifies the program at compile-time to catch various kinds of undefined behavior during program execution, for example:

选项 -fsanitize=undefined

Limitations

RTFM

比如必须要位置无关,或者不支持静态链接之类的

With CMake

TODO → LLVM

like cmu-db

make format
make check-lint
make check-clang-tidy

use sanitizer

make check-asan
make check-tsan
make check-ubsan