【Linux】使用AddressSanitizer分析内存非法使用问题

文章目录

      • 1 为什么需要AddressSanitizer?
      • 2 如何使用AddressSanitizer
      • 3 AddressSanitizer的原理
      • 4 总结

1 为什么需要AddressSanitizer?

Valgrind是比较常用的内存问题定位工具,既然已经有了Valgrind,为什么还需要AddressSanitizer呢?

与Valgrind相比,AddressSanitizer存在以下优势:

  • Valgrind通过模拟CPU来检测内存错误,导致会以较慢的速度运行程序;而AddressSanitizer是在编译阶段插入检查的逻辑,执行速度比Valgrind快很多
  • Valgrind是一个独立的工具,可以使用在任何程序上;而AddressSanitizer与编译器紧密集成,可以在构建时自动启用
  • 在错误信息的展示上,AddressSanitizer提供的错误信息比Valgrind容易理解
  • AddressSanitizer作为编译器的一部分,通过编译选项启用;而Valgrind作为独立的工具,需要更多的配置和学习才能使用
  • AddressSanitizer通过编译时插桩和运行时检查来检测内存错误,误报率较低

从使用场景来说,AddressSanitizer专注于发现内存未释放访问非法内存的问题。

2 如何使用AddressSanitizer

从gcc 4.8开始,AddressSanitizer称为gcc的一部分,但是,gcc 4.8的AddressSanitizer没有符号信息,建议使用gcc 4.9及以上版本。

首先使用AddressSanitizer来检测下常见的内存问题。

用例一:未正确释放内存

#include <iostream>int main() {int *ptr = new(int);*ptr = 0;std::cout << *ptr << std::endl;
}

上述代码申请了一个int的空间,但是没有释放,编译该程序:g++ -fsanitize=address -fno-omit-frame-pointer -o main main.cpp,然后直接执行:

0=================================================================
==711759==ERROR: LeakSanitizer: detected memory leaksDirect leak of 4 byte(s) in 1 object(s) allocated from:#0 0x7f37f92201c7 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99#1 0x55581dc742be in main (/root/asan/main+0x12be)#2 0x7f37f8c54fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).

结果比Valgrind更好理解:

  • 出现了内存泄漏
  • 导致内存泄漏的内存分配的调用栈
  • 总结,在一次分配过程中泄漏了4字节

用例二:分配的内存未使用配对的函数释放

#include <iostream>int main() {int *ptr = new(int);*ptr = 0;free(ptr);
}
=================================================================
==711960==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new vs free) on 0x602000000010#0 0x7f8ef531c517 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127#1 0x5571e62742ef in main (/root/asan/main+0x12ef)#2 0x7f8ef4d52fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#3 0x7f8ef4d5307c in __libc_start_main_impl ../csu/libc-start.c:409#4 0x5571e62741c4 in _start (/root/asan/main+0x11c4)0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014)
allocated by thread T0 here:#0 0x7f8ef531e1c7 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99#1 0x5571e627429e in main (/root/asan/main+0x129e)#2 0x7f8ef4d52fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58SUMMARY: AddressSanitizer: alloc-dealloc-mismatch ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127 in __interceptor_free
==711960==HINT: if you don't care about these errors you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==711960==ABORTING

结果也可以直接看出来:

  • alloc-dealloc-mismatch(operator new vs free):分配和销毁不匹配
  • 分配和销毁的调用栈

用例三:使用已经被回收的内存

#include <iostream>int main() {int *ptr = new(int);*ptr = 0;delete(ptr);*ptr = 1;
}
=================================================================
==712018==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x560c4aac2331 bp 0x7ffd02a2d040 sp 0x7ffd02a2d030
WRITE of size 4 at 0x602000000010 thread T0#0 0x560c4aac2330 in main (/root/asan/main+0x1330)#1 0x7ff2fbc22fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#2 0x7ff2fbc2307c in __libc_start_main_impl ../csu/libc-start.c:409#3 0x560c4aac21c4 in _start (/root/asan/main+0x11c4)0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014)
freed by thread T0 here:#0 0x7ff2fc1ef22f in operator delete(void*, unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:172#1 0x560c4aac22f9 in main (/root/asan/main+0x12f9)#2 0x7ff2fbc22fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58previously allocated by thread T0 here:#0 0x7ff2fc1ee1c7 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99#1 0x560c4aac229e in main (/root/asan/main+0x129e)#2 0x7ff2fbc22fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58SUMMARY: AddressSanitizer: heap-use-after-free (/root/asan/main+0x1330) in main
Shadow bytes around the buggy address:0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[fd]fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable:           00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cbShadow gap:              cc
==712018==ABORTING

会输出heap-use-after-free类型的结果:

  • 结果包含内存分配、释放以及写已经释放的内存的调用栈
  • 在SUMMARY下面出现两块数据,这个就涉及到AddressSanitizer的实现

3 AddressSanitizer的原理

前面说过,AddressSanitizer主要的使用场景是内存未释放访问非法内存,为了能够发现这两种情况,需要为内存保存一些状态信息。

为了能够为内存保存状态信息,AddressSanitizer会用自己的运行时库替换默认的malloc/free,将需要操作内存的代码进行替换。

例如,如果是读操作:

... = *address;

则在访问内存前执行检查:

if (IsPoisoned(address)) {ReportError(address, kAccessSize, kIsWrite);
}
... = *address;

如果是写操作:

*address = ...;

则在写操作之前进行检查:

if (IsPoisoned(address)) {ReportError(address, kAccessSize, kIsWrite);
}
*address = ...;

其中的IsPoisoned()函数就是用来检查内存地址是否合法

AddressSanitizer使用影子内存的机制进行内存地址的合法性检查:对程序实际使用的内存使用额外的字节来存储它的状态信息。

使用1个字节存储实际使用的8个字节的状态:

  • 8个字节的数据可读写,则1个字节的值为0
  • 8个字节的数据不可读写,则1个字节的值为负数,不同的值表示不同类型的内存,如0xfa表示堆左边的redzone(redzone是在正常可以使用的内存两侧的边界),0xf1表示栈左边的redzone
  • 8个字节中前k个字节可读写,后8-k个字节不可读写,则1个字节的值为k

因此,IsPoisoned()函数就是检查地址对应的影子内存中的状态是否可以访问,如果不能访问,则出现访问非法内存的问题,如果访问的地址的影子内存是redzone,则说明出现内存访问越界的问题。

基于上述描述,再来看上面heap-use-after-free的输出的后面部分的内容:

Shadow bytes around the buggy address:0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[fd]fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable:           00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cbShadow gap:              cc
==712018==ABORTING

第二部分是图例,也就是影子内存中不同的值代表的含义,例如,fd表示已经被释放的内存。

第一部分显示的就是影子内存的数据,出现了fd表示已经被释放的内存,对该内存再次访问就会出现heap-use-after-free问题。

下面再看一个栈溢出的例子:

#include <iostream>
#include <string.h>int main() {int arr1[10];int arr2[12];memcpy(arr1, arr2, 12*sizeof(int));
}

结果:

=================================================================
==714984==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe915be528 at pc 0x7f22667182c3 bp 0x7ffe915be4d0 sp 0x7ffe915bdc78
WRITE of size 48 at 0x7ffe915be528 thread T0#0 0x7f22667182c2 in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827#1 0x5620a92e0349 in main (/root/asan/main+0x1349)#2 0x7f22661c8fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#3 0x7f22661c907c in __libc_start_main_impl ../csu/libc-start.c:409#4 0x5620a92e01c4 in _start (/root/asan/main+0x11c4)Address 0x7ffe915be528 is located in stack of thread T0 at offset 72 in frame#0 0x5620a92e0298 in main (/root/asan/main+0x1298)This frame has 2 object(s):[32, 72) 'arr1' (line 5)[112, 160) 'arr2' (line 6) <== Memory access at offset 72 partially underflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcpy
Shadow bytes around the buggy address:0x1000522afc50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x1000522afc60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x1000522afc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x1000522afc80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x1000522afc90: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
=>0x1000522afca0: 00 00 00 00 00[f2]f2 f2 f2 f2 00 00 00 00 00 000x1000522afcb0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 000x1000522afcc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x1000522afcd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x1000522afce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x1000522afcf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable:           00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cbShadow gap:              cc
==714984==ABORTING

结果表明,这是一个stack-buffer-overflow的错误,对于栈内存来说,也会给出分配栈空间的变量的地方以及对栈内存出现非法访问的调用栈,还分别给出了两个变量的位置。

下面的影子内存可以看出:

  • f1是栈左红区,f2是栈中红区,f3是栈右红区
  • 所有的栈空间都处于f1和f3之间,变量之间用f2隔开
  • arr1是10个int,也就是40个字节,使用5个影子字节,即影子内存中=>所在行的左侧的5个字节
  • arr2是12个int,也就是48个字节,使用6个影子字节,即影子内存中=>所在行的左侧的6个字节
  • [f2]表明访问栈中红区,出现栈的访问越界

4 总结

AddressSanitizer是进行内存异常使用分析的工具,该工具已经集成到编译器中,因此,只能用于分析C/C++语言的内存问题分析。与Valgrind相比,运行速度更快,但是,从场景来说,AddressSanitizer主要用于检测内存的非法使用,当然也包括内存未正确释放的问题,而Valgrind则可以分析出导致内存增长的调用栈。

因此:

  • 如果出现内存偏高的问题,可以使用Valgrind工具分析
  • 如果出现内存导致的core问题,可以使用gdb的watchpoint或者AddressSanitizer分析

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

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

相关文章

java 通过 microsoft graph 调用outlook(三)

这次会添加一个Reply接口&#xff0c; 并且使用6.10.0版本 直接上代码 一&#xff0c; POM <!-- office 365 --><dependency><groupId>com.microsoft.graph</groupId><artifactId>microsoft-graph</artifactId><version>6.1…

域内 dcsync 权限维持

一、原理 DCSync 是域渗透中经常会用到的技术&#xff0c;其被整合在了 Mimikatz 中。在 DCSync 功能出现之前&#xff0c;要想获得域用户的哈希&#xff0c;需要登录域控制器&#xff0c;在域控制器上执行代码才能获得域用户的哈希。 Mimikatz的DCSync 功能&#xff1a; 该…

java8总结

java8总结 java8新特性总结1. 行为参数化2. lambda表达式2.1 函数式接口2.2 函数描述符 3. Stream API3.1 付诸实践 java8新特性总结 行为参数化lambda表达式Stream Api 1. 行为参数化 定义&#xff1a;行为参数化&#xff0c;就是一个方法接受多个不同的行为作为参数&#x…

harmony 文件上传

图片上传 1&#xff0c; 获取文件&#xff0c;这里指的是图片 在鸿蒙内部有一个API pick选择器&#xff0c;实现文件保存和文件选择的功能&#xff0c; 使用pick对象创建PhotoViewPicker实例 传入必要的参数&#xff0c;如选择图片的数量&#xff0c;和弹出窗口的位置&#xf…

【机器学习】前沿探索,如何让前端开发更加搞笑

在当今数字化时代&#xff0c;机器学习的崛起为前端开发带来了巨大的机遇和挑战。随着人工智能和数据科学的不断进步&#xff0c;前端工程师不再局限于传统的界面设计和交互体验&#xff0c;而是开始探索如何将机器学习技术融入到他们的工作中&#xff0c;以创造更加智能、个性…

面了一个程序员,因为6休1拒绝了我

人一辈子赖以生存下去的主要就考虑三件事&#xff0c;职业&#xff0c;事业&#xff0c;副业&#xff0c;有其1-2都是很不错的。如果还没到40岁&#xff0c;那不妨提前想下自己可能遇到的一些情况&#xff0c;提前做一些准备&#xff0c;未雨绸缪些。 今年整体就业大环境也一般…

【手写大跟堆详解】

文章目录 大跟堆介绍大跟堆的结构大跟堆的应用场景大跟堆的代码实现 大跟堆介绍 大根堆&#xff08;Max Heap&#xff09;是一种特殊的二叉树结构&#xff0c;它满足以下两个条件&#xff1a; 1.完全二叉树&#xff1a;大根堆是一棵完全二叉树&#xff0c;即除了最后一层外&am…

一分钟快速排序

这个 quick_sort 函数是一个实现快速排序&#xff08;Quicksort&#xff09;算法的递归函数。快速排序是一种高效的排序算法&#xff0c;通常用于对大规模数据集进行排序。以下是对该函数的详细解释&#xff1a; 函数签名 void quick_sort(int q[], int l, int r)q[]&#xf…

Qt_电脑wifi相关操作

项目描述: 在做项目时用到了获取wifi的操作。在网上查找了好久资料,这里做一些总结。 这里有显示当前电脑wifi连接状态,列出wifi列表,连接断开wifi等函数。欢迎大家留言添加文章内容。 使用范围: windows电脑(中文的环境) 使用技术:windows的cmd命令。和对字符串的解析…

C语言学习笔记--运算符与表达式(7521字爆肝)

上午好&#xff0c;本来想上午改简历下午学习c语言的&#xff0c;但想了一下上午精力充沛还是用来学习比较好&#xff0c;虽然现在失业了&#xff0c;但住在我姨家有吃有住的&#xff0c;再次感谢我姨&#xff0c;我要抓紧时间修改简历&#xff0c;然后找个工作搬出去&#xff…

【回忆版】数据科学思维与大数据智能分析 2024考试

填空&#xff08;18分&#xff09;18个 1.对数变换对大数值的范围进行压缩&#xff0c;对小数值的范围进行扩展 2.提取出大量高频率项与低频率项相关联的虚假模式&#xff0c;即交叉支持&#xff08;cross-support&#xff09;模式 3.信息论中&#xff08;&#xff09; 4.几种…

[数据集][目标检测]弹簧上料检测数据集VOC+YOLO格式142张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;142 标注数量(xml文件个数)&#xff1a;142 标注数量(txt文件个数)&#xff1a;142 标注类别…

yolov8训练自己数据集时出现loss值为nan。

具体原因目前暂未寻找到。 解决办法 将参数amp改成False即可。 相关资料&#xff1a; https://zhuanlan.zhihu.com/p/165152789 https://github.com/ultralytics/ultralytics/issues/1148

【BUG】Edge|联想电脑 Bing 搜索报错“Ref A: 乱码、 Ref B:乱码、Ref C: 日期” 的解决办法

文章目录 省流版前言解决办法 详细解释版前言问题描述与排查过程解决办法与总结 省流版 前言 我也不清楚咋滴了&#xff0c;Bing 搜索突然偶尔报错&#xff1a; 换了代理关了插件都报错。 参考&#xff1a; 我在用bing搜索时出现了如下代码&#xff0c;导致bing无法使用&am…

nginx proxy_set_header详解

proxy_set_header 是 Nginx 配置中的一个重要指令&#xff0c;特别是在使用 Nginx 作为反向代理时。该指令允许你修改由 Nginx 传递给代理后端的请求头。这对于确保后端应用程序能够接收到正确的客户端信息&#xff08;如 IP 地址、主机名等&#xff09;以及控制缓存行为等场景…

1 计算机硬件-CPU-校验码-存储系统-输入输出设备-总线结构

计算机硬件 考情分析&#xff1a;趋势很小&#xff0c;22年考过&#xff0c;根据趋势以后考的可能较小 基本组成&#xff1a;运算器&#xff0c;控制器&#xff0c;储存器&#xff0c;输入设备&#xff0c;输出设备运算器和控制器也统称为中央处理单元&#xff08;CPU&#xf…

【算法训练 day37 柠檬水找零、长度最小的子数组、用最少数量的箭引爆气球】

目录 一、柠檬水找零-LeetCode 860思路实现代码个人问题总结 二、根据身高重建队列-LeetCode 406思路实现代码个人问题总结 三.用最少数量的箭引爆气球-LeeCode 406思路实现代码个人问题总结 一、柠檬水找零-LeetCode 860 Leecode链接: leetcode 860 文章链接: 代码随想录 视频…

解锁Nginx跨域谜题:3步打造安全高效的CORS策略

Nginx作为一款强大的Web服务器和反向代理服务器&#xff0c;经常被用于处理跨域资源共享&#xff08;CORS&#xff0c;Cross-Origin Resource Sharing&#xff09;策略&#xff0c;以允许或限制不同源之间的资源请求。CORS是一种安全策略&#xff0c;用于决定Web浏览器是否应允…

深度学习——图像分类(CNN)—测试模型

测试模型 1.导入必要的库2.加载测试数据集3.假设CSV文件中的图像文件名是完整的路径4.随机选择一张图片进行展示5.加载图像6.使用模型进行预测7.设置模型的预测结果8.计算准确率9.指定test文件夹路径10.读取名为image_path的图片11.加载图像12.检查图像是否为空 训练的模型是上…

eNSP学习——OSPF单区域配置

目录 相关命令 实验背景 实验目的 实验步骤 实验拓扑 实验编址 实验步骤 1、基础配置 2、部署单区域OSPF网络 3、检查OSPF单区域的配置结果 OSPF——开放式最短路径优先 基于链路状态的协议&#xff0c;具有收敛快、路由无环、扩展性好等优点&#xff1b; 相关命令 […