libdl中link_map是什么?为何引发应用动态加载崩溃?
动态加载库时的空指针崩溃问题
问题现象与根源
最近构建的复杂C++应用编译成功后,启动时出现非法内存访问崩溃,经排查是空指针带偏移访问导致。崩溃发生在通过dlopen动态加载libXcursor.so库的过程中,核心原因是link_map->l_versions为空指针,但elf_machine_rela函数(尤其是其中的RESOLVE_MAP宏)在重定位时未对该指针做非空检查,触发崩溃的代码行是map->l_versions[ndx]:
ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff; elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], &map->l_versions[ndx], (void *) (l_addr + r->r_offset), skip_ifunc);
需要注意的是,这个出现问题的link_map并非libXcursor.so自身的依赖描述,而是libgthread库的DSO,且该依赖未完成初始化。
问题代码路径追踪
libgthread的link_map是从dl_open_worker中传入进行重定位的:
_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
它是通过以下逻辑被收集到待重定位的依赖列表中的:
l = new; do { if (! l->l_real->l_relocated) maps[nmaps++] = l; l = l->l_next; } while (l != NULL);
上述代码中的new是libXcursor.so对应的link_map。这段逻辑存在异常,因为其他依赖的初始化是通过以下代码完成的,其中包含了版本检查的步骤:
/* Load that object's dependencies. */ _dl_map_object_deps (new, NULL, 0, 0, mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT)); /* So far, so good. Now check the versions. */ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL) (void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real, 0, 0);
疑问与修复需求
- 为什么会在重定位时传入另一组未正确初始化的依赖列表,且系统加载器的关键流程中没有添加必要的非空检查?
- 针对当前glibc 2.27版本,该如何修复这个崩溃问题?
环境信息
使用的libdl版本为/lib64/libdl-2.27.so,对应glibc版本为glibc-2.27-38.fc28.x86_64。
补充说明
该问题已在后续glibc的多个提交中修复,目前无法复现,即使使用glibc 2.27版本编写小型测试应用,也无法触发原崩溃场景。
内容的提问来源于stack exchange,提问作者Anton Astafiev
相关产品推荐
相关产品推荐

