摘要:ASAN全称:Address Sanitizer,google发明的一种内存地址错误检查器。目前已经被集成到各大编译器中。

本文分享自华为云社区《内存定位利器-ASAN使用小结》,作者:云存储开发者支持团队。

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可以定位哪些内存使用问题

1、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;
}

2、Stack OOB(StackOutOfBounds 栈越界)

int main(int argc, char **argv) {
int stack_array[100];
stack_array[1] = 0;
return stack_array[argc + 100]; // BOOM
}

3、Global OOB(GlobalOutOfBounds 全局变量越界)

int global_array[100] = {-1};
int main(int argc, char **argv) {
return global_array[argc + 100]; // BOOM
}

4、UAF(UseAfterFree 内存释放后使用)

int main(int argc, char **argv) {
int *array = new int[100];
delete [] array;
return array[argc]; // BOOM
}

5、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];
}

6、UMR(uninitialized memory reads读取未初始化内存)

7、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之旅

1、写个bug,写一个释放后的内存还在使用的例子。

#include <stdlib.h>
int main()
{
int *p = malloc(sizeof(int)*10);
free(p);
*p = 3;//该程序正常情况下并不会导致进程core,因为free后的内存被glibc的内存分配器缓存着
return 0;
}

2、加上编译选项编译:gcc -fsanitize=address -g ./test.c -lasan -L /root/buildbox/gcc-10.2.0/lib64/ 其中-L指定的是libasan.so存放的位置。

3、指定asan的so的目录,export LD_LIBRARY_PATH=/root/buildbox/gcc-10.2.0/lib64/,执行./a.out执行程序,将可以看到asan报错。指出了内存异常使用的位置和原因。

4、在工程中,我们更希望程序遇到错误能不中断,而继续执行下去,我们可以使用 -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;
}

5、编译:gcc -fsanitize=address -fsanitize-recover=address -g ./test.c -lasan -L /root/buildbox/gcc-10.2.0/lib64/

6、设置环境变量:export ASAN_OPTIONS=halt_on_error=0:log_path=/var/log/err.log,执行程序./a.out

7、查看日志路径:在/var/log目录下,形成一个err.log.212的文件,212是执行./a.out的进程号。文件记录了详细的错误信息。

5. ASAN的原理是什么

ASAN要记录每一块内存的可用性。把用户程序所在的内存区域叫做主内存, 而记录主内存可用性的内存区域,则叫做影子内存 (Shadow memory)。

所有主内存的分配都按照 8 字节的方式对齐。然后按照 1:8 的压缩比例对主内存的可用性进行记录,然后存入影子内存中。影子内存无法被用户直接读写, 需要编译器生成相关的代码来访问。

每一次内存的分配和释放, 都会写入影子内存。每次读/写内存区域前, 都会读取一下影子内存, 获得这块内存访问合法性 (是否被分配, 是否已被释放)。

对影子内存的写入只在分配内存的时候发生, 所以只要分配内存是多线程安全的, ASan 就是多线程安全的, 这在大部分情况下也确实成立。

计算影子内存的地址需要快速,他们采用了: 主内存地址除以 8,再加上一个偏移量的做法. 因为堆栈分别在虚拟内存地址空间的两端,这样影子内存就会落在中间。而如果用户以外访问了影子内存,那么影子内存的"影子内存"就会落到一个非法的范围 (Shadow Gap) 内,就可以知道访问出了些问题。

点击关注,第一时间了解华为云新鲜技术~

内存问题难定位,那是因为你没用ASAN的更多相关文章

  1. 记一次《C语言踩内存》问题定位有感

    踩内存问题,个人认为算是比较容易出现但是有很难定位的问题,被踩者轻者功能瘫痪,重者一命呜呼,直接诱发死机.产生踩内存的的原因也比较多样,比较典型的有如下几种: 数组越界访问 字符串越界操作 直接操作野 ...

  2. 线上服务内存OOM问题定位[转自58沈剑]

    相信大家都有感触,线上服务内存OOM的问题,是最难定位的问题,不过归根结底,最常见的原因: 本身资源不够 申请的太多 资源耗尽 58到家架构部,运维部,58速运技术部联合进行了一次线上服务内存OOM问 ...

  3. 线上服务内存OOM问题定位

    转自:架构师之路,http://mp.weixin.qq.com/s/iOC1fiKDItn3QY5abWIelg 相信大家都有感触,线上服务内存OOM的问题,是最难定位的问题,不过归根结底,最常见的 ...

  4. 线上服务内存OOM问题定位三板斧

    相信大家都有感触,线上服务内存OOM的问题,是最难定位的问题,不过归根结底,最常见的原因: 本身资源不够 申请的太多 资源耗尽 58到家架构部,运维部,58速运技术部联合进行了一次线上服务内存OOM问 ...

  5. MAT内存问题分析定位

    MAT内存问题分析定位 1.下载安装MemoryAnalyzer工具. 2.使用DDMS将对应线程的内存日志导出来后,使用hprof-conv工具进行转换,用MAT打开转换后的hprof文件.

  6. java内存泄漏的定位与分析

    1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题. 编写java程序最为方便的地方就是我们不需要管理内存的分配和释放, ...

  7. (转)java内存泄漏的定位与分析

    转自:http://blog.csdn.net/x_i_y_u_e/article/details/51137492 1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测, ...

  8. Android应用内存泄漏的定位、分析与解决策略

    什么是内存泄漏 对于不同的语言平台来说,进行标记回收内存的算法是不一样的,像 Android(Java)则采用 GC-Root 的标记回收算法.下面这张图就展示了 Android 内存的回收管理策略( ...

  9. MFC关于VS内存释放的定位

    全部在App中完成 1.在 App.h 头文件声明 #ifdef _DEBUGprotected:      CMemoryState m_msOld, m_msNew, m_msDiff;#endi ...

随机推荐

  1. Docker部署mysql 5.7

    Docker部署mysql 5.7 准备工作 在CentOS或者Linux创建部署目录,用于存放容器的配置和MySQL数据:目的是当重装或者升级容器时,配置文件和数据不会丢失.执行以下命令: a.创建 ...

  2. 【动态UAC权限】无盾程序(win32&cmd)

    可以看到两种不同的提权方式,注意是动态,用代码提权,而不是用清单文件提前处理. 函数都写好了,这里不多做解释. win32程序: 首先需要这俩头文件,第二个我忘了啥函数要用了,总之出问题加上就对了:( ...

  3. 385. Mini Parser - LeetCode

    Question 385. Mini Parser Solution 分析:用NI(count,list)来表示NestedInteger,则解析字符串[123,[456,[789]]]过程如下: # ...

  4. 开源流程引擎该如何选择flowable还是camunda

    市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.现在国内用的最多的是activiti.flowable.camunda,下面主要从功能 ...

  5. php个性代码注释

      // _ooOoo_ // o8888888o // 88" . "88 // (| -_- |) // O\ = /O // ____/`---'\____ // . ' \ ...

  6. Spring Boot 实践 :Spring Boot + MyBatis

    Spring Boot 实践系列,Spring Boot + MyBatis . 目的 将 MyBatis 与 Spring Boot 应用程序一起使用来访问数据库. 本次使用的Library spr ...

  7. SAP Web Dynpro-版本管理

    您可以使用版本管理来管理对象的旧版本,比较版本,也可以重置它们. 在版本管理中,您可以存储ABAP开发对象的不同版本. 在ABAP工作台中,您可以比较不同版本的- 视图 视窗 控制器 您也可以存储对象 ...

  8. SAP BPC 开发日记

    1.获取维度模型的方法1 DATA:i_appset_id TYPE uj_appset_id,     i_appl_id   TYPE uj_appl_id.i_appset_id = 'SINO ...

  9. 我不就是吃点肉,应该没事吧——爬取一座城市里的烤肉店数据(附完整Python爬虫代码)

    写在前面的一点屁话: 对于肉食主义者,吃肉简直幸福感爆棚!特别是烤肉,看着一块块肉慢慢变熟,听着烤盘上"滋滋"的声响,这种期待感是任何其他食物都无法带来的.如果说甜点是" ...

  10. jenkins页面一直在Please wait while Jenkins is getting ready to work ...

    原因:因为访问官网太慢.我们只需要换一个源,不使用官网的源即可. 1.找到jenkins工作目录 find / -name *.UpdateCenter.xml 2.修改文件中的url,随后重启就行了 ...