AddressSanitizer(ASan)

ASAN功能介绍

AddressSanitizer(ASan)工具是用于用户空间程序的内存错误检测工具,无法直接用于内核空间。

ASAN早先是LLVM中的特性,后被加入gcc4.8,成为 gcc 的一部分,但不支持符号信息,无法显示出问题的函数和行数。从 4.9 开始,gcc 支持 AddressSanitizer 的所有功能。因此gcc 4.8以上版本使用ASAN时不需要安装第三方库,通过在编译时指定编译CFLAGS即可打开开关。

ASan利用编译器插桩和运行时库来检测内存错误,例如缓冲区溢出、使用未初始化的内存等。但是,内核和用户空间程序的执行环境和内存管理方式不同,因此ASan无法直接应用于内核空间。

对于内核空间的内存错误检测,可以考虑使用其他专门针对内核开发的工具,如KASAN(Kernel AddressSanitizer)。KASAN是Linux内核中的一个内存错误检测工具,可以用于检测内核空间的内存问题。后续我们介绍学习。

优点:

  1. 检测能力强大:ASan可以检测到许多内存相关的错误,如缓冲区溢出、使用未初始化的内存、使用已释放的内存等。它通过在运行时对程序进行插桩来捕获并报告这些错误,帮助开发人员及早发现和修复问题。
  2. 低侵入性:ASan工具通过编译器插桩和运行时库来实现内存错误检测,对代码的更改较少,对现有代码的侵入性较小。这使得它相对容易集成到现有项目中,而无需进行大规模的代码修改。
  3. 容易定位问题:当ASan检测到内存错误时,它会提供详细的错误报告,包括错误类型、触发位置和调用栈信息。这有助于开发人员快速定位和修复问题,减少调试时间。
  4. 平台支持广泛:ASan工具在多个平台和编译器中都有支持,包括Linux、macOS和Windows等。这使得开发人员可以在不同的环境中使用相同的工具,提高了可移植性和开发效率。

缺点:

  1. 内存开销较大:ASan会在运行时为每个分配的内存块添加额外的元数据,并在每次内存访问时进行检查。这会导致内存使用量增加,有时可能会显著影响程序的性能和内存占用。
  2. 编译时间延长:由于ASan需要对程序进行插桩,编译时间可能会延长。特别是对于大型项目或包含大量代码的程序,这可能会导致较长的编译时间。
  3. 不支持所有错误类型:尽管ASan可以检测到许多内存相关的错误,但它并不能覆盖所有可能的问题。某些类型的错误,如数据竞争和内存泄漏,ASan无法直接检测到。
  4. 可能引入假阳性:由于ASan是在运行时进行检测的,它可能会产生一些误报(假阳性),即报告了实际上并非错误的情况。这可能会增加调试和排除问题的复杂性。

综上所述,ASan作为一种内存错误检测工具,具有很多优点,但也存在一些缺点。在使用ASan之前,需要权衡其带来的利弊,根据具体情况选择适合的工具和方法来确保代码的质量和可靠性。

ASAN介绍及使用

which aarch64-none-linux-gnu-gcc
/usr/local/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc./aarch64-none-linux-gnu/lib64/libasan.so
./aarch64-none-linux-gnu/libc/usr/lib64/libasan.so

gcc编译选项

#-fsanitize=address:开启内存越界检测
#-fsanitize-recover=address:一般后台程序为保证稳定性,不能遇到错误就简单退出,而是继续运行,采用该选项支持内存出错之后程序继续运行,需要叠加设置ASAN_OPTIONS=halt_on_error=0才会生效;若未设置此选项,则内存出错即报错退出

#-fno-stack-protector:去使能栈溢出保护
#-fno-omit-frame-pointer:去使能栈溢出保护

test.c测试程序:

//命名test.c
#include <stdio.h>
#include <stdlib.h>char* getMemory()
{char *p = (char *)malloc(30);return p;
}int main()
{char *p = getMemory();p = NULL;return 0;
}
//这段代码存在内存泄漏的问题。在函数getMemory中,它使用malloc函数动态分配了内存,并返回指向该内存的指针。
//然而,在main函数中,获取到指向动态分配内存的指针后,立即将指针赋值为NULL,导致之前分配的内存地址丢失,
//无法再被释放。

ASAN_OPTIONS设置:

ASAN_OPTIONS是Address-Sanitizier的运行选项环境变量。

#halt_on_error=0:检测内存错误后继续运行
#detect_leaks=1:使能内存泄露检测
#malloc_context_size=15:内存错误发生时,显示的调用栈层数为15
#log_path=/home/asan.log:内存检查问题日志存放文件路径

#env |grep ASAN_OPTIONS
设置环境变量,

# export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/home/asan.log
# env |grep ASAN_OPTIONS
# gcc test.c -fsanitize=address -fsanitize-recover=address -fno-stack-protector -fno-omit-frame-pointer

运行a.out会生成acan检出的log, /home/asan.log.60920,如下:


=================================================================
==114141==ERROR: LeakSanitizer: detected memory leaksDirect leak of 30 byte(s) in 1 object(s) allocated from:#0 0x7fb16294bb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)#1 0x55ca068137fb in getMemory (/home/wuqianlong/a.out+0x7fb)#2 0x55ca06813817 in main (/home/wuqianlong/a.out+0x817)#3 0x7fb16249dc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).

动态库的asan日志定位

现在创建两个文件,test.c和test1.c, test.c的内容如上述内容。

//test1.c
#include <stdio.h>
#include <stdlib.h>extern char* getMemory();int main()
{char *p = getMemory();p = NULL;return 0;
}

将test.c编译成共享库libtest.so

gcc -shared -o libtest.so test.c  -fsanitize=address -fsanitize-recover=address -fno-stack-protector -fPIC

链接共享库,编译test1.c成a.out文件

gcc -o a.out test1.c /home/wuqianlong/libtest.so -fsanitize=address -fsanitize-recover=address -fno-stack-protector

运行a.out文件生成asan log如下:


=================================================================
==89713==ERROR: LeakSanitizer: detected memory leaksDirect leak of 30 byte(s) in 1 object(s) allocated from:#0 0x7f0efbcefb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)#1 0x7f0efba0f6eb in getMemory (/home/wuqianlong/libtest.so+0x6eb)#2 0x5570669f283b in main (/home/wuqianlong/a.out+0x83b)#3 0x7f0efb63fc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).

案例

1.堆访问越界(heap-buffer-overflow)

#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{int *array = malloc(sizeof (int) * 100);array[0] = 0;int res = array[1 + 100]; //array访问越界free(array);pause();//程序等待,不退出return 0;
}

编译命令:

gcc heapOOB.c -o heapOOB -g -fsanitize=address -fsanitize=leak

执行情况:

==3653==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd4 at pc 0x000000400871 bp 0x7ffe50cde9c0 sp 0x7ffe50cde9b0
READ of size 4 at 0x61400000ffd4 thread T0
#0 0x400870 in main /home/jetpack/work/4G/test/asan/heapOOB.c:7
#1 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapOOB+0x400708)
0x61400000ffd4 is located 4 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0)
allocated by thread T0 here:
#0 0x7f30b37bc602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapOOB.c:5
#2 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

2.栈访问越界(stack-buffer-overflow)

#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) 
{int stack_array[100];stack_array[100] = 0;//栈访问越界pause();return 0; 
}

编译命令:

gcc stackOOB.c -o stackOOB -g -fsanitize=address -fsanitize=leak

执行结果:

==3952==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffdb72e830 at pc 0x0000004008ef bp 0x7fffdb72e660 sp 0x7fffdb72e650
WRITE of size 4 at 0x7fffdb72e830 thread T0
#0 0x4008ee in main /home/jetpack/work/4G/test/asan/stackOOB.c:6
#1 0x7f0c47a8e83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400748 in _start (/home/jetpack/work/4G/test/asan/stackOOB+0x400748)
Address 0x7fffdb72e830 is located in stack of thread T0 at offset 432 in frame
#0 0x400825 in main /home/jetpack/work/4G/test/asan/stackOOB.c:4
This frame has 1 object(s):
[32, 432) 'stack_array' <== Memory access at offset 432 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)

3.使用已经释放的内存(UseAfterFree)

#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) 
{int *array = malloc(sizeof (int) * 100);array[0] = 0;free(array);int res = array[0]; //使用已经释放的内存pause();return 0;
}

编译命令:

gcc heapUAF.c -o heapUAF -g -fsanitize=address -fsanitize=leak

执行结果:

==4385==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe40 at pc 0x000000400877 bp 0x7ffdc9019c20 sp 0x7ffdc9019c10
READ of size 4 at 0x61400000fe40 thread T0
#0 0x400876 in main /home/jetpack/work/4G/test/asan/heapUAF.c:8
#1 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapUAF+0x400708)
0x61400000fe40 is located 0 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:
#0 0x7f93f91982ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
#1 0x40083f in main /home/jetpack/work/4G/test/asan/heapUAF.c:7
#2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
previously allocated by thread T0 here:
#0 0x7f93f9198602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapUAF.c:5
#2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

4.内存泄漏 (Memory Leak)

#include <string.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char **argv) 
{int *array = malloc(sizeof (int) * 100);memset(array, 0, 100 * 4);return 0;
}
//内存泄漏:在 main 函数中通过调用 malloc 分配了内存空间给 array,但在函数结束时并未调用 free 来释放这块内存。这将导致在每次程序运行时都会分配一块新的内存空间,而不会释放之前分配的内存,最终可能导致内存泄漏。

编译命令:

gcc heapLeak.c -o heapLeak -g -fsanitize=address -fsanitize=leak

执行结果:

==3120==ERROR: LeakSanitizer: detected memory leaksDirect leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x7f412d5b7602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x40073e in main /home/jetpack/work/4G/test/asan/heapLeak.c:7
#2 0x7f412d17583f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s).

5.函数返回的局部变量访问

int *ptr;
void usr_func(void)
{    //申请栈内存int local[100] = {0};ptr = local;
}    int main(void)
{    //使用函数栈返回的内存*ptr = 0;return 0;                                                                                                                                                                                                                                
}

6.全局变量访问越界

int global_array[100] = {0};                                                                                                                                                                                                                 int main(int argc, char **argv) 
{//全局变量global_array访问越界 global_array[101];return 0;
}

感谢一起学习,有疑问评论区讨论。

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

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

相关文章

Mybatis-Plus 自定义SQL注入器,实现真正的批量插入![MyBatis-Plus系列]

导读 Hi,大家好,我是悟纤。过着爱谁谁的生活,活出不设限的人生。 在使用MyBatis-Plus时,dao层都会去继承BaseMapper接口,这样就可以用BaseMapper接口所有的方法CRUD。 在Mybatis-Plus中调用updateById方法进行数据更新默认情况下是不能更新空值字段的。

使用契约的链上限价订单

我们开发了链上限价订单。 它基于一种称为契约的智能合约&#xff0c;只有在花费输出的交易满足特定条件时才可以花费输出。 为了演示其工作原理&#xff0c;我们实施了以比特币支付的 Ordinals 代币买卖限价订单&#xff0c;无需托管人。 它可以运行在任何比特币协议链上&…

【Python入门教程】Python中函数的用法和意义

在Python中&#xff0c;函数是一种可重用的代码块&#xff0c;它可以被多次调用以执行特定的任务。函数可以帮助我们组织代码&#xff0c;使其更易于阅读和调试&#xff0c;同时还可以提高代码的可重用性和可维护性。 一、函数的定义 在Python中&#xff0c;函数使用def关键字…

C++标准模板库(STL)-map介绍

C标准库中的map是一种关联容器&#xff0c;它提供了键值对的映射关系。每个键值对中的键都是唯一的&#xff0c;通过键可以访问对应的值。 map基本操作 插入元素&#xff1a; 使用insert函数插入元素&#xff0c;该函数有两种形式&#xff1a; // 插入一个pair<const Ke…

网络层——IP协议

文章目录 一.IP协议二.基本概念三.IP协议格式四.分片与组装五.网段划分六.特殊的IP地址七.IP地址的数量限制八.私网IP地址和公网IP地址九.路由十.路由表生成算法 一.IP协议 IP协议全称为“网际互连协议&#xff08;Internet Protocol&#xff09;”&#xff0c;IP协议是TCP/IP…

[uni-app]记录APP端跳转页面自动滚动到底部的bug

文章目录 bug描述原因分析: 处理方案 bug描述 1.点击的A页面, 跳转到了B页面, 第一次页面正常显示 2.从B页面返回A页面 3.A页面不进行任何操作,再次点击A页面进入B页面 4.B页面自动滚动到底部. 原因 看一段A页面代码 let that thisthis.defaultScrollTop uni.getStorageSy…

Zookeeper中的Watch机制的原理?

前言 Zookeeper是一个分布式协调组件&#xff0c;为分布式架构下的多个应用组件提供了顺序访问控制能力。它的数据存储采用了类似于文件系统的树形结构&#xff0c;以节点的方式来管理存储在Zookeeper上的数据 Zookeeper提供了一个Watch机制&#xff0c;可以让客户端感知到Zook…

第12周 E-R图

偏系统化的ER图例子还是不够&#xff0c;需要寻找 汇报时很多人的er图都有问题&#xff0c;重点是作为e的实体&#xff08;方框表示&#xff09;含义并没有搞懂

启动dubbo消费端过程提示No provider available for the service的问题定位与解决

文/朱季谦 某次在启动dubbo消费端时&#xff0c;发现无法从zookeeper注册中心获取到所依赖的消费者API&#xff0c;启动日志一直出现这样的异常提示 Failed to check the status of the service com.fte.zhu.api.testService. No provider available for the service com.fte…

【Vue】Vue3 超简单拖拽条动态修改容器宽度

demo 代码 const leftBoxWidth ref(200); // 默认宽度 const leftResize (e: MouseEvent) > {const startX e.clientX;const startWidth leftBoxWidth.value;const mouseMove (documentE: MouseEvent) > {// 80 是左侧菜单宽度leftBoxWidth.value startWidth docu…

卷积神经网络(VGG-16)海贼王人物识别

文章目录 前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;我的环境&#xff1a; 2. 导入数据3. 查看数据 二、数据预处理1. 加载数据2. 可视化数据3. 再次检查数据4. 配置数据集5. 归一化 三、构建VGG-16网络1. 官方模型&#xff08;已打包好&#xff…

笔记56:深度循环神经网络

本地笔记地址&#xff1a;D:\work_file\DeepLearning_Learning\03_个人笔记\3.循环神经网络\第9章&#xff1a;动手学深度学习~现代循环神经网络 a a a a a a a a

Cesium冷知识:判断cesium是否使用webgl2

老的Cesium.js&#xff0c;默认使用webgl1.0技术 现在默认使用webgl2.0技术 虽然有默认值&#xff0c;但是都可以通过相关参数来改变webgl版本 那么如何判断当前Cesium.js使用的是哪个webgl版本&#xff1f; 方案1&#xff1a; let isWebgl2viewer.scene.context.webgl2; //…

vue动态配置路由

文章目录 前言定义项目页面格式一、vite 配置动态路由新建 /router/utils.ts引入 /router/utils.ts 二、webpack 配置动态路由总结如有启发&#xff0c;可点赞收藏哟~ 前言 项目中动态配置路由可以减少路由配置时间&#xff0c;并可减少配置路由出现的一些奇奇怪怪的问题 路由…

vue将base64编码转为pdf方法

<iframe width"100%" height"100%" src"" frameborder"0" id"iframe"></iframe>使用方法: 直接调用就行 viewPdf(传入base64编码即可)//content是base64编码格式const viewPdf (content:any)> {const blob …

为什么选择B+树作为数据库索引结构?

背景 首先&#xff0c;来谈谈B树。为什么要使用B树&#xff1f;我们需要明白以下两个事实&#xff1a; 【事实1】 不同容量的存储器&#xff0c;访问速度差异悬殊。以磁盘和内存为例&#xff0c;访问磁盘的时间大概是ms级的&#xff0c;访问内存的时间大概是ns级的。有个形象…

抖音运营的必备10个工具,开启智能拓客引流新时代!

先来看实操成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 一、引言 亲爱的知友们&#xff0c;您们是否对抖音运营有浓厚的兴趣和独特的见解&#xff1f;今天&#xff0c;我将为您介绍一些抖音运营必备的工具&#xff0c;帮助您在抖音上脱颖…

windows通过命令给文件夹或文件增加权限

给Demo001追加everyone权限 D:\cmd>cacls Demo001 /p everyone:f /e 处理的目录: D:\cmd\Demo001D:\cmd> 给Demo001下的所有文件追加everyone权限 D:\cmd>cacls Demo001 /p everyone:f /e /t 处理的目录: D:\cmd\Demo001 处理的目录: D:\cmd\Demo001\A 处理的文件:…

SpringMvc请求原理流程

springmvc是用户和服务沟通的桥梁&#xff0c;官网提供了springmvc的全面使用和解释&#xff1a;DispatcherServlet :: Spring Framework 流程 1.Tomcat启动 2.解析web.xml文件&#xff0c;根据servlet-class找到DispatcherServlet&#xff0c;根据init-param来获取spring的…

Mysql 千万级别查询优化

经常碰到慢查询报警&#xff0c;我们线上的慢sql阈值是1s&#xff0c;出现报警的表数据有 7000多万&#xff0c;经常出现报警的是一个group by的count查询&#xff0c;于是便开始着手优化这块&#xff0c;记录下自己优化过程中的心得 拿下面这张表举例&#xff0c;这是一张记录…