admin 2025-07-26
147
2012年,Google发布了一篇论文:《AddressSanitizer:AFastAddressSanityChecker》。介绍了一种新的内存检测方法,让上面的问题得到了很大的改进。截止到目前,AddressSanitizer已经被广泛应用到了各种语言:C、C++、Objective-C、Java等。以Google自己的benchmark为参考,开启ASan后,会使程序运行速度平均下降2倍左右,内存占用会增加3倍左右(和硬件有关)。这和其他工具动辄十几倍的消耗要好上不少。(如果要跑比较大的包,还是建议用AddressSanitzer的)。对于AddressSanitizer可以用来检测如下内存使用错误:
内存释放后又被使用;
内存重复释放;
释放未申请的内存;
使用栈内存作为函数返回值;
使用了超出作用域的栈内存;
内存越界访问;
1.AddressSanitzer是怎么做的加了ASAN相关的编译选项后,代码中的每一次内存访问操作都会被编译器修改为如下方式:
编译后:
程序中跑Valgrind其会极大的降低程序运行速度,大约降低10倍,而跑AddressSanitizer大约只降低2倍!与valgrind相比asan消耗非常低,甚至可以直接在生产环境中启用asan排查跟踪内存问题。
2.AddressSanitizer安装与配置AddressSanitizer在后被默认加入,成为gcc的一部分,但不支持符号信息,无法显示出问题的函数和行数。从4.9开始,gcc支持AddressSanitizer的所有功能。因此以上版本使用ASAN时不需要安装第三方库,通过在编译时指定编译CFLAGS即可打开开关。
如果使用AddressSanitizer错则需要先安装,Ubuntu安装命令为:
sudoapt-getinstalllibasan0
如果要使用命令行(gcc/g++),可以使用
-fsanitize=address:开启内存越界检测
-fsanitize-recover=address:一般后台程序为保证稳定性,不能遇到错误就简单退出,而是继续运行,采用该选项支持内存出错之后程序继续运行,需要叠加设置ASAN_OPTIONS=halt_on_error=0才会生效;若未设置此选项,则内存出错即报错退出
-fno-stack-protector:去使能栈溢出保护
-fno-omit-frame-pointer:去使能栈溢出保护
-g1:表示最小调试信息,通常debug版本用-g即-g2
如果要在CmakeList,则可以调用以下命令target_link_libraries:
set(CMAKE_CXX_FLAGS"${CMAKE_CXX_FLAGS}-fsanitize=address-fno-omit-frame-pointer")set(CMAKE_LINKER_FLAGS"${CMAKE_LINKER_FLAGS}-fsanitize=address")target_link_libraries(MyTargetasan)ASAN_OPTIONS还需要完成环境变量设置选项,这样当程序出问题的时候我们可以立刻查到log情况:
halt_on_error=0:检测内存错误后继续运行
detect_leaks=1:使能内存泄露检测
malloc_context_size=15:内存错误发生时,显示的调用栈层数为15
log_path=/home/:内存检查问题日志存放文件路径
exportASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/debug/
当出现问题后我们可以访问log文件,并记录了我们这个程序的错误是ERROR后面的内容detectedmemoryleaks,说明有内存泄漏。
错误类型有如下一些:
(heap)useafterfree释放后使用
heapbufferoverflow堆缓存访问溢出
stackbufferoverflow栈缓存访问溢出
globalbufferoverflow全局缓冲访问溢出
useafterreturn
useafterscope
initializationsorderbugs
memoryleaks内存泄露