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、一些编译选项
-E
预处理-S
编译-c
编译并汇编,产生可重定位目标文件-o <file>
编译、汇编并链接,产生可执行目标文件
2、编译优化
-O -O0 -O1 -O2 -O3 -Os -Ofast -Og
-O0Reduce compilation time and make debugging produce the expected results. This is the default.
-OgOptimize 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
下面是与其相关的选项:
-g
显示更多的调试信息,如出错行
-fno-omit-frame-pointer
不要忽略栈指针
-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.
-fno-optimize-sibling-calls
不要优化尾递归
-foptimize-sibling-calls
Optimize sibling and tail recursive calls.
Enabled at levels -O2, -O3, -Os.
示例
这里是官方文档中的示例:
- Use after free (dangling pointer dereference)
- Heap buffer overflow
- Stack buffer overflow
- Global buffer overflow
- Use after return
- Use after scope
- Initialization order bugs
- Memory leaks
下面自己写几个 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 0x7ffdb7db5a80READ 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 faShadow 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 0x7ffc32cff220READ 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 faShadow 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 0x7ffd2217bf70READ 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 variableHINT: 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 00Shadow 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-fPIEThese 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.
-fpicGenerate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine.
-pieProduce 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:
- Array subscript out of bounds, where the bounds can be statically determined
- Bitwise shifts that are out of bounds for their data type
- Dereferencing misaligned or null pointers
- Signed integer overflow
- Conversion to, from, or between floating-point types which would overflow the destination
选项 -fsanitize=undefined
Limitations
RTFM
比如必须要位置无关,或者不支持静态链接之类的
With CMake
TODO → LLVM
like cmu-db
make formatmake check-lintmake check-clang-tidy
use sanitizer
make check-asanmake check-tsanmake check-ubsan