Address Sanitizer(ASan)是一个快速的内存错误检测工具。这里说明它的用法。

参考资料

AddressSanitizer
https://github.com/google/sanitizers/wiki/AddressSanitizer

1. 简介

Address Sanitizer(ASan)是一个快速的内存错误检测工具。它非常快,只拖慢程序两倍左右(比起Valgrind快多了)。它包括一个编译器instrumentation模块和一个提供malloc()/free()替代项的运行时库。

从gcc 4.8开始,AddressSanitizer成为gcc的一部分。当然,要获得更好的体验,最好使用4.9及以上版本,因为gcc 4.8的AddressSanitizer还不完善,最大的缺点是没有符号信息。

2. 使用步骤

  • 用-fsanitize=address选项编译和链接你的程序。
  • 用-fno-omit-frame-pointer编译,以得到更容易理解stack trace。
  • 可选择-O1或者更高的优化级别编译
gcc -fsanitize=address -fno-omit-frame-pointer -O1 -g use-after-free.c -o use-after-free

运行use-after-fee。如果发现了错误,就会打印出类似下面的信息:

==9901==ERROR: AddressSanitizer: heap-use-after-free on address 0x60700000dfb5 at pc 0x45917b bp 0x7fff4490c700 sp 0x7fff4490c6f8
READ of size 1 at 0x60700000dfb5 thread T0#0 0x45917a in main use-after-free.c:5#1 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226#2 0x459074 in _start (a.out+0x459074)
0x60700000dfb5 is located 5 bytes inside of 80-byte region [0x60700000dfb0,0x60700000e000)
freed by thread T0 here:#0 0x4441ee in __interceptor_free projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64#1 0x45914a in main use-after-free.c:4#2 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
previously allocated by thread T0 here:#0 0x44436e in __interceptor_malloc projects/compiler-rt/lib/asan/asan_malloc_linux.cc:74#1 0x45913f in main use-after-free.c:3#2 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
SUMMARY: AddressSanitizer: heap-use-after-free use-after-free.c:5 main
  • 第一部分(ERROR)指出错误类型是heap-use-after-free;
  • 第二部分(READ), 指出线程名thread T0,操作为READ,发生的位置是use-after-free.c:5。
    • 该heapk块之前已经在use-after-free.c:4被释放了;
    • 该heap块是在use-fater-free.c:3分配
  • 第三部分 (SUMMARY) 前面输出的概要说明。

3. 错误类型

3.1 (heap) use after free 释放后使用

下面的代码中,分配array数组并释放,然后返回它的一个元素。

  5 int main (int argc, char** argv)6 {7     int* array = new int[100];8     delete []array;9     return array[1];10 }

下面的错误信息与前面的“使用步骤”一节中的类似。

==3189==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe44
at pc 0x0000004008f1 bp 0x7ffc9b6e2630 sp 0x7ffc9b6e2620
READ of size 4 at 0x61400000fe44 thread T0#0 0x4008f0 in main /home/ron/dev/as/use_after_free.cpp:9#1 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)#2 0x4007b8 in _start (/home/ron/dev/as/build/use_after_free+0x4007b8)0x61400000fe44 is located 4 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:#0 0x7f3763ef1caa in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99caa)#1 0x4008b5 in main /home/ron/dev/as/use_after_free.cpp:8#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)previously allocated by thread T0 here:#0 0x7f3763ef16b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)#1 0x40089e in main /home/ron/dev/as/use_after_free.cpp:7#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)SUMMARY: AddressSanitizer: heap-use-after-free /home/ron/dev/as/use_after_free.cpp:9 main

3.2 heap buffer overflow 堆缓存访问溢出

如下代码中,访问的位置超出堆上数组array的边界。

  2 int main (int argc, char** argv)3 {4     int* array = new int[100];5     int res = array[100];6     delete [] array;7     return res;8 }

下面的错误信息指出:

  • 错误类型是heap-buffer-overflow
  • 不合法操作READ发生在线程T0, heap_buf_overflow.cpp:5
  • heap块分配发生在heap_buf_overflow.cpp
==3322==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd0
at pc 0x0000004008e0 bp 0x7ffeddce53a0 sp 0x7ffeddce5390
READ of size 4 at 0x61400000ffd0 thread T0#0 0x4008df in main /home/ron/dev/as/heap_buf_overflow.cpp:5#1 0x7f3b83d0882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)#2 0x4007b8 in _start (/home/ron/dev/as/build/heap_buf_overflow+0x4007b8)0x61400000ffd0 is located 0 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0)
allocated by thread T0 here:#0 0x7f3b841516b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)#1 0x40089e in main /home/ron/dev/as/heap_buf_overflow.cpp:4#2 0x7f3b83d0882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ron/dev/as/heap_buf_overflow.cpp:5 main

3.2 stack buffer overflow 栈缓存访问溢出

如下代码中,访问的位置超出栈上数组array的边界。

  2 int main (int argc, char** argv)3 {4     int array[100];5     return array[100];6 }

下面的错误信息指出:

  • 错误类型是stack-buffer-overflow
  • 不合法操作READ发生在线程T0, stack_buf_overflow.cpp:5
  • 栈块在线程T0的栈上432偏移位置上。
==3389==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd061fa4a0
at pc 0x0000004009ff bp 0x7ffd061fa2d0 sp 0x7ffd061fa2c0
READ of size 4 at 0x7ffd061fa4a0 thread T0#0 0x4009fe in main /home/ron/dev/as/stack_buf_overflow.cpp:5#1 0x7fbade4e882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)#2 0x400858 in _start (/home/ron/dev/as/build/stack_buf_overflow+0x400858)Address 0x7ffd061fa4a0 is located in stack of thread T0 at offset 432 in frame#0 0x400935 in main /home/ron/dev/as/stack_buf_overflow.cpp:3This frame has 1 object(s):[32, 432) '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)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/ron/dev/as/stack_buf_overflow.cpp:5 main

3.3 global buffer overflow 全局缓冲访问溢出

如下代码中,访问的位置超出全局数组array的边界。

  2 int array[100];3 4 int main (int argc, char** argv)5 {6     return array[100];7 }

下面的错误信息指出:

  • 错误类型是global-buffer-overflow
  • 不合法操作READ发生在线程T0, global_buf_overflow.cpp:6
  • 缓存块在global_buf_overflow.cpp:2 定义。
==3499==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000601270
at pc 0x000000400915 bp 0x7ffd8e80c020 sp 0x7ffd8e80c010
READ of size 4 at 0x000000601270 thread T0#0 0x400914 in main /home/ron/dev/as/global_buf_overflow.cpp:6#1 0x7f613c1c882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)#2 0x400808 in _start (/home/ron/dev/as/build/global_buf_overflow+0x400808)0x000000601270 is located 0 bytes to the right of global variable 'array' defined in
'/home/ron/dev/as/global_buf_overflow.cpp:2:5' (0x6010e0) of size 400
SUMMARY: AddressSanitizer: global-buffer-overflow /home/ron/dev/as/global_buf_overflow.cpp:6 main

3.4 use after return

3.5 use after scope

3.6 initializations order bugs

3.7 memory leaks 内存泄露

检测内存的LeakSanitizer是集成在AddressSanitizer中的一个相对独立的工具,它工作在检查过程的最后阶段。

下面代码中,p指向的内存没有释放。

  4 void* p;5 6 int main ()7 {8     p = malloc (7);9     p = 0;10     return 0;11 }

下面的错误信息指出 detected memory leaks

  • 内存在mem_leak.cpp:8分配
  • 缓存块在global_buf_overflow.cpp:2 定义。
==4088==ERROR: LeakSanitizer: detected memory leaksDirect leak of 7 byte(s) in 1 object(s) allocated from:#0 0x7ff9ae510602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)#1 0x4008d3 in main /home/ron/dev/as/mem_leak.cpp:8#2 0x7ff9ae0c882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)SUMMARY: AddressSanitizer: 7 byte(s) leaked in 1 allocation(s).

目前,并不是所有的平台都默认检测内存泄露,可以指定ASAN_OPTIONS开启如下:

ASAN_OPTIONS=detect_leaks=1 yourapp

而且不是所有的平台支持检测内存泄露,比如ARM,就会得到这样的提示:

==1901==AddressSanitizer: detect_leaks is not supported on this platform.

4. 工作原理

4.1 概要说明

AddressSanitizer的运行时库替换malloc()/free()。分配缓存前后的空间标记为poisoned,已经被释放的缓存也被标记为poisoned。内存访问的代码都被编译器替换如下:

替换之前:

*address = ...;

替换之后:

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

访问之前检查访问地址是否poisoned,如果是,报告错误。

4.2 memory mapping 和 instrumentation

进程的虚拟地址空间划分为两个不相连的部分:

  • main application memory (Mem)。这是程序自身代码使用的内存;
  • Shadow memory (Shadow)。这里放的是shadow value(meta data)。从Mem到Shadow之间有映射关系。将Mem的一个字节标记为poisoned,其实就是在对应的Shadow内存中写入指定值。

伪代码如下。它先从Mem中地址计算对应的Shadow地址。

shadow_address = MemToShadow (address);
if (ShadowIsPoisoned(shadow_address))
{ReportError (address, kAccessSize, kIsWrite);
}

4.3 mapping

Mem中的8字节映射到Shadow memory中是1字节。
这个字节可能有9种不同的值:

  • 所有8字节都是unpoisoned的,则值为0;
  • 所有8字节都是poisoned的,则值为负;
  • 前k字节为unpoisoned,后面8-k字节为poisoned, 则值为k。
    • malloc()分配的内存总是8字节的倍数,如果要分配的缓存不是8字节的倍数,则尾部的8个字节poisoned状态不同。比如分配13字节,会得到两个8字节。前1个全是unpoisoned,后一个只有前5个字节是unpoisoned,后3个字节是poisoned。

4.4 栈的处理

为了捕捉栈的访问溢出,AddressSanitizer在缓存前后加上保护区。这里可以看到设置对应Shadow memory的代码。

改编之前为:

void foo()
{char a[8];...return;
}

改编之后为:

void foo()
{char redzone1[32];  // 32-byte alignedchar a[8];          // 32-byte alignedchar redzone2[24];char redzone3[32];  // 32-byte alignedint  *shadow_base = MemToShadow(redzone1);shadow_base[0] = 0xffffffff;  // poison redzone1shadow_base[1] = 0xffffff00;  // poison redzone2, unpoison 'a'shadow_base[2] = 0xffffffff;  // poison redzone3...shadow_base[0] = shadow_base[1] = shadow_base[2] = 0; // unpoison allreturn;
}

4.5 malloc()/free()的处理

运行时库用自己的函数替换malloc() / free()。

  • malloc()在缓存前后分配保护区。缓存本身标记为unpoisoned,保护区标记为poisoned。
  • free() 将整个区域,包括缓存和保护区,都标记为poisoned,并将该区域放入一个特别的队列中,以保证malloc()在相当长的时间内不会再次使用它)。

上述都是直接就显示了代码的行数,实际很多时候都是库文件,需要通过addr2line命令工具来定位具体代码行数

addr2line -a -C -e libstagefright_omx.so  -f 00023808

addr2line -e libWebSocketProtocolWrapper.so 0x65679

地址内容就是asan输出信息的中的库+地址,这个libWebSocketProtocolWrapper.so+06679这个串中的地址

Address Sanitizer定位内存问题相关推荐

  1. Android稳定性系列-01-使用 Address Sanitizer检测原生代码中的内存错误

    前言 想必大家曾经被各种Native Crash折磨过,本地测试没啥问题,一到线上或者自动化测试就出现各种SIGSEGV.SIGABRT.SIGILL.SIGBUS.SIGFPE异常,而且堆栈还是崩溃 ...

  2. LINUX 下使用Address Sanitizer ,以及不能运行的问题

    文章目录 一. 简介 二.AddressSanitizer 的使用 使用方法 1.使用添加编译选项的方式使用ASan 2.使用CMake添加编译选项 三.测试 不添加Asan选项,不会有任何输出 添加 ...

  3. Address Sanitizer使用指南

    文章目录 前言 一.Address Sanitizer简介 二.使用步骤 1.安装gcc7.5.0 2.编译程序 3.设置运行环境 4.运行程序 5.分析内存检测报告 前言 使用C/C++编程,不可避 ...

  4. Sanitizers 系列之 address sanitizer 用法篇

    入门例子 二分查找算法是非常经典的算法,它看似简单,但是写出一个完全正确的二分查找算法还是比较考验工程师的算法功力的,下面是在网上流传的一个版本,请读者思考:这个程序正确吗? #include < ...

  5. 用mtrace定位内存泄漏

    一. 缘起 有的公众号读者,看完我上次写给大学生的查bug方法后,希望我多分享一些查bug的实践经验和具体步骤,比如如何查内存泄漏和core dump问题.所以,就打算写这篇文章. 二. 内存泄漏简介 ...

  6. linux libasan.so,Address Sanitizer 用法

    Address Sanitizer(ASan)是一个快速的内存错误检测工具.这里说明它的用法. 参考资料 1. 简介 Address Sanitizer(ASan)是一个快速的内存错误检测工具.它非常 ...

  7. Address Sanitizer 用法

    Address Sanitizer(ASan)是一个快速的内存错误检测工具.这里说明它的用法. 参考资料 AddressSanitizer https://github.com/google/sani ...

  8. 使用Windbg定位内存泄露

    在网上看了两篇文章,整理一下,大致内容如下: 场景一:运行Debug版本程序,用Windbg attach 上去,等程序退出时,基于内存泄露报告,定位内存泄露的位置. 首先使用windbg工具gfla ...

  9. Android NDK Address Sanitizer

    文章目录 构建 运行 堆栈轨迹 二进制测试 此文章是基于官方文档 Address Sanitizer的基础上做了一些扩展说明. 从 API 级别 27 (Android O MR 1) 开始,Andr ...

最新文章

  1. 按照重复的次数高到低进行排序
  2. cmd怎么运行http_Scrapy源码剖析(二)Scrapy是如何运行起来的?
  3. pythonfind_python实现find -name的功能
  4. 前华为技术大牛告诉你:如何正确入门深度学习
  5. java 转换int类型数值到对应的金额大写
  6. php支付问题,如何解决php支付错误的问题
  7. 什么样的简历,面试官一看就知道多少水份!!!
  8. Flash安装低版本方法
  9. QGIS获取OSM地图矢量数据
  10. 数仓建模—建模工具PdMan
  11. 随机森林回归预测r语言_R包randomForest的随机森林回归模型以及对重要变量的选择...
  12. 科学研究设计五:实验设计
  13. Symantec的SEP服务器(SEPM)从12.1 RU6MP5 升级到14 MP1 操作手册
  14. 红米停在android,红米手机在Android开发中无法获取data/data中数据的问题
  15. python如何期货交易_Python期货量化交易实战
  16. [人工智能-深度学习-43]:输入预处理 - 规范化Normalization、标准化Standardization、正态分布、算术平均、方差
  17. iOS App体验设计
  18. XMind8.0介绍与安装(破解)
  19. cad怎么画坐标系箭头_AutoCAD2016怎么画箭头 在一条直线上画个箭头方法
  20. 常用的系统操作响应时间

热门文章

  1. Python——Python使用POP3协议客户端poplib登录邮箱并解压缩zip、rar压缩包
  2. winform使用NPIO导入导出Excel
  3. Java小型管理系统,文章中有源码,适合作为课程作业,个人小项目(中等)
  4. 【在线OJ项目】项目环境与项目演示
  5. 18135usm_更快更安静 佳能18-135mm USM镜头解析
  6. 数据结构背包问题c语言思路,C语言学习趣事_数据结构_经典命题_1_背包问题_分析_1...
  7. uniapp中如何引入uView模板
  8. php libiconv close_PHP 編譯安裝時出現 undefined reference to `libiconv' 錯誤的解決方法
  9. AD原理图与封装创建
  10. 永坤电机SEO三天两夜课程学习总结