当前位置: 首页 > news >正文

使用 eBPF检测 mmap泄露

目录

背景

官网

malloc泄露检测

mmap泄露检测

调用munmap释放内存

小结


背景

我们知道 mmap系统调用申请的内存空间,属于文件映射区域 和 匿名映射区域。这部分区域并不属于 heap,所以用一般的内存泄露检测工具是检测不出来的。例如:一般常用的内存泄露检测工具 vagrind、ASAN、malloc_debug等。关于ASAN的介绍,可以参考:ASAN入门参考-CSDN博客

官网

https://github.com/iovisor/bcc/blob/master/tools/memleak.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或许是个不错的选择。

相关文章:

  • 【电路笔记】-节点电压分析和网状电流分析
  • EDA实验----四选一多路选择器设计(QuartusII)
  • Java中单例模式
  • Echarts柱状体实现滚动条动态滚动
  • Spring源码系列-框架中的设计模式
  • [工业自动化-11]:西门子S7-15xxx编程 - PLC从站 - 分布式IO从站/从机
  • 【C++笔记】优先级队列priority_queue的模拟实现
  • 原型模式(创建型)
  • 解析html生成Word文档
  • 总结:利用原生JDK封装工具类,解析properties配置文件以及MF清单文件
  • 七个优秀微服务跟踪工具
  • 微服务-开篇-个人对微服务的理解
  • 【Springboot】基于注解式开发Springboot-Vue3整合Mybatis-plus实现分页查询
  • 每次重启完IDEA,application.properties文件里的中文变成?
  • Flink 基础 -- 应用开发(Table API SQL) 概念和通用API
  • 【Leetcode】101. 对称二叉树
  • const let
  • docker python 配置
  • echarts的各种常用效果展示
  • express.js的介绍及使用
  • in typeof instanceof ===这些运算符有什么作用
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • Swoft 源码剖析 - 代码自动更新机制
  • tweak 支持第三方库
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 电商搜索引擎的架构设计和性能优化
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 前端面试之闭包
  • 手机端车牌号码键盘的vue组件
  • 线上 python http server profile 实践
  • 你对linux中grep命令知道多少?
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ​Spring Boot 分片上传文件
  • ​ubuntu下安装kvm虚拟机
  • # 安徽锐锋科技IDMS系统简介
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (52)只出现一次的数字III
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (原)Matlab的svmtrain和svmclassify
  • .gitignore
  • .NET Core 中的路径问题
  • .NET Micro Framework初体验(二)
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • ?php echo ?,?php echo Hello world!;?
  • @selector(..)警告提示
  • [ 渗透测试面试篇 ] 渗透测试面试题大集合(详解)(十)RCE (远程代码/命令执行漏洞)相关面试题
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务