Address Sanitizer是google的C/C++内存地址错误检查器。其在编译时和运行时发挥作用,已被集成进了各大编译器之中。
一、它可以检测出下面这些错误
1. Use after free (dangling pointer dereference):使用已释放的堆内存
// RUN: clang -O -g -fsanitize=address %t && ./a.out
int main(int argc, char **argv) {
int *array = new int[100];
delete [] array;
return array[argc]; // BOOM
}
2. Heap buffer overflow:堆内存溢出
// RUN: clang -O -g -fsanitize=address %t && ./a.out
int main(int argc, char **argv) {
int *array = new int[100];
array[0] = 0;
int res = array[argc + 100]; // BOOM
delete [] array;
return res;
}
3. Stack buffer overflow:栈内存溢出
// RUN: clang -O -g -fsanitize=address %t && ./a.out
int main(int argc, char **argv) {
int stack_array[100];
stack_array[1] = 0;
return stack_array[argc + 100]; // BOOM
}
4. Global buffer overflow:访问的区域是全局变量, 并且超过了分配给它的空间
// RUN: clang -O -g -fsanitize=address %t && ./a.out
int global_array[100] = {-1};
int main(int argc, char **argv) {
return global_array[argc + 100]; // BOOM
}
5. Use after return:默认不开启, 指: 函数在栈上的局部变量在函数返回后被使用
// RUN: clang -O -g -fsanitize=address %t && ./a.out
// 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];
}
6. Use after scope:使用作用域之外的变量
// 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;
}
7. Initialization order bugs:默认不开启,检查全局变量或静态变量初始化的时候有没有利用未初始化的变量
$ cat tmp/init-order/example/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;
}
$ cat tmp/init-order/example/b.cc
int foo() { return 42; }
int extern_global = foo();
8. Memory leaks:内存泄漏,检查未释放的堆内存
$ cat memory-leak.c
#include <stdlib.h>
void *p;
int main() {
p = malloc(7);
p = 0; // The memory is leaked here.
return 0;
}
$ clang -fsanitize=address -g memory-leak.c
$ ./a.out
二、使用
ASAN的使用需编译器支持。GCC、clang最简单的使用方式是增加编译选项:-fsanitize=address。更多选项可以看官方文档:
clang的使用方式:
AddressSanitizer — Clang 16.0.0git documentation
GCC的使用方式:
Instrumentation Options (Using the GNU Compiler Collection (GCC))
?
三、原理简介
首先我们需要接管每次内存分配/释放. 并且每一次对内存的读/写都需要加上一个检查 (所以需要编译器的配合).
对于上面这些需要检测出的问题, ASan 提出了解决方案, 可以比较好的处理这些问题, 同时不至于损失太多性能/空间.
参考资料:
1.?AddressSanitizer · google/sanitizers Wiki · GitHub
|