admin管理员组文章数量:1037775
探秘VPP:快速定位libc内存泄漏的有效方法
这里就使用到了动态库预加载LD_PRELOAD机制。LD_PRELOAD 是 Linux 系统中一个非常强大的特性,它允许用户通过环境变量指定在加载其他共享库之前优先加载的共享库。这一机制主要用于动态链接阶段,可以在不修改程序源代码的情况下改变或增强程序的行为。
在Linux系统中,当一个程序启动时,操作系统会调用动态链接器(通常是ld.so或其变种)来加载并链接程序依赖的所有共享库。如果环境变量LD_PRELOAD被设置,动态链接器会在处理其他任何共享库之前优先加载由LD_PRELOAD指定的共享库。动态链接器遵循一种称为“先到先得”的符号解析策略。实现细节是通过修改动态链接器内部的某些函数(如_dl_load_object())来调整加载顺序,确保LD_PRELOAD中的库最先被加载。这样做的结果是,这些预加载的库有机会“先入为主”,即它们可以定义与后续加载的库中同名的符号(如函数),从而实现对这些符号的覆盖。
在 vpp(Vector Packet Processing)里,内存的分配与管理依赖于 vpp 的主堆(main - heap)。然而,当引入外部库时,尤其是在插件场景下,问题就会凸显出来。以 IKEv2 插件使用的 OpenSSL 库为例,这些外部库一般会通过标准的 libc
函数,如 malloc()
和 free()
等来进行内存管理,这实际上是在使用默认的 libc
堆。但 vpp 本身并不知晓这个默认 libc
堆的存在。这一情况直接导致 vpp 自带的内存跟踪工具(memory - trace)失去作用,无法对外部库使用的内存进行有效跟踪和管理。
为解决相关问题,vpp 代码中提供了 libvppmem_preload.so
库,其相关代码位于 src/vpp/mem/mem.c
文件。该库的作用是替换标准的 libc
内存管理函数,例如 malloc()
、free()
等,转而使用vpp自身的内存管理函数,如 clib_mem_alloc
、clib_mem_free
等。
这种替换方式具有显著优势。它能确保所有内存分配与释放操作都通过 vpp 的内存管理接口完成,进而使 vpp 自带的调试和监控工具能够全面监测整个应用程序的内存使用情况,即使是由外部库发起的内存操作也能被覆盖。以下是具体的操作方法如下:
代码语言:javascript代码运行次数:0运行复制执行make build编译之后,库一般存放在下面的位置:root@learningvpp:~/workspace/vpp# ls build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -l
lrwxrwxrwx 1 root root 26 Mar 10 13:09 build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -> libvppmem_preload.so.25.06
接下来我们使用LD_PRELOAD机制加载libvppmem_preload.so库,来运行vpp,如下:
- root@learningvpp:~/workspace/vpp# LD_PRELOAD=build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so build-root/install-vpp_debug-native/vpp/bin/vpp -c /etc/vpp/startup.conf
- vpp mem: libc allocation requested but no vpp heap ready, defaulting to libc.
- unix_config:461: couldn't open log '/var/log/vpp/vpp.log'
- vpp[1428284]: perfmon: skipping source 'intel-uncore' - intel_uncore_init: no uncore units found
- vpp[1428284]: perfmon: skipping source 'intel-core' - intel_core_init: not a IA-32 CPU
- vpp[1428284]: vlib_pci_bind_to_uio: Skipping PCI device 0000:00:05.0: missing kernel VFIO or UIO driver
- vpp[1428284]: vat-plug/load: vat_plugin_register: idpf plugin not loaded...
- vpp[1428284]: vat-plug/load: vat_plugin_register: oddbuf plugin not loaded...
在上图第2行的提示中,反映了在VPP初始化的初期,由于VPP自身的堆管理机制尚未准备就绪,暂时通过libc的malloc()函数进行内存分配。此状况仅会在VPP启动的极短时间内出现。一旦VPP的内存管理系统完成初始化,便会切换至使用VPP自己的内存管理策略。这表明了系统在启动过程中短暂依赖标准C库的内存分配功能,以确保顺利过渡到VPP定制的内存管理流程。
接下来,我们将利用VPP自带的插件单元测试用例中的test crypto来验证是否能够成功记录到与OpenSSL相关的库调用。
代码语言:txt复制#开启内存跟踪命令
DBGvpp# memory-trace on main-heap
#调用加解密测试套件
DBGvpp# test crypto
128-GCM Spec. TC1 (encrypt-aes-128-gcm) OK
128-GCM Spec. TC1 (decrypt-aes-128-gcm) OK
128-GCM Spec. TC2 (encrypt-aes-128-gcm) OK
.....
#查询跟踪情况:
DBGvpp# show memory main-heap verbose
Thread 0 vpp_main
base 0x74ce3fe01000,size 512m,locked,unmap-on-destroy, traced, name 'main heap'
page stats: page-size 4K, total 131072, mapped 25159, not-mapped 105913
numa 0: 25159 pages, 98.28m bytes
total: 512.00M, used: 101.54M, free: 410.46M, trimmable: 398.23M
free chunks 1008 free fastbin blks 0
max total allocated 512.00M
Bytes Count Sample Traceback
192 2 0x72a4168a32b0 0x72a435ad3225
CRYPTO_zalloc + 0x15
0x72a411fe4128
HMAC_Init_ex + 0x167
0x72a40e8041f8
0x72a40e8034cd
0x72a40e800aa0
vnet_crypto_key_add + 0x1db
generate_digest + 0x116
test_crypto + 0x48e
test_crypto_command_fn + 0x224
vlib_cli_dispatch_sub_commands + 0xe24
vlib_cli_dispatch_sub_commands + 0xe24
我们注意到,在VPP的内存跟踪中,OpenSSL的CRYPTO_zalloc()调用显示为malloc()。这种情况仅在使用了预加载技术时才会出现;如果没有使用预加载,这一调用将不会在此处显示。这表明预加载使我们能够捕捉到通过libc库进行的内存分配操作,即使这些操作实际上是由OpenSSL函数发起的。
至此,我们可以监测到openssl相关函数调用栈情况,至于是否存在泄漏情况,还需要阅读代码判断。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-03-13,如有侵权请联系 cloudcommunity@tencent 删除内存泄漏libc函数内存内存管理探秘VPP:快速定位libc内存泄漏的有效方法
这里就使用到了动态库预加载LD_PRELOAD机制。LD_PRELOAD 是 Linux 系统中一个非常强大的特性,它允许用户通过环境变量指定在加载其他共享库之前优先加载的共享库。这一机制主要用于动态链接阶段,可以在不修改程序源代码的情况下改变或增强程序的行为。
在Linux系统中,当一个程序启动时,操作系统会调用动态链接器(通常是ld.so或其变种)来加载并链接程序依赖的所有共享库。如果环境变量LD_PRELOAD被设置,动态链接器会在处理其他任何共享库之前优先加载由LD_PRELOAD指定的共享库。动态链接器遵循一种称为“先到先得”的符号解析策略。实现细节是通过修改动态链接器内部的某些函数(如_dl_load_object())来调整加载顺序,确保LD_PRELOAD中的库最先被加载。这样做的结果是,这些预加载的库有机会“先入为主”,即它们可以定义与后续加载的库中同名的符号(如函数),从而实现对这些符号的覆盖。
在 vpp(Vector Packet Processing)里,内存的分配与管理依赖于 vpp 的主堆(main - heap)。然而,当引入外部库时,尤其是在插件场景下,问题就会凸显出来。以 IKEv2 插件使用的 OpenSSL 库为例,这些外部库一般会通过标准的 libc
函数,如 malloc()
和 free()
等来进行内存管理,这实际上是在使用默认的 libc
堆。但 vpp 本身并不知晓这个默认 libc
堆的存在。这一情况直接导致 vpp 自带的内存跟踪工具(memory - trace)失去作用,无法对外部库使用的内存进行有效跟踪和管理。
为解决相关问题,vpp 代码中提供了 libvppmem_preload.so
库,其相关代码位于 src/vpp/mem/mem.c
文件。该库的作用是替换标准的 libc
内存管理函数,例如 malloc()
、free()
等,转而使用vpp自身的内存管理函数,如 clib_mem_alloc
、clib_mem_free
等。
这种替换方式具有显著优势。它能确保所有内存分配与释放操作都通过 vpp 的内存管理接口完成,进而使 vpp 自带的调试和监控工具能够全面监测整个应用程序的内存使用情况,即使是由外部库发起的内存操作也能被覆盖。以下是具体的操作方法如下:
代码语言:javascript代码运行次数:0运行复制执行make build编译之后,库一般存放在下面的位置:root@learningvpp:~/workspace/vpp# ls build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -l
lrwxrwxrwx 1 root root 26 Mar 10 13:09 build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -> libvppmem_preload.so.25.06
接下来我们使用LD_PRELOAD机制加载libvppmem_preload.so库,来运行vpp,如下:
- root@learningvpp:~/workspace/vpp# LD_PRELOAD=build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so build-root/install-vpp_debug-native/vpp/bin/vpp -c /etc/vpp/startup.conf
- vpp mem: libc allocation requested but no vpp heap ready, defaulting to libc.
- unix_config:461: couldn't open log '/var/log/vpp/vpp.log'
- vpp[1428284]: perfmon: skipping source 'intel-uncore' - intel_uncore_init: no uncore units found
- vpp[1428284]: perfmon: skipping source 'intel-core' - intel_core_init: not a IA-32 CPU
- vpp[1428284]: vlib_pci_bind_to_uio: Skipping PCI device 0000:00:05.0: missing kernel VFIO or UIO driver
- vpp[1428284]: vat-plug/load: vat_plugin_register: idpf plugin not loaded...
- vpp[1428284]: vat-plug/load: vat_plugin_register: oddbuf plugin not loaded...
在上图第2行的提示中,反映了在VPP初始化的初期,由于VPP自身的堆管理机制尚未准备就绪,暂时通过libc的malloc()函数进行内存分配。此状况仅会在VPP启动的极短时间内出现。一旦VPP的内存管理系统完成初始化,便会切换至使用VPP自己的内存管理策略。这表明了系统在启动过程中短暂依赖标准C库的内存分配功能,以确保顺利过渡到VPP定制的内存管理流程。
接下来,我们将利用VPP自带的插件单元测试用例中的test crypto来验证是否能够成功记录到与OpenSSL相关的库调用。
代码语言:txt复制#开启内存跟踪命令
DBGvpp# memory-trace on main-heap
#调用加解密测试套件
DBGvpp# test crypto
128-GCM Spec. TC1 (encrypt-aes-128-gcm) OK
128-GCM Spec. TC1 (decrypt-aes-128-gcm) OK
128-GCM Spec. TC2 (encrypt-aes-128-gcm) OK
.....
#查询跟踪情况:
DBGvpp# show memory main-heap verbose
Thread 0 vpp_main
base 0x74ce3fe01000,size 512m,locked,unmap-on-destroy, traced, name 'main heap'
page stats: page-size 4K, total 131072, mapped 25159, not-mapped 105913
numa 0: 25159 pages, 98.28m bytes
total: 512.00M, used: 101.54M, free: 410.46M, trimmable: 398.23M
free chunks 1008 free fastbin blks 0
max total allocated 512.00M
Bytes Count Sample Traceback
192 2 0x72a4168a32b0 0x72a435ad3225
CRYPTO_zalloc + 0x15
0x72a411fe4128
HMAC_Init_ex + 0x167
0x72a40e8041f8
0x72a40e8034cd
0x72a40e800aa0
vnet_crypto_key_add + 0x1db
generate_digest + 0x116
test_crypto + 0x48e
test_crypto_command_fn + 0x224
vlib_cli_dispatch_sub_commands + 0xe24
vlib_cli_dispatch_sub_commands + 0xe24
我们注意到,在VPP的内存跟踪中,OpenSSL的CRYPTO_zalloc()调用显示为malloc()。这种情况仅在使用了预加载技术时才会出现;如果没有使用预加载,这一调用将不会在此处显示。这表明预加载使我们能够捕捉到通过libc库进行的内存分配操作,即使这些操作实际上是由OpenSSL函数发起的。
至此,我们可以监测到openssl相关函数调用栈情况,至于是否存在泄漏情况,还需要阅读代码判断。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-03-13,如有侵权请联系 cloudcommunity@tencent 删除内存泄漏libc函数内存内存管理本文标签: 探秘VPP快速定位libc内存泄漏的有效方法
版权声明:本文标题:探秘VPP:快速定位libc内存泄漏的有效方法 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1748288544a2280421.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论