Repository navigation
[libc][libdl] reject out-of-bounds ELF relocation targets - #12016
Conversation
- Validate relocation target range against module memory bounds to prevent out-of-bounds writes - Add dlmodule_check_rel_target helper and apply checks to ARM, RISC-V, and x86 arch relocators - Add bounds checks for ELF section header indices and symbol table indices - Abort module load and ensure proper cleanup (freeing section_load_addr) on relocation failure - Fix RT-Thread#11898
|
👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread! 为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。 🛠 操作步骤 | Steps
完成后,提交将自动更新至 如有问题欢迎联系我们,再次感谢您的贡献!💐 |
📌 Code Review Assignment🏷️ Tag: componentsReviewers: @Maihuanyi Changed Files (Click to expand)
🏷️ Tag: components_libcReviewers: @GorrayLi @mysterywolf Changed Files (Click to expand)
📊 Current Review Status (Last Updated: 2026-11-10 10:02 CST)
📝 Review Instructions
|
|
|
补上 PR 描述里缺的那一半证据:真的喂了一个坏模块,在 QEMU 里跑通了。结论是补丁有效,而且 环境
构建模块的方式参考了 #11989 的 fixtures: 注入方式:只改 master(未打补丁)
两个坏样本的崩溃都发生在 (backtrace 用 一个值得注意的细节写入本身没有立即 fault。 所以:
打上本 PR
关键日志: 为什么这条证据重要:两种 ELF 类型走的是不同函数——
如果只测 一个设计上的观察(不是缺陷)节级检查用的是 未覆盖
我没有把 PoC 代码提交到任何分支——按日报的规矩,这类带攻击性的样本不该进仓库。需要的话可以把生成脚本(不含二进制)整理成一个宿主侧工具,但那要维护者先表态。 |
BernardXiong
left a comment
There was a problem hiding this comment.
结论:未发现阻塞问题,可以合并。
已验证:
- 新增
dlmodule_check_rel_target()同时检查r_offset相对vstart_addr的下溢和mem_size上界,覆盖 arm/riscv/x86 三个现有 arch 实现; dlmodule_load_shared_object()/dlmodule_load_relocated_object()补了sh_link、sh_info、sym_index、e_shstrndx的边界校验,并检查dlmodule_relocate()返回值;section_load_addr[sh_info] == 0的段直接跳过,避免对未加载段做重定位。
非阻塞:sh_offset / sh_size 仍未按 ELF 文件实际大小校验(如 dlelf.c:203、dlelf.c:299),畸形 ELF 可在加载阶段越界读;不属于本次重定位写路径,建议后续补上。
CI:107 pass / 3 skip,全部通过。
Rbb666
left a comment
There was a problem hiding this comment.
补充一处重定位写入范围问题和两处加载兼容性回归,详见三条行内评论。
| return 0; | ||
| } | ||
|
|
||
| if (dlmodule_check_rel_target(module, (rt_ubase_t)rel->r_offset, sizeof(Elf32_Addr)) != RT_EOK) |
There was a problem hiding this comment.
[P1] Thumb 重定位的写入范围仍超出此处检查
R_ARM_THM_CALL / R_ARM_THM_JUMP24 分支用 *(rt_uint16_t *)(where + 2) 写第二个半字。where 的类型是 Elf32_Addr *,所以该写入位于目标地址后 8 字节,而此处只校验 4 字节。目标位于模块尾部时仍可能越界写入。建议将第二个半字的写入改为 *((rt_uint16_t *)where + 1),使实际写入范围与校验一致。
对应写入位于 arm.c 第 123 行。此问题通过指针运算检查确认。
| strtab + sym->st_name); | ||
| strtab + sym->st_name); | ||
| rt_free(section_load_addr); | ||
| return -RT_ERROR; |
There was a problem hiding this comment.
[P2] 此处会拒绝合法的空操作重定位和未定义弱符号
ET_REL 中 R_ARM_NONE 使用符号索引 0 时,加载器会先查找空符号名,并在这里返回错误,无法到达 ARM 实现中新增的空操作分支。引用未定义 STB_WEAK 对象的 R_ARM_ABS32 也会走到此处并失败。建议在解析符号前跳过空操作,并按 ABI 处理未定义弱符号。
用基线和本 PR 源码做宿主侧对照测试,上述两个用例在基线返回 RT_EOK,本 PR 均返回 -RT_ERROR。
| unsolved = RT_FALSE; | ||
| return -RT_ERROR; | ||
| } | ||
| if (elf_module->e_shstrndx >= elf_module->e_shnum) |
There was a problem hiding this comment.
[P2] 此检查会拒绝无节头表的共享对象
ELF 允许共享对象省略节头表,此时 e_shoff、e_shnum 和 e_shstrndx 均为 0。对于无需重定位、可直接通过 PT_LOAD 加载的 ET_DYN,基线能够完成加载,此处却因 0 >= 0 返回错误。建议先处理节头表缺省的情况,再校验节名表索引。
用基线和本 PR 源码做宿主侧对照测试,该用例在基线返回 RT_EOK,本 PR 返回 -RT_ERROR。节头表缺省的字段约定见 ELF 头规范。
中文
问题
dlmodule重定位写地址按mem_space + r_offset - vstart计算,未做范围校验;畸形 ELF 可写出模块内存(#11898)。改动
dlelf.c/dlmodule.h:重定位目标越界则失败arch/arm.c、riscv.c、x86.c:同策略防护验证
静态审阅越界路径;未板级、未跑恶意模块。
English
Problem
dlmodulerelocation writesmem_space + r_offset - vstartwith no range check; a bad ELF can write outside the module (#11898).Changes
dlelf.c/dlmodule.h: reject out-of-range reloc targetsarch/arm.c,riscv.c,x86.c: same guardTest plan
Static review of OOB paths; no board / malicious module run.
Fixes #11898