内存泄漏试试AScan
我用最多的排查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
二 使用方法
确保自己的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:
....
说明:
错误类型是 stack-buffer-overflow
不合法操作WRITE发生在线程T0 WRITE of size 5 at 0x7ffed0cfce50 thread T0
具体发生的位置:/home/miaohq/tests/main.c:7
后面还有影子内存一些指示,后续再开一篇聊下,主要我也不熟悉哈哈:)。
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)
说明:
错误类型:heap-buffer-overflow
错误原因:WRITE of size 5 at 0x60200000eff0 thread T0
发生位置: #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
说明:
错误类型:heap-use-after-free
错误原因:WRITE of size 4 at 0x60200000eff0 thread T0
发生位置:#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()
说明:
错误类型:global-buffer-overflow
错误原因:READ of size 4 at 0x00000060224c thread T0 即发生了越界读
错误位置:#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).
说明:
显示错误原因为:detected memory leaks
被泄漏的内存:Direct leak of 5 byte(s) in 1 object(s) allocated from
泄漏的具体位置:0x47c59e in func4 /home/miaohq/tests/main.c:49
总结信息: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相关推荐
- 掘地三尺,我挖出了这次线上事故的元凶:Java内存泄漏!
来源:http://www.importnew.com/29591.html 一个凄凉的午夜 一定是网络的问题 一定是 GC 的问题 一定是内存泄漏 真有这么糟糕吗? 还是不行 那现在是怎么回事? 修 ...
- 一次 Java 内存泄漏排查过程,学习学习
人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...
- 一次毕生难忘的 Java 内存泄漏排查经历
作者:yizhe 原文:http://www.importnew.com/29591.html 人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身 ...
- js内存泄漏常见的四种情况(From LeuisKen)
本文主要选取了4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them 这篇文章中的一小部分来说明一下js中产生内存泄漏的常见情 ...
- C++ 动态内存管理:c/c++的动态内存管理,new/delete,operator new/delete,placement-new, 内存泄漏
c/c++的动态内存管理 new/delete opeartor new/delete placement-new 内存泄漏 c/c++的动态内存管理 在开始之前首先要了解c和c++的内存分布,我简单 ...
- ReactiveCocoa中潜在的内存泄漏及解决方案
ReactiveCocoa是GitHub开源的一个函数响应式编程框架,目前在美团App中大量使用.用过它的人都知道很好用,也确实为我们的生活带来了很多便利,特别是跟MVVM模式结合使用,更是如鱼得水. ...
- haddler处理队列 netty_Netty堆外内存泄漏排查,这一篇全讲清楚了
上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...
- python项目“内存泄漏”的调试过程
现象: 在压测的过程中,服务消耗的内存不断飙升,使用的内存大大超过了它可能消耗的内存大小 首先是内存泄漏的几个可能原因: 1.存在循环引用,gc不能释放: 2.存在全局对象,该对象不断的变大,占据内存 ...
- 一次 Java 内存泄漏排查过程,涨姿势
人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...
最新文章
- Leetcode(18)-四数之和
- 基于Flink秒级计算时CPU监控图表数据中断问题
- 创建与合并分支-git入门教程
- Trapper: Transformer模型都在此!
- Laravel添加验证场景提高针对性质的验证
- mac 配置/etc/profile重启后不生效
- 计算机二级access知识点6,2019年计算机二级ACCESS考试知识点:关系数据模型
- TCP三次握手的序列号和确认号
- figma应用——面向用户的咖啡点单APP的设计过程记录
- 插图 引用 同一行两个插图_数学身份的优雅LED插图
- 读书笔记——计算机基础之操作系统
- Java定义全局变量的方法
- .NET C#到Java没那么难,MVC篇
- java用if怎么编程税收_Java - 使用If和if else语句的简单税计算器
- 关于COM类工厂80070005和8000401a错误分析及解决办法
- 信息安全原理与技术第七次实验:木马攻击与防范
- 华硕A55V,终于解决了无线网指示灯不亮的问题。
- x / k向上取整转换为向下取整
- html th中加斜杠,css 模拟表格斜线
- 理解 PHP 8 的 JIT