我用最多的排查c程序的内存问题的工具就是valgrind了,但是它这个工具有时候不是太好用,比如说,速度很慢,平时运行一次1s的程序,用valgrind排查可能要10s左右,还有没有好用的工具那,这不就发现了一个快速内存错误检测工具:Address Sanitizer。

一  简单介绍

Address Sanitizer是谷歌的快速的内存错误检测工具,它非常快只拖慢程序2倍左右的速度,在这次使用过程中,也是深有体会。在GCC 4.9版本以上,就可以很好的使用了。

Sanitizers是谷歌发起的开源工具集,包括了AddressSanitizer, MemorySanitizer, ThreadSanitizer, LeakSanitizer,Sanitizers项目本是LLVM项目的一部分,但GNU也将该系列工具加入到了自家的GCC编译器中。GCC从4.8版本开始支持Address和Thread Sanitizer,4.9版本开始支持Leak Sanitizer和UB Sanitizer,这些都是查找隐藏Bug的利器。

可以支持的内存检测:

  • Use after free

  • Heap buffer overflow

  • Stack buffer overflow

  • Global buffer overflow

  • Use after return

  • Use after scope

  • Initialization order bugs

  • Memory leaks

二 使用方法

  1. 确保自己的gcc版本在4.9以上,4.8版本上缺少符号信息,我的测试机器:centos 7.x ,gcc的版本是4.8.5版本,需要升级。

gcc -v
gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)

升级过程:

wget https://mirrors.ustc.edu.cn/gnu/gcc/gcc-4.9.4/gcc-4.9.4.tar.gz
tar  xvf  gcc-4.9.4.tar.gz
cd gcc-4.9.4
./contrib/download_prerequisites
mkdir build-gcc
cd build-gcc/
../gcc-4.9.4/configure --enable-checking=release --enable-languages=c,c++ --disable-multilib
make -j 8
make install

注意编译安装gcc的过程,超级慢要1-2个小时。2. 编译时候添加选项:

-fsanitize=address  -fno-omit-frame-pointer -fno-optimize-sibling-calls  -O0

编译的时候说缺少个依赖的库。通过:

yum install libasan -y

命令可以安装下。

注意:Address Sanitizer 会替换malloc和free,如果采用第三方的内存申请库,则无法替换,会造成功能缺失。

三 实践测试

3.1 栈溢出

  1 #include <stdio.h>2 #include <stdlib.h>34 int func0(void)5 {6    char str[4] = {0};7    strcpy(str,"1234");8    return 0;9 }10 int main(int argc,char *argv[])11 {12    func0();13    return 0;14 }

命令:

gcc -g main.c -o t1  -fsanitize=leak -fsanitize=address  -fno-omit-frame-pointer

运行报错:

[root@localhost tests]# ./t1
./t1: error while loading shared libraries: libasan.so.1: cannot open shared object file: No such file or directory

缺少动态库,解决办法:

[root@localhost tests]# find / -name libasan.so.1
/usr/local/lib64/libasan.so.1
[root@localhost tests]# echo /usr/local/lib64 >> /etc/ld.so.conf
[root@localhost tests]# ldconfig

运行显示:

[root@localhost tests]# ./t1
=================================================================
==2890==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffed0cfce50 at pc 0x400965 bp 0x7ffed0cfce20 sp 0x7ffed0cfce18
WRITE of size 5 at 0x7ffed0cfce50 thread T0#0 0x400964 in func0 /home/miaohq/tests/main.c:7#1 0x4009d2 in main /home/miaohq/tests/main.c:12#2 0x7f73dfa19444 in __libc_start_main (/lib64/libc.so.6+0x22444)#3 0x400738 (/home/miaohq/tests/t1+0x400738)Address 0x7ffed0cfce50 is located in stack of thread T0 at offset 32 in frame#0 0x400815 in func0 /home/miaohq/tests/main.c:5This frame has 1 object(s):[32, 36) 'str' <== Memory access at offset 32 partially 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/miaohq/tests/main.c:7 func0
Shadow bytes around the buggy address:
....

说明:

  1. 错误类型是 stack-buffer-overflow

  2. 不合法操作WRITE发生在线程T0 WRITE of size 5 at 0x7ffed0cfce50 thread T0

  3. 具体发生的位置:/home/miaohq/tests/main.c:7

  4. 后面还有影子内存一些指示,后续再开一篇聊下,主要我也不熟悉哈哈:)。

3.2 堆溢出

示例代码如下:

 11 void func1(void)12 {13    char * p = (char*) malloc(sizeof(char)*4);14    char chs[] ={"12345"};15    memset(p,0x0,4);16    if (p != NULL) {17      memcpy(p,chs,5);18    }19 }

按照同样办法编译,测试如下:

==37125==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000eff0 at pc 0x400cbe bp 0x7ffc7be10c20 sp 0x7ffc7be10c18
WRITE of size 5 at 0x60200000eff0 thread T0#0 0x400cbd in func1 /home/miaohq/tests/main.c:17#1 0x400d3d in main /home/miaohq/tests/main.c:24#2 0x7fe88958d444 in __libc_start_main (/lib64/libc.so.6+0x22444)#3 0x400858 (/home/miaohq/tests/t1+0x400858)0x60200000eff4 is located 0 bytes to the right of 4-byte region [0x60200000eff0,0x60200000eff4)
allocated by thread T0 here:#0 0x7fe88998c0f2 in __interceptor_malloc ../../../../gcc-4.9.4/libsanitizer/asan/asan_malloc_linux.cc:96#1 0x400b60 in func1 /home/miaohq/tests/main.c:13#2 0x400d3d in main /home/miaohq/tests/main.c:24#3 0x7fe88958d444 in __libc_start_main (/lib64/libc.so.6+0x22444)

说明:

  1. 错误类型:heap-buffer-overflow

  2. 错误原因:WRITE of size 5 at 0x60200000eff0 thread T0

  3. 发生位置: #0 0x400cbd in func1 /home/miaohq/tests/main.c:17

3.3 释放后使用

就是申请了一块内存区域,释放后没有设置为NULL,后续继续使用了。代码示例:

 21 void func2(void)22 {23     int * a = (int*)malloc(sizeof(int)*1);24    if ( a != NULL ) {25       *a = 1;26       printf("a is:%d.",*a);27      free(a);28      *a = 2;29      printf("error a is:%d.",*a);30    }31 }

同样的编译方法,报错如下:

==37556==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200000eff0 at pc 0x400ed6 bp 0x7ffd2dafb6d0 sp 0x7ffd2dafb6c8
WRITE of size 4 at 0x60200000eff0 thread T0#0 0x400ed5 in func2 /home/miaohq/tests/main.c:28#1 0x400f0c in main /home/miaohq/tests/main.c:36#2 0x7f5632169444 in __libc_start_main (/lib64/libc.so.6+0x22444)#3 0x400948 (/home/miaohq/tests/t1+0x400948)0x60200000eff0 is located 0 bytes inside of 4-byte region [0x60200000eff0,0x60200000eff4)
freed by thread T0 here:#0 0x7f5632567ec1 in __interceptor_free ../../../../gcc-4.9.4/libsanitizer/asan/asan_malloc_linux.cc:79#1 0x400e9e in func2 /home/miaohq/tests/main.c:27#2 0x400f0c in main /home/miaohq/tests/main.c:36#3 0x7f5632169444 in __libc_start_main (/lib64/libc.so.6+0x22444)previously allocated by thread T0 here:#0 0x7f56325680f2 in __interceptor_malloc ../../../../gcc-4.9.4/libsanitizer/asan/asan_malloc_linux.cc:96#1 0x400e2b in func2 /home/miaohq/tests/main.c:23#2 0x400f0c in main /home/miaohq/tests/main.c:36#3 0x7f5632169444 in __libc_start_main (/lib64/libc.so.6+0x22444)SUMMARY: AddressSanitizer: heap-use-after-free /home/miaohq/tests/main.c:28 func2

说明:

  1. 错误类型:heap-use-after-free

  2. 错误原因:WRITE of size 4 at 0x60200000eff0 thread T0

  3. 发生位置:#0 0x400ed5 in func2 /home/miaohq/tests/main.c:28 这个挺好的,有明确的错误类型,指示位置也很准确。

3.4 全局缓存溢出

这些错误类型比valgrind分的更细致点,让我们来看看代码:

int g_abc[11];36 int func3(void)37 {38     int i = 0;39     for (i = 0; i <= 100; i++) {40       printf("value:%d\t",g_abc[i]);41       if (i%10 == 0 && i != 0) {42          printf("\n");43       }44    }45    return g_abc[12];46 }

很不幸,这个翻车了,就算我把循环改成了100也是可以正常处理的,另外在gcc下没有告警,在clang中倒是在编译的时候就给出了告警信息:

clang: warning: argument unused during compilation: '-u se-after-free'
main.c:45:11: warning: array index 12 is past the end of the array (which contains 11 elements) [-Warray-bounds]return g_abc[12];^     ~~
main.c:5:1: note: array 'g_abc' declared here
int g_abc[11];

编译器改成g++,编译命令如下:

g++    -fsanitize=address  -fno-omit-frame-pointer -fsanitize=leak    -use-after-free  -g main.c -o t1

再次执行的时候报错:

[root@localhost tests]# ./t1
value:0 value:0 value:0 value:0 value:0 value:0 value:0 value:0 value:0 value:0 value:0
=================================================================
==25944==ERROR: AddressSanitizer: global-buffer-overflow on address 0x00000060224c at pc 0x401079 bp 0x7ffce4e9ffe0 sp 0x7ffce4e9ffd8
READ of size 4 at 0x00000060224c thread T0#0 0x401078 in func3() /home/miaohq/tests/main.c:40#1 0x401299 in main /home/miaohq/tests/main.c:60#2 0x7f3727c92444 in __libc_start_main (/lib64/libc.so.6+0x22444)#3 0x400a68 (/home/miaohq/tests/t1+0x400a68)0x00000060224c is located 0 bytes to the right of global variable 'g_abc' from 'main.c' (0x602220) of size 44
SUMMARY: AddressSanitizer: global-buffer-overflow /home/miaohq/tests/main.c:40 func3()

说明:

  1. 错误类型:global-buffer-overflow

  2. 错误原因:READ of size 4 at 0x00000060224c thread T0 即发生了越界读

  3. 错误位置:#0 0x401078 in func3() /home/miaohq/tests/main.c:40

3.5 内存泄漏

内存泄漏代码:

 47 int func4(void)48 {49    char * p = (char*) malloc(5);50    memset(p,0x0,5);51    memcpy(p,"1234",4),52    printf("%s\n",p);53 }

在我gcc的4.9.4版本情况下,如果按照上述编译和运行后,并没有看到任何内存泄漏的提示, 在clang的编译器下,可以通过:

ASAN_OPTIONS=detect_leaks=1 ./t1

运行程序来显示内存泄漏,但是我的gcc的版本还是低了,还是无法显示内存泄漏,索性直接升级到最新版本,升级过程太慢,这个无法忍受,我还是先用clang编译器测试下。

yum install clang -y
clang    -fsanitize=address  -fno-omit-frame-pointer -fsanitize=leak    -use-after-free  -g main.c -o t1
ASAN_OPTIONS=detect_leaks=1 ./t1

显示信息如下:

root@localhost tests]# ASAN_OPTIONS=detect_leaks=1 ./t1
1234
=================================================================
==108674==ERROR: LeakSanitizer: detected memory leaksDirect leak of 5 byte(s) in 1 object(s) allocated from:#0 0x465229 in __interceptor_malloc (/home/miaohq/tests/t1+0x465229)#1 0x47c59e in func4 /home/miaohq/tests/main.c:49#2 0x47cabb in main /home/miaohq/tests/main.c:58#3 0x7efd6b207444 in __libc_start_main (/lib64/libc.so.6+0x22444)SUMMARY: AddressSanitizer: 5 byte(s) leaked in 1 allocation(s).

说明:

  1. 显示错误原因为:detected memory leaks

  2. 被泄漏的内存:Direct leak of 5 byte(s) in 1 object(s) allocated from

  3. 泄漏的具体位置:0x47c59e in func4 /home/miaohq/tests/main.c:49

  4. 总结信息:AddressSanitizer: 5 byte(s) leaked in 1 allocation(s).

提示:有时候我们编译程序通过-g 或-ggdb调试仍然有问题,只显示地址,这里面可以试试这个办法:

[root@localhost tests]# addr2line -a -C -e ./t1 0x47c46b
0x000000000047c46b
/home/miaohq/tests/main.c:51

四 升级gcc到最新版本

按照上面的方法升级到9.3.0,运行缺少库:安装:

./bin/xxx: error while loading shared libraries: libasan.so.5: cannot open shared object file: No such file or directory
## 解决办法
yum install centos-release-scl-rh
yum --enablerepo=centos-sclo-rh-testing install libasan5

五 诗词鉴赏

永遇乐·京口北固亭怀古
[宋] 辛弃疾
千古江山,英雄无觅,孙仲谋处。
舞榭歌台,风流总被、雨打风吹去。
斜阳草树,寻常巷陌,
人道寄奴曾住。
想当年,金戈铁马,气吞万里如虎。元嘉草草,封狼居胥,
赢得仓皇北顾。
四十三年,望中犹记,烽火扬州路。
可堪回首,佛狸祠下,
一片神鸦社鼓。
凭谁问:廉颇老矣,尚能饭否?

内存泄漏试试AScan相关推荐

  1. 掘地三尺,我挖出了这次线上事故的元凶:Java内存泄漏!

    来源:http://www.importnew.com/29591.html 一个凄凉的午夜 一定是网络的问题 一定是 GC 的问题 一定是内存泄漏 真有这么糟糕吗? 还是不行 那现在是怎么回事? 修 ...

  2. 一次 Java 内存泄漏排查过程,学习学习

    人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...

  3. 一次毕生难忘的 Java 内存泄漏排查经历

    作者:yizhe 原文:http://www.importnew.com/29591.html 人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身 ...

  4. js内存泄漏常见的四种情况(From LeuisKen)

    本文主要选取了4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them 这篇文章中的一小部分来说明一下js中产生内存泄漏的常见情 ...

  5. C++ 动态内存管理:c/c++的动态内存管理,new/delete,operator new/delete,placement-new, 内存泄漏

    c/c++的动态内存管理 new/delete opeartor new/delete placement-new 内存泄漏 c/c++的动态内存管理 在开始之前首先要了解c和c++的内存分布,我简单 ...

  6. ReactiveCocoa中潜在的内存泄漏及解决方案

    ReactiveCocoa是GitHub开源的一个函数响应式编程框架,目前在美团App中大量使用.用过它的人都知道很好用,也确实为我们的生活带来了很多便利,特别是跟MVVM模式结合使用,更是如鱼得水. ...

  7. haddler处理队列 netty_Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  8. python项目“内存泄漏”的调试过程

    现象: 在压测的过程中,服务消耗的内存不断飙升,使用的内存大大超过了它可能消耗的内存大小 首先是内存泄漏的几个可能原因: 1.存在循环引用,gc不能释放: 2.存在全局对象,该对象不断的变大,占据内存 ...

  9. 一次 Java 内存泄漏排查过程,涨姿势

    人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...

最新文章

  1. Leetcode(18)-四数之和
  2. 基于Flink秒级计算时CPU监控图表数据中断问题
  3. 创建与合并分支-git入门教程
  4. Trapper: Transformer模型都在此!
  5. Laravel添加验证场景提高针对性质的验证
  6. mac 配置/etc/profile重启后不生效
  7. 计算机二级access知识点6,2019年计算机二级ACCESS考试知识点:关系数据模型
  8. TCP三次握手的序列号和确认号
  9. figma应用——面向用户的咖啡点单APP的设计过程记录
  10. 插图 引用 同一行两个插图_数学身份的优雅LED插图
  11. 读书笔记——计算机基础之操作系统
  12. Java定义全局变量的方法
  13. .NET C#到Java没那么难,MVC篇
  14. java用if怎么编程税收_Java - 使用If和if else语句的简单税计算器
  15. 关于COM类工厂80070005和8000401a错误分析及解决办法
  16. 信息安全原理与技术第七次实验:木马攻击与防范
  17. 华硕A55V,终于解决了无线网指示灯不亮的问题。
  18. x / k向上取整转换为向下取整
  19. html th中加斜杠,css 模拟表格斜线
  20. 理解 PHP 8 的 JIT

热门文章

  1. 爬虫快速入门——Request对象的使用
  2. springboot整合rocketmq,支持多连接生产者和消费者配置。不同topic适配不同业务处理类
  3. C语言——利用递归函数完成斐波拉契前n项的计算
  4. centos8使用技巧与阿贝云服务器
  5. php将一组数从小到大排序,php数组排序从小到大函数
  6. 和吴昊一起玩推理(第二季首映式)Round 11 —— 从无有到无穷
  7. CainAbel工具的下载和使用
  8. 蜘蛛爬行html语言的顺序,SEO优化 蜘蛛的爬行规则以及让蜘蛛爬行的快速办法
  9. 物联网四大支撑网络,物联网发展三个阶段
  10. 容斥原理之求区间中与某数互质的个数