当前位置:网站首页>内存定位利器-ASAN使用小结
内存定位利器-ASAN使用小结
2022-08-04 12:50:00 【华为云】
1.什么是ASAN
ASAN全称:Address Sanitizer,google发明的一种内存地址错误检查器。目前已经被集成到各大编译器中。
2.为什么我们需要ASAN
在c/c++开发过程中,经常出现内存异常使用的问题,比如踩内存,被踩的内存如果未被使用对外无影响。而一旦使用了被踩的内存,可能会出现进程core,死循环,进入异常分支等等各种千奇百怪的问题。这个时候要去定位这段内存为什么被踩,相当困难,因为已经错过了案发现场。如果不幸,遇到了这种问题,常用手段是:
1)分析被踩内存的特征值,比如是否是一个magic值,然后从代码库中找特征值,分析代码,缩小排查方向。
2)找到必现条件,通过gdb的watch功能,watch被踩的内存地址,一旦被踩,gdb将会打出踩内存的堆栈。
根据作者的经验,出现踩内存的问题需要消耗大量的人力定位。少则一人周,多种数人月。而这类问题,往往是由于某个低级编码错误引起的。
所以,我们迫切的希望,能在踩内存的第一现场就把凶手抓住,而不是在破坏已经表现出来的时候再去分析定位。而asan就能达到这个目的,它会接管内存的申请和释放,每次的内存的读写都会检查,因此可以做到快速的定位踩内存的问题。在asan之前也有其他的内存分析工具,但是asan是这些工具中比较优秀的,并不会损失大量的性能和内存(官方数据,性能下降两倍,而valgrind下降20倍:https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools)。
3.ASAN可以定位哪些内存使用问题
- Heap OOB(HeapOutOfBounds 堆内存越界)
int main(int argc, char **argv) { int *array = new int[100]; array[0] = 0; int res = array[argc + 100]; // BOOM delete [] array; return res;}
- Stack OOB(StackOutOfBounds 栈越界)
int main(int argc, char **argv) { int stack_array[100]; stack_array[1] = 0; return stack_array[argc + 100]; // BOOM}
- Global OOB(GlobalOutOfBounds 全局变量越界)
int global_array[100] = {-1};int main(int argc, char **argv) { return global_array[argc + 100]; // BOOM}
- UAF(UseAfterFree 内存释放后使用)
int main(int argc, char **argv) { int *array = new int[100]; delete [] array; return array[argc]; // BOOM}
- UAR(UseAfterReturn 栈内存回收后使用,该功能还存在少量bug,默认未开启,开启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];}
UMR(uninitialized memory reads读取未初始化内存)
Leaks(内存泄露)
4.怎么使用ASAN工具
现在大部分编译器已经集成了支持asan的能力,编译的时候加上编译选项即可。
常见的编译选项:
- -fsanitize=address 开起asan能力,gcc 4.8版本开启支持。
- -fsanitize-recover=address :asan检查到错误后,不core继续运行,需要配合环境变量ASAN_OPTIONS=halt_on_error=0:report_path=xxx使用。gcc 6版本开始支持。
本文使用的是华为 EulerOS v2r9 版本。
下面开始我们的asan之旅
- 写个bug,写一个释放后的内存还在使用的例子。
#include <stdlib.h>int main(){ int *p = malloc(sizeof(int)*10); free(p); *p = 3;//该程序正常情况下并不会导致进程core,因为free后的内存被glibc的内存分配器缓存着 return 0;}
加上编译选项编译:
gcc -fsanitize=address -g ./test.c -lasan -L /root/buildbox/gcc-10.2.0/lib64/
其中-L
指定的是libasan.so
存放的位置。指定asan的so的目录,
export LD_LIBRARY_PATH=/root/buildbox/gcc-10.2.0/lib64/
,执行./a.out
执行程序,将可以看到asan报错。指出了内存异常使用的位置和原因。在工程中,我们更希望程序遇到错误能不中断,而继续执行下去,我们可以使用
-fsanitize-recover=address
方法。这次我们更改下代码,多引入几个错误。
#include <stdlib.h>int main(){ int *p = malloc(sizeof(int)*10); free(p); *p = 3; //错误1.释放后继续使用 p = malloc(sizeof(int)*10); p[11] = 3;//错误2,越界写 return 0;}
编译:
gcc -fsanitize=address -fsanitize-recover=address -g ./test.c -lasan -L /root/buildbox/gcc-10.2.0/lib64/
设置环境变量:
export ASAN_OPTIONS=halt_on_error=0:log_path=/var/log/err.log
,执行程序./a.out
查看日志路径:在
/var/lo
g目录下,形成一个err.log.212
的文件,212是执行./a.out
的进程号。文件记录了详细的错误信息。
5. ASAN的原理是什么
ASAN要记录每一块内存的可用性. 把用户程序所在的内存区域叫做主内存, 而记录主内存可用性的内存区域, 则叫做影子内存 (Shadow memory).
所有主内存的分配都按照 8 字节的方式对齐. 然后按照 1:8 的压缩比例对主内存的可用性进行记录, 然后存入影子内存中. 影子内存无法被用户直接读写, 需要编译器生成相关的代码来访问.
每一次内存的分配和释放, 都会写入影子内存. 每次读/写内存区域前, 都会读取一下影子内存, 获得这块内存访问合法性 (是否被分配, 是否已被释放).
对影子内存的写入只在分配内存的时候发生, 所以只要分配内存是多线程安全的, ASan 就是多线程安全的, 这在大部分情况下也确实成立.
计算影子内存的地址需要快速, 他们采用了: 主内存地址除以 8, 再加上一个偏移量的做法. 因为堆栈分别在虚拟内存地址空间的两端, 这样影子内存就会落在中间. 而如果用户以外访问了影子内存, 那么影子内存的"影子内存"就会落到一个非法的范围 (Shadow Gap) 内, 就可以知道访问出了些问题.
边栏推荐
- Valentine's Day Romantic 3D Photo Wall [with source code]
- 5 cloud security management strategies enterprises should implement
- “蔚来杯“2022牛客暑期多校训练营4 N
- 来 TDengine 开发者大会,洞悉数据技术发展的未来趋势
- FHQ-Treap 简介
- 新消费、出海、大健康......电子烟寻找“避风港”
- 【PHP实现微信公众平台开发—基础篇】第2章 微信公众账号及申请流程详解
- 动规(18)-并查集基础题——团伙
- 微信小程序使用腾讯云对象储存上传图片
- Control CD-ROM with VbScript
猜你喜欢
项目里的各种配置,你都了解吗?
新 Nsight Graph、Nsight Aftermath 版本中的性能提升和增强功能
【解决方案 三十一】Navicat数据库结构同步
《独行月球》猛药,治不了开心麻花内耗
MySQL - Explain explanation
"Lonely Walking on the Moon" is a powerful medicine, it can't cure the internal friction of happy twist
【VSCode】一文详解vscode下安装vim后无法使用Ctrl+CV复制粘贴 使用Vim插件的配置记录
做项目管理有且有必要了解并学习的重要知识--PMP项目管理
Valentine's Day Romantic 3D Photo Wall [with source code]
如何治理资源浪费?百度云原生成本优化最佳实践
随机推荐
新 Nsight Graph、Nsight Aftermath 版本中的性能提升和增强功能
num_workers
年轻人为什么不喜欢买蒙牛、伊利了?
RobotFramework二次开发(一)
什么是 DevOps?看这一篇就够了!
缓存中间件技术选型Memcached、MongoDB、Redis
项目里的各种配置,你都了解吗?
正确使用Impala的invalidate metadata与refresh语句
备份控制文件
缓存字符流
ShanDong Multi-University Training #4 A、B、C、G
RK1126编译gdb 板子上gdb调试程序
oracle sql中根据条件判断是否插入数据
【水一个徽章】
Two years of independent development experience Programmers tell us the experience of making money (listen to the masters who really make money)
使用SQLServer复制数据库
《独行月球》猛药,治不了开心麻花内耗
String是引用类型
全面认识MOS管,一篇文章就够了
如何治理资源浪费?百度云原生成本优化最佳实践