CentOS中调用dlopen返回非零无效句柄触发SIGSEGV的问题排查
动态链接库加载异常问题排查
问题描述
我尝试给运行中的Tomcat进程安装seccomp BPF过滤器,操作步骤如下:用gdb附加到进程后,调用dlopen加载共享库.so文件,返回了非零整数句柄,但用gdb的x命令查看该句柄指向的内存时,提示Cannot access memory at address;后续用该句柄调用dlsym时触发SIGSEGV信号导致gdb终止。具体操作输出如下:
(gdb) set $handle=dlopen("/opt/seccompfilter.so",1) (gdb) x $handle 0xffffffffaaef8730: Cannot access memory at address 0xffffffffaaef8730 (gdb) call dlsym((void *)$handle, "install_filter") [Thread 0x7f6250aed700 (LWP 2069) exited] Program received signal SIGSEGV, Segmentation fault. _dl_lookup_symbol_x (undef_name=0x5636aaef81d0 "install_filter", undef_map=0xffffffffaaef8730, ref=0x7ffe774956a0, symbol_scope=0xffffffffaaef8ab8, version=0x0, type_class=0, flags=2, skip_map=0x0) at dl-lookup.c:733 733 while ((*scope)->r_list[i] != skip_map) The program being debugged was signaled while in a function called from GDB. GDB remains in the frame where the signal was received. To change this behavior use "set unwindonsignal on". Evaluation of the expression containing the function (__dlsym) will be abandoned. When the function is done executing, GDB will silently stop.
操作环境:
CentOS 7 kernel-3.10.0-1160.90.1.el7.x86_64 gdb 7.6.1-120.el7 glibc 2.17
相同步骤在Ubuntu 20.04上可成功执行,我还尝试用dlopen加载libc.so.6共享库,得到同样的错误结果。
问题原因及解决思路
这是CentOS 7(glibc 2.17)与Ubuntu 20.04(glibc 2.31)在动态链接器行为上的差异导致的,核心问题是gdb调用dlopen时的地址空间上下文不匹配:
- 地址空间布局随机化(ASLR)与动态链接器内部状态:CentOS 7的glibc 2.17中,
dlopen返回的句柄是动态链接器内部link_map结构体的指针,但该指针属于进程的共享库加载地址空间,gdb执行表达式时的上下文未同步该空间的权限映射,导致无法直接通过x命令访问对应内存。 dlsym崩溃的直接原因:报错显示_dl_lookup_symbol_x函数访问*scope指向的内存时出错,本质是dlopen返回的link_map指针在当前gdb执行上下文里无效——glibc 2.17的动态链接器不支持在gdb表达式求值环境中直接用该句柄调用dlsym,而Ubuntu 20.04的glibc版本做了兼容性优化,允许此类操作。
可行解决方法
- 进程内注入执行:不要在gdb里直接调用
dlopen和dlsym,编写一个包含seccomp过滤器安装逻辑的小共享库,用gdb的call命令调用dlopen加载这个库,让库内部自行完成逻辑。 - 临时关闭ASLR:执行
echo 0 > /proc/sys/kernel/randomize_va_space关闭地址空间随机化后重试,仅适合测试场景,生产环境禁用会降低系统安全性。 - 升级依赖组件:若条件允许,通过devtoolset升级CentOS 7的gdb到更高版本,同时升级glibc兼容版本,但CentOS 7基础库升级风险较高,需谨慎操作。
内容的提问来源于stack exchange,提问作者Lam WingWong
相关产品推荐
相关产品推荐

