eBPF检测 mmap泄露"/>
使用 eBPF检测 mmap泄露
目录
背景
官网
malloc泄露检测
mmap泄露检测
调用munmap释放内存
小结
背景
我们知道 mmap系统调用申请的内存空间,属于文件映射区域 和 匿名映射区域。这部分区域并不属于 heap,所以用一般的内存泄露检测工具是检测不出来的。例如:一般常用的内存泄露检测工具 vagrind、ASAN、malloc_debug等。关于ASAN的介绍,可以参考:ASAN入门参考-CSDN博客
官网
.py
可以看到,基于eBPF的BCC工具是支持以下内存申请接口,进行内存泄露检测的。
attach_probes("malloc")attach_probes("calloc")attach_probes("realloc")attach_probes("mmap")attach_probes("posix_memalign")attach_probes("valloc", can_fail=True) # failed on Android, is deprecated in libc.so from bionic directoryattach_probes("memalign")attach_probes("pvalloc", can_fail=True) # failed on Android, is deprecated in libc.so from bionic directoryattach_probes("aligned_alloc", can_fail=True) # added in C11
我们先来看看最常见的 malloc 的检测。
malloc泄露检测
test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>void allocate_mmap_memory()
{int *p=mmap(NULL,//系统指定首地址getpagesize(),//一个页(基本单位)PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,//匿名映射0,0);//可以在这一页里随便折腾printf("mmap address p : %p\n",p);*p=20;*(p+1)=30;*(p+2)=40;munmap(p,4096);//释放内存
}void a()
{int* p = malloc(5);printf("malloc address p : %p\n",p);//allocate_mmap_memory();//free(p);
}void b()
{a();
}void c()
{b();
}int main(int argc,char* argv[])
{while(1){sleep(15);c();}return 0;
}
编译运行:
wj@wj:~/linux$ gcc test.c -o test.out
wj@wj:~/linux$ ./test.out
malloc address p : 0x561051d3a2a0
malloc address p : 0x561051d3a6d0
malloc address p : 0x561051d3a6f0
malloc address p : 0x561051d3a710
^C
wj@wj:~/linux$ ps -ef | grep test
kernoops 1078 1 0 19:33 ? 00:00:00 /usr/sbin/kerneloops --test
wj 4861 2537 0 21:52 pts/0 00:00:00 ./test.out
wj 4863 3794 0 21:52 pts/2 00:00:00 grep --color=auto test
sudo python3 memleak -a -p 4861
# -a 表示显示每个内存分配请求的大小以及地址
# -p 指定案例应用的 PID 号
wj@wj:/usr/share/bcc/tools$ sudo python3 memleak -a -p 4861
[sudo] wj 的密码:
Attaching to pid 4861, Ctrl+C to quit.
[21:52:46] Top 10 stacks with outstanding allocations:
[21:52:51] Top 10 stacks with outstanding allocations:
[21:52:56] Top 10 stacks with outstanding allocations:addr = 561051d3a6f0 size = 55 bytes in 1 allocations from stack0x0000561051aec28e a+0x16 [test.out]0x0000561051aec2c2 b+0x12 [test.out]0x0000561051aec2d7 c+0x12 [test.out]0x0000561051aec301 main+0x27 [test.out]0x00007fa2fc829d90 __libc_start_call_main+0x80 [libc.so.6]
[21:53:01] Top 10 stacks with outstanding allocations:addr = 561051d3a6f0 size = 55 bytes in 1 allocations from stack0x0000561051aec28e a+0x16 [test.out]0x0000561051aec2c2 b+0x12 [test.out]0x0000561051aec2d7 c+0x12 [test.out]0x0000561051aec301 main+0x27 [test.out]0x00007fa2fc829d90 __libc_start_call_main+0x80 [libc.so.6]
[21:53:06] Top 10 stacks with outstanding allocations:addr = 561051d3a6f0 size = 55 bytes in 1 allocations from stack0x0000561051aec28e a+0x16 [test.out]0x0000561051aec2c2 b+0x12 [test.out]0x0000561051aec2d7 c+0x12 [test.out]0x0000561051aec301 main+0x27 [test.out]0x00007fa2fc829d90 __libc_start_call_main+0x80 [libc.so.6]
[21:53:11] Top 10 stacks with outstanding allocations:addr = 561051d3a6f0 size = 5addr = 561051d3a710 size = 510 bytes in 2 allocations from stack0x0000561051aec28e a+0x16 [test.out]0x0000561051aec2c2 b+0x12 [test.out]0x0000561051aec2d7 c+0x12 [test.out]0x0000561051aec301 main+0x27 [test.out]0x00007fa2fc829d90 __libc_start_call_main+0x80 [libc.so.6]
[21:53:16] Top 10 stacks with outstanding allocations:addr = 561051d3a6f0 size = 5addr = 561051d3a710 size = 510 bytes in 2 allocations from stack0x0000561051aec28e a+0x16 [test.out]0x0000561051aec2c2 b+0x12 [test.out]0x0000561051aec2d7 c+0x12 [test.out]0x0000561051aec301 main+0x27 [test.out]0x00007fa2fc829d90 __libc_start_call_main+0x80 [libc.so.6]
[21:53:21] Top 10 stacks with outstanding allocations:addr = 561051d3a6f0 size = 5addr = 561051d3a710 size = 510 bytes in 2 allocations from stack0x0000561051aec28e a+0x16 [test.out]0x0000561051aec2c2 b+0x12 [test.out]0x0000561051aec2d7 c+0x12 [test.out]0x0000561051aec301 main+0x27 [test.out]0x00007fa2fc829d90 __libc_start_call_main+0x80 [libc.so.6]
从 memleak 的输出可以看到,案例应用在不停地分配内存,并且这些分配的地址没有被回收。
test.c 加上释放函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>void allocate_mmap_memory()
{int *p=mmap(NULL,//系统指定首地址getpagesize(),//一个页(基本单位)PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,//匿名映射0,0);//可以在这一页里随便折腾printf("mmap address p : %p\n",p);*p=20;*(p+1)=30;*(p+2)=40;munmap(p,4096);//释放内存
}void a()
{int* p = malloc(5);printf("malloc address p : %p\n",p);//allocate_mmap_memory();free(p);
}void b()
{a();
}void c()
{b();
}int main(int argc,char* argv[])
{while(1){sleep(15);c();}return 0;
}
再次检测。
wj@wj:~/linux$ gcc test.c -o test.out
wj@wj:~/linux$ ./test.out
malloc address p : 0x558bfb5142a0
malloc address p : 0x558bfb5142a0
malloc address p : 0x558bfb5142a0
malloc address p : 0x558bfb5142a0
^C
wj@wj:~/linux$ ps -ef | grep test
kernoops 1078 1 0 19:33 ? 00:00:00 /usr/sbin/kerneloops --test
wj 5018 2537 0 22:07 pts/0 00:00:00 ./test.out
wj 5029 3794 0 22:08 pts/2 00:00:00 grep --color=auto test
wj@wj:~/linux$
wj@wj:/usr/share/bcc/tools$ sudo python3 memleak -a -p 5018
Attaching to pid 5018, Ctrl+C to quit.
[22:08:09] Top 10 stacks with outstanding allocations:
[22:08:14] Top 10 stacks with outstanding allocations:
[22:08:19] Top 10 stacks with outstanding allocations:
[22:08:24] Top 10 stacks with outstanding allocations:
[22:08:29] Top 10 stacks with outstanding allocations:
[22:08:34] Top 10 stacks with outstanding allocations:
[22:08:39] Top 10 stacks with outstanding allocations:
[22:08:44] Top 10 stacks with outstanding allocations:
^Cwj@wj:/usr/share/bcc/tools$
现在,我们看到,案例应用已经没有遗留内存,证明我们的修复工作成功完成。
mmap泄露检测
test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>void allocate_mmap_memory()
{int *p=mmap(NULL,//系统指定首地址getpagesize(),//一个页(基本单位)PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,//匿名映射0,0);//可以在这一页里随便折腾printf("mmap address p : %p\n",p);*p=20;*(p+1)=30;*(p+2)=40;munmap(p,4096);//释放内存
}void a()
{//int* p = malloc(5);//printf("malloc address p : %p\n",p);allocate_mmap_memory();//free(p);
}void b()
{a();
}void c()
{b();
}int main(int argc,char* argv[])
{while(1){sleep(15);c();}return 0;
}
编译运行:
wj@wj:~/linux$ gcc test.c -o test.out
wj@wj:~/linux$ ./test.out
mmap address p : 0x7fd503547000
mmap address p : 0x7fd50350d000
^C
wj@wj:~/linux$
wj@wj:~/linux$ ps -ef | grep test
kernoops 1078 1 0 19:33 ? 00:00:00 /usr/sbin/kerneloops --test
wj 5100 2537 0 22:15 pts/0 00:00:00 ./test.out
wj 5105 3794 0 22:15 pts/2 00:00:00 grep --color=auto test
wj@wj:~/linux$
wj@wj:/usr/share/bcc/tools$ sudo python3 memleak -a -p 5100
Attaching to pid 5100, Ctrl+C to quit.
[22:15:52] Top 10 stacks with outstanding allocations:addr = 55fbc12f22a0 size = 1024addr = 7fd503547000 size = 40961024 bytes in 1 allocations from stack0x00007fd50327eba4 __GI__IO_file_doallocate+0x94 [libc.so.6]4096 bytes in 1 allocations from stack0x000055fbc0d581df allocate_mmap_memory+0x36 [test.out]0x000055fbc0d58239 a+0x12 [test.out]0x000055fbc0d5824e b+0x12 [test.out]0x000055fbc0d58263 c+0x12 [test.out]0x000055fbc0d5828d main+0x27 [test.out]0x00007fd503229d90 __libc_start_call_main+0x80 [libc.so.6]
[22:15:57] Top 10 stacks with outstanding allocations:addr = 55fbc12f22a0 size = 1024addr = 7fd503547000 size = 40961024 bytes in 1 allocations from stack0x00007fd50327eba4 __GI__IO_file_doallocate+0x94 [libc.so.6]4096 bytes in 1 allocations from stack0x000055fbc0d581df allocate_mmap_memory+0x36 [test.out]0x000055fbc0d58239 a+0x12 [test.out]0x000055fbc0d5824e b+0x12 [test.out]0x000055fbc0d58263 c+0x12 [test.out]0x000055fbc0d5828d main+0x27 [test.out]0x00007fd503229d90 __libc_start_call_main+0x80 [libc.so.6]
[22:16:02] Top 10 stacks with outstanding allocations:addr = 55fbc12f22a0 size = 1024addr = 7fd503547000 size = 40961024 bytes in 1 allocations from stack0x00007fd50327eba4 __GI__IO_file_doallocate+0x94 [libc.so.6]4096 bytes in 1 allocations from stack0x000055fbc0d581df allocate_mmap_memory+0x36 [test.out]0x000055fbc0d58239 a+0x12 [test.out]0x000055fbc0d5824e b+0x12 [test.out]0x000055fbc0d58263 c+0x12 [test.out]0x000055fbc0d5828d main+0x27 [test.out]0x00007fd503229d90 __libc_start_call_main+0x80 [libc.so.6]
[22:16:07] Top 10 stacks with outstanding allocations:addr = 55fbc12f22a0 size = 1024addr = 7fd503547000 size = 4096addr = 7fd50350d000 size = 40961024 bytes in 1 allocations from stack0x00007fd50327eba4 __GI__IO_file_doallocate+0x94 [libc.so.6]8192 bytes in 2 allocations from stack0x000055fbc0d581df allocate_mmap_memory+0x36 [test.out]0x000055fbc0d58239 a+0x12 [test.out]0x000055fbc0d5824e b+0x12 [test.out]0x000055fbc0d58263 c+0x12 [test.out]0x000055fbc0d5828d main+0x27 [test.out]0x00007fd503229d90 __libc_start_call_main+0x80 [libc.so.6]
从 memleak 的输出可以看到,案例应用在不停地分配内存,并且这些分配的地址没有被回收。
调用munmap释放内存
test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>void allocate_mmap_memory()
{int *p=mmap(NULL,//系统指定首地址getpagesize(),//一个页(基本单位)PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,//匿名映射0,0);//可以在这一页里随便折腾printf("mmap address p : %p\n",p);*p=20;*(p+1)=30;*(p+2)=40;munmap(p,4096);//释放内存
}void a()
{//int* p = malloc(5);//printf("malloc address p : %p\n",p);allocate_mmap_memory();//free(p);
}void b()
{a();
}void c()
{b();
}int main(int argc,char* argv[])
{while(1){sleep(15);c();}return 0;
}
编译运行:
wj@wj:~/linux$ gcc test.c -o test.out
wj@wj:~/linux$ ./test.out
mmap address p : 0x7f4e82632000
mmap address p : 0x7f4e82632000
^C
wj@wj:~/linux$
wj@wj:~/linux$ ps -ef | grep test
kernoops 1078 1 0 19:33 ? 00:00:00 /usr/sbin/kerneloops --test
wj 5467 2537 0 22:24 pts/0 00:00:00 ./test.out
wj 5470 3794 0 22:24 pts/2 00:00:00 grep --color=auto test
wj@wj:~/linux$
wj@wj:/usr/share/bcc/tools$ sudo python3 memleak -a -p 5467
Attaching to pid 5467, Ctrl+C to quit.
[22:25:09] Top 10 stacks with outstanding allocations:
[22:25:14] Top 10 stacks with outstanding allocations:
[22:25:19] Top 10 stacks with outstanding allocations:
[22:25:24] Top 10 stacks with outstanding allocations:
[22:25:29] Top 10 stacks with outstanding allocations:
现在,我们看到,案例应用已经没有遗留内存,证明我们的修复工作成功完成。
小结
如果工作中遇到了mmap相关的泄露,考虑一下 eBPF或许是个不错的选择。
更多推荐
使用 eBPF检测 mmap泄露
发布评论