Address Sanitizer(Asan)原理及相关GCC选项列表


该工具由编译器检测模块和替换malloc函数的一个运行库组成

1.1      ASAN介绍   
ASAN,也即地址消毒技术,通过编译插桩,能够发现此堆/栈/全局变量读写溢出,内存泄露等问题,并将信息直接打印到日志中。(内存泄露检测功能已支持BMC的arm32位平台)。

       ASAN地址消毒与valgrind工具的比较:
悬空指针
堆缓冲区溢出
堆栈缓冲区溢出
全局缓冲区溢出
use after free
初始化顺序错误
内存泄漏

       ASAN地址消毒的优点:

1、  不用修改模块代码,直接编译插桩。

2、  问题检测的准确率高,日志中打印的信息很全面。

3、  使用方便,和BMC直接调试APP的方法很相似。

ASAN的运行条件为gcc版本 > 4.8,版本越高,可支持的ASAN编译参数越多,功能越强大,


4.2      ASan检测原理
ASan分两部分。一部分是在编译时会通过插入的检测代码(即编译时插桩)。第二部分是运行库,它存在于libasan.so动态库中,运行时,会对GLIBC中如下表一所示的29个函数进行替换以便检测内存错误(即运行时插桩)[3]。

通过对ASan源代码asan_interceptors.cc的函数InitializeAsanInterceptors分析,可以看出,ASan会防护如下GLIBC库28个函数。

表一 ASan防护函数列表

序号

分类

函数名

1

内存管理函数

malloc

2

free

3

mem*函数

memcmp

4

memcpy

5

memmove

6

str*函数

strcat

7

strchr

8

strcpy

9

strlen

10

wcslen

11

strncat

12

strncpy

13

strdup

14

strnlen

15

类型转换函数

atoi

16

strtol

17

atol

18

atoll

19

strtoll

20

signal,
jump相关函数

longjmp

21

sigaction

22

signal

23

siglongjmp

24

其他函数

index

25

pthread_create

26

atexit

27

throw

28

fork

以malloc 和free为例。通过gdb单步调试,发现malloc会被__interceptor_malloc代替。提示信息如下。

__interceptor_malloc (size=248) at ../../.././libsanitizer/asan/asan_malloc_linux.cc:38

free会被__interceptor_free代替,__interceptor_free会进一步调用Deallocate。

  ASan对缓冲区溢出防护的的基本步骤如下:

1.        通过在被保护的栈、全局变量、堆周围建立标记为中毒状态(Poisnoned)的red-zones;

以栈缓冲区溢出检测为例,如下代码展示了red-zone的建立。
ASan使用影子内存来确定内存了访问是否安全。此外,ASan使用更加高效的映射机制和更加紧凑的影子编码,可以检测栈、全局变量和堆的错误,比AddrCheck工具速度要快。

ASan使能后,缓冲区的每一次访问之前,均会添加检测代码。


6      Address Sanitizer相关的GCC编译选项汇总
以GCC-5.1.0为例,除了-fsanitize=address外,还有一些其他的检测功能,详情如下。

1

-fsanitize=address

用户态内存错误检测,可以被环境变量ASAN_OPTIONS控制具体行为

2

-fsanitize=kernel-address

内核态内存错误检测器

3

-fsanitize=thread

使能ThreadSanitizer,快速数据竞争检测。

4

-fsanitize=leak

使能LeakSanitizer,内存泄露检测,作用于链接阶段。只有当-fsanitize=address和-fsanitize=thread均不使能时才有效。没有-fsanitize=address功能全面,只做内存泄露检测,但是速度比-fsanitize=address要快。

内存泄露检查-fsanitize=leak只有在GCC版本≥4.9时才有效。

5

-fsanitize=undefined

运行时快速未定义行为检测器。包括如下6~22共17个子选项。

6

-fsanitize=shift

移位操作符的移位大小超过了位宽或者小于零,或者左边是负值。 对于有符号数移位, 检查C中的有符号溢出,在C++中检查无符号溢出。

7

-fsanitize=integer-divide-by-zero

整数除零。

8

-fsanitize=unreachable

如果控制流到达 __builtin_unreachable.

9

-fsanitize=vla-bound

可变长数组边界值非正。

10

-fsanitize=null

使用一个空指针或者创建一个空引用

11

-fsanitize=return

仅C++有效,对函数返回值进行检查,定义了返回值为非空的函数如果未返回有效值将会报错。

12

-fsanitize=signed-integer-overflow

有符号整数溢出, 包含所有通过 -ftrapv 添加的检查, 并且检查有符号除法溢出 (INT_MIN / -1)。

13

-fsanitize=bounds

数组索引越界, 以防数组边界可以静态检测。

14

-fsanitize=alignment

使用一个未对齐的指针或者引用。

15

-fsanitize=object-size

尝试使用优化器可以探测到不属于访问对象的字节。 对象的大小使用 __builtin_object_size 检测, 并且结果可能会探测到多个问题在高层次的优化。

16

-fsanitize=float-divide-by-zero

浮点除零。不能被-fsanitize=undefined使能。

17

-fsanitize=float-cast-overflow

浮点到整形转换的检查,不能被-fsanitize=undefined使能。

18

-fsanitize=nonnull-attribute

对使用__attribute__ nonnull限定参数非空的函数进行检查。

19

-fsanitize=returns-nonnull-attribute

对使用__attribute__((returns_nonnull))限定返回为非空指针的函数进行检测。

20

-fsanitize=bool

加载一个既不是真也不是假的bool值。

21

-fsanitize=enum

加载一个枚举类型的值,但是值不在那个枚举类型范围内。

22

-fsanitize=vptr

使用一个vptr预示着具有错误动态类型的对象,或者它的生命长度还未开始或者已经结束。与 -fno-rtti 兼容。

23

-fno-sanitize=all

禁止之前所有的子选项,-fsanitize=all是不能使用的,因某些子选项是不兼容的

24

-fasan-shadow-offset=number

自定义AddressSanitizer检查中的shadow偏移。

25

-fsanitize-recover[=opts]

使用逗号分隔的列表控制错误恢复模式。如:-fsanitize-recover=undefined,float-cast-overflow,float-divide-by-zero

-fno-sanitize-recover等同于

-fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero

 

26

-fsanitize-undefined-trap-on-error

控制编译器用using __builtin_trap替代libubsan报告未定义行为.

你可能感兴趣的:(C++/C内存问题检测工具)