valgrind 内存泄漏_应用 AddressSanitizer 发现程序内存错误

f9878d2a3e9fa8f534a48da86cd4f143.png

应用 AddressSanitizer 发现程序内存错误

7865a36ee8e8ad07df0437d1ded3c269.png

作为 C/ C++ 工程师,在开发过程中会遇到各类问题,最常见便是内存使用问题,比如,越界泄漏。过去常用的工具是 Valgrind,但使用 Valgrind 最大问题是它会极大地降低程序运行的速度,初步估计会降低 10 倍运行速度。而 Google 开发的 AddressSanitizer 这个工具很好地解决了 Valgrind 带来性能损失问题,它非常快,只拖慢程序 2 倍速度。

AddressSanitizer 概述

AddressSanitizer 是一个基于编译器的测试工具,可在运行时检测 C/C++ 代码中的多种内存错误。严格上来说,AddressSanitizer 是一个编译器插件,它分为两个模块,一个是编译器的 instrumentation 模块,一个是用来替换 malloc/free 的动态库。

Instrumentation 主要是针对在 llvm 编译器级别对访问内存的操作(store,load,alloc等),将它们进行处理。动态库主要提供一些运行时的复杂的功能(比如 poison/unpoison shadow memory)以及将 malloc/free 等系统调用函数 hook 住。

AddressSanitizer 基本使用

根据 AddressSanitizer Wiki 可以检测下面这些内存错误 - Use after free:访问堆上已经被释放的内存 - Heap buffer overflow:堆上缓冲区访问溢出 - Stack buffer overflow:栈上缓冲区访问溢出 - Global buffer overflow:全局缓冲区访问溢出 - Use after return:访问栈上已被释放的内存 - Use after scope:栈对象使用超过定义范围 - Initialization order bugs:初始化命令错误 - Memory leaks:内存泄漏

这里我只简单地介绍下基本的使用,详细的使用文档可以看官方的编译器使用文档,比如 Clang 的文档:https://clang.llvm.org/docs/AddressSanitizer.html

Use after free 实践例子

下面这段代码是一个很简单的 Use after free 的例子:

//use_after_free.cpp
#include <iostream>
int main(int argc, char **argv) {int *array = new int[100];delete [] array;std::cout << array[0] << std::endl;return 1;
}

编译代码,并且运行,这里可以看到只需要在编译的时候带上 -fsanitize=address 选项就可以了。

clang++  -O -g -fsanitize=address ./use_after_free.cpp
./a.out

最终我们会看到如下的输出:

==10960==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000040 at pc 0x00010d471df0 bp 0x7ffee278e6b0 sp 0x7ffee278e6a8
READ of size 4 at 0x614000000040 thread T0#0 0x10d471def in main use_after_free.cpp:6#1 0x7fff732c17fc in start (libdyld.dylib:x86_64+0x1a7fc)0x614000000040 is located 0 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)
freed by thread T0 here:#0 0x10d4ccced in wrap__ZdaPv (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x51ced)#1 0x10d471ca1 in main use_after_free.cpp:5#2 0x7fff732c17fc in start (libdyld.dylib:x86_64+0x1a7fc)previously allocated by thread T0 here:#0 0x10d4cc8dd in wrap__Znam (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x518dd)#1 0x10d471c96 in main use_after_free.cpp:4#2 0x7fff732c17fc in start (libdyld.dylib:x86_64+0x1a7fc)SUMMARY: AddressSanitizer: heap-use-after-free use_after_free.cpp:6 in main

可以看到一目了然,非常清楚的告诉了我们在哪一行内存被释放,而又在哪一行内存再次被使用。

还有一个是内存泄漏,比如下面的代码,显然 p 所指的内存没有被释放。

void *p;int main() {p = malloc(7);p = 0; // The memory is leaked here.return 0;
}

编译然后运行

clang -fsanitize=address -g  ./leak.c
./a.out

可以看到如下的结果

=================================================================
==17756==ERROR: LeakSanitizer: detected memory leaksDirect leak of 7 byte(s) in 1 object(s) allocated from:#0 0x4ffc80 in malloc (/home/simon.liu/workspace/a.out+0x4ffc80)#1 0x534ab8 in main /home/simon.liu/workspace/./leak.c:4:8#2 0x7f127c42af42 in __libc_start_main (/usr/lib64/libc.so.6+0x23f42)SUMMARY: AddressSanitizer: 7 byte(s) leaked in 1 allocation(s).

不过这里要注意内存泄漏的检测只会在程序最后退出之前进行检测,也就是说如果你在运行时如果不断地分配内存,然后在退出的时候对内存进行释放,AddressSanitizer 将不会检测到内存泄漏,这种时候可能你就需要另外的工具了 JeMalloc / TCMalloc。

AddressSanitizer 基本原理

这里简单介绍一下 AddressSanitizer 的实现,更详细的算法实现可以看《AddressSanitizer: a fast address sanity checker》:https://www.usenix.org/system/files/conference/atc12/atc12-final39.pdf

AddressSanitizer 会替换你的所有 malloc 以及 free,然后已经被分配(malloc)的内存区域的前后会被标记为 poisoned (主要是为了处理 overflow 这种情况),而释放(free)的内存会被标记为 poisoned(主要是为了处理 Use after free)。你的代码中的每一次的内存存取都会被编译器做类似下面的翻译.

before:

*address = ...;  // or: ... = *address;

after:

shadow_address = MemToShadow(address);
if (ShadowIsPoisoned(shadow_address)) {ReportError(address, kAccessSize, kIsWrite);
}
*address = ...;  // or: ... = *address;

这里可以看到首先会对内存地址有一个翻译(MemToShadow)的过程,然后再来判断当所访问的内存区域是否为 poisoned,如果是则直接报错并退出。

这里之所以会有这个翻译是因为 AddressSanitizer 将虚拟内存分为了两部分:

  1. Main application memory(Mem)也就是被当前程序自身使用的内存
  2. Shadow memory 简单来说就是保存了主存元信息的一块内存,比如主存的那些区域被 posioned 都是在 Shadow memory 中保存的

AddressSanitizer 和其他内存检测工具对比

下图是 AddressSanitizer 与其他的一些内存检测工具的对比:

c8e79a23e5a181d9325c251ab9e100be.png

参数说明:

  • DBI: dynamic binary instrumentation(动态二进制插桩)
  • CTI: compile-time instrumentation (编译时插桩)
  • UMR: uninitialized memory reads (读取未初始化的内存)
  • UAF: use-after-free (aka dangling pointer) (使用释放后的内存)
  • UAR: use-after-return (使用返回后的值)
  • OOB: out-of-bounds (溢出)
  • x86: includes 32- and 64-bit.

可以看到相比于 Valgrind,AddressSanitizer 只会拖慢程序 2 倍运行速度。当前 AddressSanitizer 支持 GCC 以及 Clang,其中 GCC 是从 4.8 开始支持,而 Clang 的话是从 3.1 开始支持。

AddressSanitizer 的使用注意事项

  1. AddressSanitizer 在发现内存访问违规时,应用程序并不会自动崩溃。这是由于在使用模糊测试工具时,它们通常都是通过检查返回码来检测这种错误。当然,我们也可以在模糊测试进行之前通过将环境变量 ASAN_OPTIONS 修改成如下形式来迫使软件崩溃:
export ASAN_OPTIONS='abort_on_error=1'/
  1. AddressSanitizer 需要相当大的虚拟内存(大约 20 TB),不用担心,这个只是虚拟内存,你仍可以使用你的应用程序。但像 american fuzzy lop 这样的模糊测试工具就会对模糊化的软件使用内存进行限制,不过你仍可以通过禁用内存限制来解决该问题。唯一需要注意的就是,这会带来一些风险:测试样本可能会导致应用程序分配大量的内存进而导致系统不稳定或者其他应用程序崩溃。因此在进行一些重要的模糊测试时,不要去尝试在同一个系统上禁用内存限制。

在 Nebula Graph 中开启 AddressSanitizer

我们在 Nebula Graph 中也使用了 AddressSanitizer,它帮助我们发现了非常多的问题。而在 Nebula Graph 中开启 AddressSanitizer 很简单,只需要在 Cmake 的时候带上打开 ENABLE_ASAN 这个 Option 就可以,比如:

Cmake -DENABLE_ASAN=On

这里建议所有的开发者在开发完毕功能运行单元测试的时候都打开 AddressSanitizer 来运行单元测试,这样可以发现很多不容易发现的内存问题,节省很多调试的时间。

附录

  • Nebula Graph:一个开源的分布式图数据库
  • GitHub:https://github.com/vesoft-inc/nebula
  • 官方博客:https://nebula-graph.io/cn/posts/
  • 微博:weibo.com/nebulagraph

推荐阅读

  • Jepsen 测试框架在图数据库 Nebula Graph 中的实践

c5480c6dbdbbea337a5c4b1e35f84bad.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/527566.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置文件存int类型_Redis详解(五)------ redis的五大数据类型实现原理

> 本系列教程持续更新&#xff0c;可以微信搜索「 IT可乐 」第一时间阅读。回复《电子书》有我为大家特别筛选的书籍资料前面两篇博客&#xff0c;第一篇介绍了五大数据类型的基本用法&#xff0c;第二篇介绍了Redis底层的六种数据结构。在Redis中&#xff0c;并没有直接使用…

java list 占用内存不释放_性能不打折,内存占用减少90%,Facebook提出极致模型压缩方法Quant-Noise...

对于动辄上百 M 大小的神经网络来说&#xff0c;模型压缩能够减少它们的内存占用、通信带宽和计算复杂度等&#xff0c;以便更好地进行应用部署。最近&#xff0c;来自 Facebook AI 的研究者提出了一种新的模型量化压缩技术 Quant-Noise&#xff0c;让神经网络在不降低性能的前…

苹果系统安装python环境_如何在mac下配置python虚拟环境

安装python虚拟环境核心目的就是为了复制一个python环境,这样新项目下载的所有包,都会存放在虚拟环境下的python site-package 中 首先安装 1. sudo pip install virtualenv # 安装虚拟环境 2. sudo pip install virtualenvwrapper # 安装虚拟环境扩展包 3. vim .bash_profile …

linux程序无法获取cpu资源,Linux系统编程获取系统的CPU资源

还是纯干货&#xff0c;代码拿来&#xff1a;#include #include #include #include #include #include #include void work(){FILE *f;int i;double x 4.5;f tmpfile();for(i 0; i < 10000;i){fprintf(f,"Do some output\n");if (ferror(f)){fprintf(stderr,&q…

dbnetlib sqlserver不存在或拒绝访问_404:对不起,您访问的网页不存在

404&#xff1a;对不起您访问的网页不存在https://www.zhihu.com/video/1101123296195723264你可能在上网时也好奇过&#xff0c;为什么点开一个无法显示信息的页面&#xff0c;上面会出现数字404&#xff1f;要解释这个问题&#xff0c;我们需要从互联网诞生之初说起。20世纪8…

linux中控和安卓中控哪个比较好,原厂中控和改装中控有什么区别?哪个更好?...

很多人都喜欢车辆上有一个大屏的中控。那种大屏的中控不仅功能很多&#xff0c;而且还很实用。但是有很多车需要高配才能有中控。而且还需要加钱。很多人在买车的时候都会选择一些低配的车&#xff0c;然后自己去安装中控。有些人认为原厂的中控不仅价格贵&#xff0c;而且还有…

bufferedreader读取中文乱码_python之pandas模块关于csv文件乱码问题解决

介绍相信部分小伙伴们在处理windows系统生成的csv文件时会遇到中文显示乱码的问题&#xff0c;尤其是使用Excel打开这类文件时这类问题尤为突出。解决如图&#xff0c;我们通过Excel工具打开该csv文件时&#xff0c;中文展示为乱码。此时&#xff0c;只需要我们通过功能强大的数…

linux mint python3.6,Linux 上如何安装并切换最新版本的 Python 3.6

原标题&#xff1a;Linux 上如何安装并切换最新版本的 Python 3.6如果你安装了 Linux 系统&#xff0c;正在学习 Python 并想要使用最新的版本的话&#xff0c;那么这篇文章就是为你而写的。-- ShekinPython是 Linux 中一种最流行的编程语言。它被写成了各种工具和库。除此之外…

centos 卸载软件_Linux服务器运维必备技能 软件包和启动项超详细整理

一、RedHat/CentOS包管理1. 包管理工具RPM(1) 简介最早用在 RedHat的包管理器&#xff0c;现在已经成为Linux常见的包管理系统。RPM包有两种类型&#xff1a;二进制RPM包源码RPM包可以在www.rpmfind.net网站查询软件包&#xff0c;也可以查到其依赖项&#xff1a;(2) RPM包命名…

linux内存分配器类型,内核早期内存分配器:memblock

原标题&#xff1a;内核早期内存分配器&#xff1a;memblock本文转载自Linux爱好者本文来自 程雪涛的自荐投稿Linux内核使用伙伴系统管理内存&#xff0c;那么在伙伴系统工作前&#xff0c;如何管理内存&#xff1f;答案是memblock。memblock在系统启动阶段进行简单的内存管理&…

java substring截取字符串_java基础教程之字符串的介绍,比较重要的一个知识点【下】...

字符串操作1、获取子字符串通过String类的substring()方法可对字符串进行截取。这些方法的共同点就是都是利用字符串的下标进行截取。应明确字符串下标是从0开始的。substring()方法被两种不同的方法重载&#xff0c;来满足不同的需要。(1)substring(intbeginIndex)该方法返回的…

在linux环境下安装wiringpi库,wiringPi库的pwm配置及使用说明

本文介绍树莓派(raspberry pi)在linux c 环境下的硬件pwm配置及使用方法。1. 下载安装wiringPi此步骤建议参考官网指南&#xff0c;wiringPi提供了对树莓派的硬件IO访问&#xff0c;包括GPIO/I2C/PWM等&#xff0c;下载安装后本地会出现wiringPi文件夹&#xff0c;根目录下有/e…

excel split函数_Excel 字符串拆分

用 Excel 处理数据时&#xff0c;有时需要对字符串进行拆分。对于比较简单的拆分&#xff0c;使用 Excel 函数可以顺利完成&#xff0c;但碰到一些特殊需求&#xff0c;或者拆分的规则比较复杂时&#xff0c;则很难用 Excel 实现了。这里列出一些拆分需求示例&#xff0c;分析拆…

python优雅编程_Python优雅地可视化数据

[导读]声明&#xff1a;由于本文的代码大部分是参考书中的例子&#xff0c;所以不提供完整代码&#xff0c;只提供示例片段&#xff0c;也就是只能看出某一部分用法&#xff0c;感兴趣的需要在自己的数据上学习测试。 声明&#xff1a;由于本文的代码大部分是参考书中的例子&am…

sparkstreaming 读取mysql_第十篇|SparkStreaming手动维护Kafka Offset的几种方式

Spark Streaming No Receivers 方式的createDirectStream 方法不使用接收器&#xff0c;而是创建输入流直接从Kafka 集群节点拉取消息。输入流保证每个消息从Kafka 集群拉取以后只完全转换一次&#xff0c;保证语义一致性。但是当作业发生故障或重启时&#xff0c;要保障从当前…

anaconda 安装pytorch_conda上安装PyTorch

conda上安装PyTorch这里的安装系统&#xff1a;Windows系统第一步&#xff0c;打开Anaconda Prompt第二步&#xff0c;为pytorch创建一个环境&#xff1a;conda create -n pytorch python3.8&#xff08;这里的pytorch是环境的名称&#xff0c;python3.8表示安装的是3.8版本的p…

curd什么意思中文_查英英字典:What a shame是什么意思?

查英英字典&#xff1a;What a shame是什么意思&#xff1f;“互联网”时代学习英语有两种“学习方法”&#xff1a;简单粗暴学法和自己动手丰衣足食法。一、简单粗暴法&#xff1a;直接问老师要“中文答案”在过去非互联网时代&#xff0c;“老师”往往是提供“答案”的唯一渠…

c++读出像素矩阵_Python传numpy矩阵调c++(求3D图像连通区域)

Python有很多种调c的方法&#xff0c;有的复杂有的简单&#xff0c;有时使用的时候反而不知道到底该用哪一种比较好&#xff0c;其实没有最好的方法&#xff0c;只有适合不适合自己。本文从我所遇到的问题说起&#xff0c;然后讲述另一种比较简单的python调c并且传参numpy矩阵的…

android四大组件的作用简书,Android四大组件是什么

Android四大组件是&#xff1a;活动、服务、广播接收器、内容提供商。它们的英文名称是ACTIVITY、SERVICE、BroadcastReceiver、Content Provider。四个组件分别起到不同的作用&#xff0c;相互配合才能确保安卓系统的正常运行&#xff0c;因此是缺一不可的。Android四大组件及…

python 获取文件大小_第41p,超级重要,Python中的os库

大家好&#xff0c;我是杨数Tos&#xff0c;这是《从零基础到大神》系列课程的第41篇文章&#xff0c;第二阶段的课程&#xff1a;Python基础知识&#xff1a;Python内置库之os库的使用。学习本课程&#xff0c;建议先看一遍&#xff1a;【计算机基础知识】课程。os模块是与操作…