Memory leak refers to the failure of the program to release memory that is no longer used due to negligence or error. Memory leakage does not mean that there is a physical disappearance in the memory, but that after the application allocates a certain section of memory, due to design errors, the control of the section of memory is lost before releasing the section of memory, resulting in a waste of memory.
We will inevitably encounter memory leakage in the process of development. How do you check it? I guess you use the following tools?
-
valgrind
-
mtrace
-
dmalloc
-
ccmalloc
-
memwatch
-
debug_new
Here, program meow recommends a new tool for checking memory leaks: address sanitizer (asan). This tool comes with gcc and can be used in versions above 4.8. It supports Linux, OS, Android and other platforms. It can not only detect memory leaks, but also is a memory error detection tool. The problems that can be detected include:
-
Memory leak
-
Stack and global memory out of bounds access
-
Continue to use after free
-
Local memory is used by the outer layer
-
Initialization order bugs
Use the following code directly:
Detect memory leaks
Memory leak Code:
#include <stdlib.h> void func1() { malloc(7); } void func2() { malloc(5); } int main() { func1(); func2(); return 0;}
Compile and output:
g++ -fsanitize=address -g test_leak.cc && ./a.out ===================================================================103==ERROR: LeakSanitizer: detected memory leaks Direct leak of 7 byte(s) in 1 object(s) allocated from: #0 0x7f95b231eb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40) #1 0x7f95b36007f7 in func1() /home/wangzhiqiang/test/test_leak.cc:3 #2 0x7f95b3600814 in main /home/wangzhiqiang/test/test_leak.cc:8 #3 0x7f95b1e61b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) Direct leak of 5 byte(s) in 1 object(s) allocated from: #0 0x7f95b231eb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40) #1 0x7f95b3600808 in func2() /home/wangzhiqiang/test/test_leak.cc:5 #2 0x7f95b3600819 in main /home/wangzhiqiang/test/test_leak.cc:9 #3 0x7f95b1e61b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) SUMMARY: AddressSanitizer: 12 byte(s) leaked in 2 allocation(s).
The compilation method is very simple. You only need to add - fsanitize=address -g to detect the specific location of memory leakage and the size of leakage space.
Detect stack memory out of bounds access
Example:
#include <iostream> int main() { int *array = new int[100]; array[0] = 0; int res = array[100]; // out of bounds delete[] array; return res;}
Compile and output:
g++ -fsanitize=address -g test_leak.cc && ./a.out ===================================================================110==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000001d0 at pc 0x7f0e06400d2e bp 0x7ffff5963f10 sp 0x7ffff5963f00READ of size 4 at 0x6140000001d0 thread T0 #0 0x7f0e06400d2d in main /home/wangzhiqiang/test/test_leak.cc:6 #1 0x7f0e048d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) #2 0x7f0e06400bb9 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xbb9) 0x6140000001d0 is located 0 bytes to the right of 400-byte region [0x614000000040,0x6140000001d0)allocated by thread T0 here: #0 0x7f0e05120608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608) #1 0x7f0e06400cab in main /home/wangzhiqiang/test/test_leak.cc:4 #2 0x7f0e048d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) SUMMARY: AddressSanitizer: heap-buffer-overflow /home/wangzhiqiang/test/test_leak.cc:6 in mainShadow bytes around the buggy address: 0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00 0x0c287fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00=>0x0c287fff8030: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa 0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa faShadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb==110==ABORTING
It is convenient to locate the error of out of bounds access to stack memory.
Global memory out of bounds access:
Example:
#include <iostream> int global_array[100] = {0}; int main() { int res = global_array[100]; // out of bounds return 0;}
Compile and output:
g++ -fsanitize=address -g test_leak.cc && ./a.out===================================================================116==ERROR: AddressSanitizer: global-buffer-overflow on address 0x7f42e6e02310 at pc 0x7f42e6c00c84 bp 0x7fffdda52780 sp 0x7fffdda52770READ of size 4 at 0x7f42e6e02310 thread T0 #0 0x7f42e6c00c83 in main /home/wangzhiqiang/test/test_leak.cc:6 #1 0x7f42e50d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) #2 0x7f42e6c00b69 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb69) 0x7f42e6e02310 is located 0 bytes to the right of global variable 'global_array' defined in 'test_leak.cc:3:5' (0x7f42e6e02180) of size 400SUMMARY: AddressSanitizer: global-buffer-overflow /home/wangzhiqiang/test/test_leak.cc:6 in mainShadow bytes around the buggy address: 0x0fe8dcdb8410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe8dcdb8420: 00 00 00 00 00 00 00 00 01 f9 f9 f9 f9 f9 f9 f9 0x0fe8dcdb8430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe8dcdb8440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe8dcdb8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00=>0x0fe8dcdb8460: 00 00[f9]f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00 0x0fe8dcdb8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe8dcdb8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe8dcdb8490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe8dcdb84a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe8dcdb84b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb==116==ABORTING
Local memory is used by the outer layer
Example:
#include <iostream> volatile int *p = 0; int main() {{ int x = 0; p = &x;} *p = 5; return 0;}
Compile and output:
g++ -fsanitize=address -g test_leak.cc && ./a.out===================================================================243==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fffce12a4b0 at pc 0x7f3993e00e7e bp 0x7fffce12a480 sp 0x7fffce12a470WRITE of size 4 at 0x7fffce12a4b0 thread T0 #0 0x7f3993e00e7d in main /home/wangzhiqiang/test/test_leak.cc:10 #1 0x7f39922d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) #2 0x7f3993e00c89 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xc89) Address 0x7fffce12a4b0 is located in stack of thread T0 at offset 32 in frame #0 0x7f3993e00d79 in main /home/wangzhiqiang/test/test_leak.cc:5 This frame has 1 object(s): [32, 36) 'x' <== Memory access at offset 32 is inside this variableHINT: 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-use-after-scope /home/wangzhiqiang/test/test_leak.cc:10 in mainShadow bytes around the buggy address:0x100079c1d440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00=>0x100079c1d490: 00 00 f1 f1 f1 f1[f8]f2 f2 f2 00 00 00 00 00 000x100079c1d4a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x100079c1d4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00Shadow byte legend (one shadow byte represents 8 application bytes):Addressable: 00Partially addressable: 01 02 03 04 05 06 07Heap left redzone: faFreed heap region: fdStack left redzone: f1Stack mid redzone: f2Stack right redzone: f3Stack after return: f5Stack use after scope: f8Global redzone: f9Global init order: f6Poisoned by user: f7Container overflow: fcArray cookie: acIntra object redzone: bbASan internal: feLeft alloca redzone: caRight alloca redzone: cb==243==ABORTING
Used after free
Example:
#include <iostream> int main() { int *array = new int[100]; delete[] array; int a = array[0]; // error return 0;}
Compile and output:
g++ -fsanitize=address -g test_leak.cc && ./a.out===================================================================282==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000040 at pc 0x7f209fa00caa bp 0x7ffff2a15020 sp 0x7ffff2a15010READ of size 4 at 0x614000000040 thread T0 #0 0x7f209fa00ca9 in main /home/wangzhiqiang/test/test_leak.cc:6 #1 0x7f209ded1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) #2 0x7f209fa00b69 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb69) 0x614000000040 is located 0 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)freed by thread T0 here: #0 0x7f209e721480 in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe1480) #1 0x7f209fa00c72 in main /home/wangzhiqiang/test/test_leak.cc:5 #2 0x7f209ded1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) previously allocated by thread T0 here: #0 0x7f209e720608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608) #1 0x7f209fa00c5b in main /home/wangzhiqiang/test/test_leak.cc:4 #2 0x7f209ded1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) SUMMARY: AddressSanitizer: heap-use-after-free /home/wangzhiqiang/test/test_leak.cc:6 in mainShadow bytes around the buggy address: 0x0c287fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00=>0x0c287fff8000: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd 0x0c287fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c287fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c287fff8030: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa 0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa faShadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb==282==ABORTING
Initialization order bugs
For example, here are two files:
// test_memory1.cc#include <stdio.h> extern int extern_global;int read_extern_global() { return extern_global; } int x = read_extern_global() + 1; int main() { printf("%d\n", x); return 0;}
// test_memory2.cc int foo() { return 123; }int extern_global = foo();
The output of the first compilation method is as follows:
g++ test_memory1.cc test_memory2.cc && ./a.out1
The output of the second compilation method is as follows:
g++ test_memory2.cc test_memory1.cc && ./a.out124
We usually don't pay much attention to this problem in the programming process. However, this potential bug can be detected through ASan:
Compile and output:
g++ -fsanitize=address -g test_memory1.cc test_memory2.cc ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true ./a.out===================================================================419==ERROR: AddressSanitizer: initialization-order-fiasco on address 0x7f46c20021a0 at pc 0x7f46c1e00c28 bp 0x7fffe423d920 sp 0x7fffe423d910READ of size 4 at 0x7f46c20021a0 thread T0 #0 0x7f46c1e00c27 in read_extern_global() /home/wangzhiqiang/test/test_memory1.cc:3 #1 0x7f46c1e00cb3 in __static_initialization_and_destruction_0 /home/wangzhiqiang/test/test_memory1.cc:4 #2 0x7f46c1e00d0a in _GLOBAL__sub_I__Z18read_extern_globalv /home/wangzhiqiang/test/test_memory1.cc:8 #3 0x7f46c1e00e5c in __libc_csu_init (/mnt/d/wzq/wzq/util/test/a.out+0xe5c) #4 0x7f46c0461b27 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b27) #5 0x7f46c1e00b09 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb09) 0x7f46c20021a0 is located 0 bytes inside of global variable 'extern_global' defined in 'test_memory2.cc:2:5' (0x7f46c20021a0) of size 4 registered at: #0 0x7f46c08764a8 (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x364a8) #1 0x7f46c1e00e0b in _GLOBAL__sub_I_00099_1__Z3foov (/mnt/d/wzq/wzq/util/test/a.out+0xe0b) #2 0x7f46c1e00e5c in __libc_csu_init (/mnt/d/wzq/wzq/util/test/a.out+0xe5c) SUMMARY: AddressSanitizer: initialization-order-fiasco /home/wangzhiqiang/test/test_memory1.cc:3 in read_extern_global()Shadow bytes around the buggy address: 0x0fe9583f83e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f83f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f8400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f8410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f8420: 00 00 00 00 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9=>0x0fe9583f8430: 00 00 00 00[f6]f6 f6 f6 f6 f6 f6 f6 00 00 00 00 0x0fe9583f8440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f8460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0fe9583f8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb==419==ABORTING
Note: you need to add environment variables before running the program:
ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true
Summary
ASan is a good tool to detect memory problems. It does not need to configure the environment and is easy to use. It only needs - fsanitize=address -g when compiling. You can choose to add the corresponding ASan when running the program_ The options environment variable can detect many memory problems. Its error message is also very useful to clearly indicate what type of memory error is currently, such as:
-
detected memory leaks
-
heap-buffer-overflow
-
stack-buffer-overflow
-
global-buffer-overflow
-
heap-use-after-free
-
initialization-order-fiasco
See Google's official documents for details: https://github.com/google/sanitizers/wiki/AddressSanitizer