当前位置:网站首页>内存定位利器-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/log目录下,形成一个err.log.212的文件,212是执行./a.out的进程号。文件记录了详细的错误信息。


5. ASAN的原理是什么
ASAN要记录每一块内存的可用性. 把用户程序所在的内存区域叫做主内存, 而记录主内存可用性的内存区域, 则叫做影子内存 (Shadow memory).
所有主内存的分配都按照 8 字节的方式对齐. 然后按照 1:8 的压缩比例对主内存的可用性进行记录, 然后存入影子内存中. 影子内存无法被用户直接读写, 需要编译器生成相关的代码来访问.
每一次内存的分配和释放, 都会写入影子内存. 每次读/写内存区域前, 都会读取一下影子内存, 获得这块内存访问合法性 (是否被分配, 是否已被释放).
对影子内存的写入只在分配内存的时候发生, 所以只要分配内存是多线程安全的, ASan 就是多线程安全的, 这在大部分情况下也确实成立.
计算影子内存的地址需要快速, 他们采用了: 主内存地址除以 8, 再加上一个偏移量的做法. 因为堆栈分别在虚拟内存地址空间的两端, 这样影子内存就会落在中间. 而如果用户以外访问了影子内存, 那么影子内存的"影子内存"就会落到一个非法的范围 (Shadow Gap) 内, 就可以知道访问出了些问题.
边栏推荐
猜你喜欢

Hit the interview!The latest interview booklet of Ali Jin, nine silver and ten is stable!

【黑马早报】尚乘数科上市13天,市值超阿里;北大终止陈春花聘用合同;新东方花近200亿退学费和遣散费;张小泉75%产品贴牌代工...

七夕疯狂搞钱的年轻人,一周赚14万

炫酷又高效的数据可视化大屏,做起来真的没那么难!丨极客星球

新消费、出海、大健康......电子烟寻找“避风港”

Chinese valentine's day of young people crazy to make money, earn 140000 a week

Focusing on data sources, data quality and model performance to build a credit profile of small and micro enterprises

Geoffrey Hinton:深度学习的下一个大事件

【解决方案 三十一】Navicat数据库结构同步

【PHP实现微信公众平台开发—基础篇】第1章 课程介绍
随机推荐
“蔚来杯“2022牛客暑期多校训练营5 B、C、F、G、H、K
从零开始配置 vim(6)——缩写
【解决方案 三十一】Navicat数据库结构同步
1314元的七夕礼盒,收割了多少直男?
How to develop small program plug-ins to achieve profitability?
获取本机IP地址的脚本
Cache character stream
双目立体视觉笔记(二)
leetcode 48. Rotate Image 旋转图像(Medium)
MySQL-数据类型
Arduino框架下I2S控制ADC采样以及PWM输出示例解析
他是“中台”之父,凭一个概念为阿里狂赚百亿
Hit the interview!The latest interview booklet of Ali Jin, nine silver and ten is stable!
跨链桥已成行业最大安全隐患 为什么和怎么办
What is DevOps?Enough to read this one!
一分钟认识 IndexedDB 数据库,太强大了!
Why is Luo Zhenyu's A-share dream so difficult to fulfill?
【VSCode】一文详解vscode下安装vim后无法使用Ctrl+CV复制粘贴 使用Vim插件的配置记录
Systemui qsSetting添加新图标
MFC的相机双目标定界面设计
