Linux 检测内存泄漏

内存泄漏是指由于疏忽或错误造成程序未能释放已经不再使用的内存。 内存泄漏并非指内存在物理上的消失, 而是应用程序分
配某段内存后, 由于设计错误, 导致在释放该段内存之前就失去了对该段内存的控制, 从而造成了内存的浪费。我们平时开发过程中不可避免的会遇到内存泄漏问题,经常使用下面几个工具。

  • valgrind
  • mtrace
  • dmalloc
  • ccmalloc
  • memwatch
  • debug_new

这里我看到过一个新的一个排查内存泄漏的工具: AddressSanitizer(ASan), 该工具为 gcc 自带, 4.8 以上版本都可以使用,
支持 Linux、 OS、 Android 等多种平台, 不止可以检测内存泄漏, 它其实是一个内存错误检测工具, 可以检测的问题有:

  • 内存泄漏
  • 堆栈和全局内存越界访问
  • free 后继续使用
  • 局部内存被外层使用
  • Initialization order bugs

检测内存泄漏

1. #include <stdlib.h>
2.
3. void func1() { malloc(7); }
4.
5. void func2() { malloc(5); }
6.
7. int main() {
8. func1();
9. func2();
10. return 0;
11. }

编译 and 输出:

1. g++ -fsanitize=address -g test_leak.cc && ./a.out
2.
3. =================================================================
4. ==103==ERROR: LeakSanitizer: detected memory leaks
5.
6. Direct leak of 7 byte(s) in 1 object(s) allocated from:
7. #0 0x7f95b231eb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
8. #1 0x7f95b36007f7 in func1() /home/wangzhiqiang/test/test_leak.cc:3
9. #2 0x7f95b3600814 in main /home/wangzhiqiang/test/test_leak.cc:8
10.#3 0x7f95b1e61b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
11.
12. Direct leak of 5 byte(s) in 1 object(s) allocated from:
13. #0 0x7f95b231eb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
14. #1 0x7f95b3600808 in func2() /home/wangzhiqiang/test/test_leak.cc:5
15. #2 0x7f95b3600819 in main /home/wangzhiqiang/test/test_leak.cc:9
16. #3 0x7f95b1e61b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
17.
18. SUMMARY: AddressSanitizer: 12 byte(s) leaked in 2 allocation(s).

编译方式很简单, 只需要添加 -fsanitize=address -g 就可以检测出具体产生内存泄漏的位置以及泄漏空间的大小

检测堆栈内存越界访问

1. #include <iostream>
2.
2. int main() {
3. int *array = new int[100];
4. array[0] = 0;
5. int res = array[100]; // out of bounds
6. delete[] array;
7. return res;
8. }

编译 and 输出:

9. g++ -fsanitize=address -g test_leak.cc && ./a.out
2.
10. =================================================================
11. ==110==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000001d0 at pc 0x7f0e06400d2e bp 0x7ffff5963f10 sp 0x7f
fff5963f00
12. READ of size 4 at 0x6140000001d0 thread T0
13. #0 0x7f0e06400d2d in main /home/wangzhiqiang/test/test_leak.cc:6
14. #1 0x7f0e048d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
15. #2 0x7f0e06400bb9 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xbb9)
9.
16. 0x6140000001d0 is located 0 bytes to the right of 400-byte region [0x614000000040,0x6140000001d0)
17. allocated by thread T0 here:
18. #0 0x7f0e05120608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608)
19. #1 0x7f0e06400cab in main /home/wangzhiqiang/test/test_leak.cc:4
20. #2 0x7f0e048d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
15.
21. SUMMARY: AddressSanitizer: heap-buffer-overflow /home/wangzhiqiang/test/test_leak.cc:6 in main
22. Shadow bytes around the buggy address:
23. 0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
24. 0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
25. 0x0c287fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
26. 0x0c287fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
27. 0x0c287fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
28. =>0x0c287fff8030: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
29. 0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
30. 0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
31. 0x0c287fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
32. 0x0c287fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
33. 0x0c287fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
34. Shadow byte legend (one shadow byte represents 8 application bytes):
35. Addressable: 00
36. Partially addressable: 01 02 03 04 05 06 07
37. Heap left redzone: fa
38. Freed heap region: fd
39. Stack left redzone: f1
40. Stack mid redzone: f2
41. Stack right redzone: f3
42. Stack after return: f5
43. Stack use after scope: f8
44. Global redzone: f9
45. Global init order: f6
46. Poisoned by user: f7
47. Container overflow: fc
48. Array cookie: ac
49. Intra object redzone: bb
50. ASan internal: fe
51. Left alloca redzone: ca
52. Right alloca redzone: cb
53. ==110==ABORTING

全局内存越界访问:

54. #include <iostream>
2.
55. int global_array[100] = {0};
4.
56. int main() {
57. int res = global_array[100]; // out of bounds
58. return 0;
59. }

编译 and 输出:

1. g++ -fsanitize=address -g test_leak.cc && ./a.out
2. =================================================================
3. ==116==ERROR: AddressSanitizer: global-buffer-overflow on address 0x7f42e6e02310 at pc 0x7f42e6c00c84 bp 0x7fffdda52780 sp 0x
7fffdda52770
4. READ of size 4 at 0x7f42e6e02310 thread T0
5. #0 0x7f42e6c00c83 in main /home/wangzhiqiang/test/test_leak.cc:6
6. #1 0x7f42e50d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
7. #2 0x7f42e6c00b69 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb69)
8.
9. 0x7f42e6e02310 is located 0 bytes to the right of global variable 'global_array' defined in 'test_leak.cc:3:5' (0x7f42e6e0218
0) of size 400
10. SUMMARY: AddressSanitizer: global-buffer-overflow /home/wangzhiqiang/test/test_leak.cc:6 in main
11. Shadow bytes around the buggy address:
12. 0x0fe8dcdb8410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
13. 0x0fe8dcdb8420: 00 00 00 00 00 00 00 00 01 f9 f9 f9 f9 f9 f9 f9
14. 0x0fe8dcdb8430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
15. 0x0fe8dcdb8440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
16. 0x0fe8dcdb8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
17. =>0x0fe8dcdb8460: 00 00[f9]f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00
18. 0x0fe8dcdb8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
19. 0x0fe8dcdb8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20. 0x0fe8dcdb8490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
21. 0x0fe8dcdb84a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
22. 0x0fe8dcdb84b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
23. Shadow byte legend (one shadow byte represents 8 application bytes):
24. Addressable: 00
25. Partially addressable: 01 02 03 04 05 06 07
26. Heap left redzone: fa
27. Freed heap region: fd
28. Stack left redzone: f1
29. Stack mid redzone: f2
30. Stack right redzone: f3
31. Stack after return: f5
32. Stack use after scope: f8
33. Global redzone: f9
34. Global init order: f6
35. Poisoned by user: f7
36. Container overflow: fc
37. Array cookie: ac
38. Intra object redzone: bb
39. ASan internal: fe
40. Left alloca redzone: ca
41. Right alloca redzone: cb
42. ==116==ABORTING
 

局部内存被外层使用

1. #include <iostream>
2.
2. volatile int *p = 0;
4.
3. int main() {
4. {
5. int x = 0;
6. p = &x;
7. }
8. *p = 5;
9. return 0;
10. }
1. g++ -fsanitize=address -g test_leak.cc && ./a.out
2. =================================================================
3. ==243==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fffce12a4b0 at pc 0x7f3993e00e7e bp 0x7fffce12a480 sp 0x7
fffce12a470
4. WRITE of size 4 at 0x7fffce12a4b0 thread T0
5. #0 0x7f3993e00e7d in main /home/wangzhiqiang/test/test_leak.cc:10
6. #1 0x7f39922d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
7. #2 0x7f3993e00c89 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xc89)
8.
9. Address 0x7fffce12a4b0 is located in stack of thread T0 at offset 32 in frame
10. #0 0x7f3993e00d79 in main /home/wangzhiqiang/test/test_leak.cc:5
11.
12. This frame has 1 object(s):
13. [32, 36) 'x' <== Memory access at offset 32 is inside this variable
14. HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
15. (longjmp and C++ exceptions *are* supported)
16. SUMMARY: AddressSanitizer: stack-use-after-scope /home/wangzhiqiang/test/test_leak.cc:10 in main
17. Shadow bytes around the buggy address:
18. 0x100079c1d440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
19. 0x100079c1d450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20. 0x100079c1d460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
21. 0x100079c1d470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
22. 0x100079c1d480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
23. =>0x100079c1d490: 00 00 f1 f1 f1 f1[f8]f2 f2 f2 00 00 00 00 00 00
24. 0x100079c1d4a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
25. 0x100079c1d4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
26. 0x100079c1d4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
27. 0x100079c1d4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
28. 0x100079c1d4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
29. Shadow byte legend (one shadow byte represents 8 application bytes):
30. Addressable: 00
31. Partially addressable: 01 02 03 04 05 06 07
32. Heap left redzone: fa
33. Freed heap region: fd
34. Stack left redzone: f1
35. Stack mid redzone: f2
36. Stack right redzone: f3
37. Stack after return: f5
38. Stack use after scope: f8
39. Global redzone: f9
40. Global init order: f6
41. Poisoned by user: f7
42. Container overflow: fc
43. Array cookie: ac
44. Intra object redzone: bb
45. ASan internal: fe
46. Left alloca redzone: ca
47. Right alloca redzone: cb
48. ==243==ABORTING 

Initialization order bugs
这里有两个文件

1. // test_memory1.cc
2. #include <stdio.h>
3.
4. extern int extern_global;
5. int read_extern_global() { return extern_global; }
6.
7. int x = read_extern_global() + 1;
8.
9. int main() {
10. printf("%d\n", x);11. return 0;
12. }
1. // test_memory2.cc
2.
3. int foo() { return 123; }
4. int extern_global = foo();

第一种编译方式:

1. g++ test_memory1.cc test_memory2.cc && ./a.out
2. 1

第二种编译方式

1. g++ test_memory2.cc test_memory1.cc && ./a.out
2. 124
1. g++ -fsanitize=address -g test_memory1.cc test_memory2.cc
2.
3. ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true ./a.out
4. =================================================================
5. ==419==ERROR: AddressSanitizer: initialization-order-fiasco on address 0x7f46c20021a0 at pc 0x7f46c1e00c28 bp 0x7fffe423d920
sp 0x7fffe423d910
6. READ of size 4 at 0x7f46c20021a0 thread T0
7. #0 0x7f46c1e00c27 in read_extern_global() /home/wangzhiqiang/test/test_memory1.cc:3
8. #1 0x7f46c1e00cb3 in __static_initialization_and_destruction_0 /home/wangzhiqiang/test/test_memory1.cc:4
9. #2 0x7f46c1e00d0a in _GLOBAL__sub_I__Z18read_extern_globalv /home/wangzhiqiang/test/test_memory1.cc:8
10. #3 0x7f46c1e00e5c in __libc_csu_init (/mnt/d/wzq/wzq/util/test/a.out+0xe5c)
11. #4 0x7f46c0461b27 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b27)
12. #5 0x7f46c1e00b09 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb09)
13.
14. 0x7f46c20021a0 is located 0 bytes inside of global variable 'extern_global' defined in 'test_memory2.cc:2:5' (0x7f46c20021a0)
of size 4
15. registered at:
16. #0 0x7f46c08764a8 (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x364a8)
17. #1 0x7f46c1e00e0b in _GLOBAL__sub_I_00099_1__Z3foov (/mnt/d/wzq/wzq/util/test/a.out+0xe0b)
18. #2 0x7f46c1e00e5c in __libc_csu_init (/mnt/d/wzq/wzq/util/test/a.out+0xe5c)
19.
20. SUMMARY: AddressSanitizer: initialization-order-fiasco /home/wangzhiqiang/test/test_memory1.cc:3 in read_extern_global()
21. Shadow bytes around the buggy address:
22. 0x0fe9583f83e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
23. 0x0fe9583f83f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
24. 0x0fe9583f8400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
25. 0x0fe9583f8410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
26. 0x0fe9583f8420: 00 00 00 00 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9
27. =>0x0fe9583f8430: 00 00 00 00[f6]f6 f6 f6 f6 f6 f6 f6 00 00 00 00
28. 0x0fe9583f8440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
29. 0x0fe9583f8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30. 0x0fe9583f8460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
31. 0x0fe9583f8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
32. 0x0fe9583f8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
33. Shadow byte legend (one shadow byte represents 8 application bytes):
34. Addressable: 00
35. Partially addressable: 01 02 03 04 05 06 07
36. Heap left redzone: fa
37. Freed heap region: fd
38. Stack left redzone: f1
39. Stack mid redzone: f240. Stack right redzone: f3
41. Stack after return: f5
42. Stack use after scope: f8
43. Global redzone: f9
44. Global init order: f6
45. Poisoned by user: f7
46. Container overflow: fc
47. Array cookie: ac
48. Intra object redzone: bb
49. ASan internal: fe
50. Left alloca redzone: ca
51. Right alloca redzone: cb
52. ==419==ABORTING

注意: 这里在运行程序前需要添加环境变量:

1. ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true

具体可以看 google 的官方文档: https://github.com/google/sanitizers/wiki/AddressSanitizer

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值