内存泄漏试试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

二 使用方法

  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>
  3
  4 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:5

  This 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 leaks

Direct 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

五 诗词鉴赏

永遇乐·京口北固亭怀古
[宋] 辛弃疾
千古江山,英雄无觅,孙仲谋处。
舞榭歌台,风流总被、雨打风吹去。
斜阳草树,寻常巷陌,
人道寄奴曾住。
想当年,金戈铁马,气吞万里如虎。

元嘉草草,封狼居胥,
赢得仓皇北顾。
四十三年,望中犹记,烽火扬州路。
可堪回首,佛狸祠下,
一片神鸦社鼓。
凭谁问:廉颇老矣,尚能饭否?
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在gprMax项目中,使用gprMax.py文件可以进行地质雷达数据的模拟和处理。首先,你需要通过命令行进入gprMax文件夹的路径,并激活gprMax环境。\[1\]然后,gprMax.py文件中的代码被分解为多个模块的导入,包括argparse、datetime、os、platform、sys等等。这些模块提供了一些必要的功能和工具,用于模拟和处理地质雷达数据。\[2\]在main()函数中,你可以找到gprMax.py文件的主要逻辑。最后,run_main()函数中调用了run_opt_sim()函数,该函数并不在gprMax.py文件中,而是从gprMax.optimisation_taguchi模块中导入的。\[3\] 关于"gprmax ascan"的问题,我需要更多的上下文信息才能给出具体的回答。请提供更多关于"gprmax ascan"的背景和具体问题的细节。 #### 引用[.reference_title] - *1* [Gprmax](https://blog.csdn.net/weixin_47779606/article/details/129438514)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [gprMax项目代码分解:gprMax.py](https://blog.csdn.net/xuan373366788/article/details/128660090)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值