首页 > 系统相关 >内存问题难定位,那是因为你没用ASAN

内存问题难定位,那是因为你没用ASAN

时间:2023-04-19 18:33:15浏览次数:54  
标签:int 没用 内存 asan ASAN array argc

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) 内,就可以知道访问出了些问题。

标签:int,没用,内存,asan,ASAN,array,argc
From: https://www.cnblogs.com/lidabo/p/17334266.html

相关文章

  • 使用mprotect定位踩内存故障
    前言对于C语言来说,内存被踩是比较常见的问题,轻则普通变量被改写程序逻辑出错,重则指针变量被改写引发指针解引用出现未定义行为风险;定位内存被踩一直是棘手的难题,如果出现程序跑死,一般可以通过堆栈信息来定位:1)查看跑死的调用链,确定跑死代码的位置;2)根据pc指针找到具体代码;3)走......
  • GE反射内存实时通讯网络解决方案
    时通讯网络是用于需要较高实时性要求的应用领域的专用网络通讯技术,一般采用基于高速网络的共享存储器技术实现。它除了具有严格的传输确定性和可预测性外,还具有速度高、通信协议简单、宿主机负载轻、软硬件平台适应性强、可靠的传输纠错能力、支持中断信号的传输等特点。本方案选......
  • Linux内存管理之mem_map对象.md
    在linux内核中,所有的物理内存都用structpage结构来描述,这些对象以数组形式存放,而这个数组的地址就是mem_map。内核以节点node为单位,每个node下的物理内存统一管理,也就是说在表示内存node的描述类型structpglist_data中,有node_mem_map这个成员,其针对平坦型内存进行描述(CONFIG_FL......
  • CANN开发实践:4个DVPP内存问题的典型案例解读
    摘要:由于DVPP媒体数据处理功能对存放输入、输出数据的内存有更高的要求(例如,内存首地址128字节对齐),因此需调用专用的内存申请接口,那么本期就分享几个关于DVPP内存问题的典型案例,并给出原因分析及解决方法。本文分享自华为云社区《FAQ_DVPP内存问题案例》,作者:昇腾CANN。DVPP是昇腾......
  • 实时查看Docker容器占用的CPU、内存状态
    安装Linux下安装方法:wgethttps://github.com/bcicen/ctop/releases/download/v0.5/ctop-0.5-linux-amd64-Octopsudomvctop/usr/local/bin/sudochmod+x/usr/local/bin/ctop执行命令:ctop使用ctop运行后,通过下面的按键可以实现不同的功能1)a-只查看运行状态的容器f-......
  • 在Go语言中,如何优化内存使用效率?
    在Go语言中,可以通过以下几种方式来优化内存使用效率:避免使用过多的内存尽可能地避免使用过多的内存是最有效的内存优化方法之一。在编写代码时,应该尽可能地避免使用全局变量和大量的临时变量。同时,可以使用常量、静态变量和缓存等方式来避免频繁地分配和释放内存。及时释放不......
  • 【valgrind】软件调试工具-valgrind内存调试工具
    valgrind工具安装Ubuntu环境安装sudoaptinstallvalgrind源码编译1.源码下载http://valgrind.org/downloads/valgrind-3.12.0.tar.bz22.valgrind编译安装tar-jxvfvalgrind-3.12.0.tar.bz2cdvalgrind-3.12.0./configuremakesudomakeinstallvalgrind运行分析程......
  • SQL Server占用内存不释放卡死问题
      最近项目中发现使用SQLServer的机器会出现10天左右占满内存卡死情况,百度后发现对应的原因如下:    即:SQLServer内存管理是分配了最大内存是多少,就会使用多少,在再次使用的时候,才会释放掉空闲的内存,它不会主动全部释放掉所有空闲内存。所以解决方式是:在sqlSe......
  • 内存屏障--- asm volatile("" ::: "memory")
    转载:(14条消息)内存屏障---asmvolatile("":::"memory")_"asm(:::\"memory\")"_咕噜咕噜斯基的博客-CSDN博客CompilermemorybarrierThesebarrierspreventacompilerfromreorderinginstructions,theydonotpreventreorderingbyCPU.T......
  • VM虚拟化学习四——虚拟机CPU和内存动态扩容
    1.CPU动态扩容  1.1创建虚拟机配置CPU数 [root@linux-node4~]#virt-install--help|grepcpu    #创建虚拟机的时候可以配置CPU --vcpusVCPUS    Numberofvcpustoconfigureforyourguest.Ex: --vcpus5          ......