[Bug] libdl/dlmodule: no bounds validation on ELF relocation metadata - malformed .mo gives arbitrary kernel-mode memory write with controlled value during load
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 25/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- c
调研方向
Start with components/libc/posix/libdl/dlelf.c and the architecture relocation handlers in components/libc/posix/libdl/arch/arm.c, arch/riscv.c, and arch/x86.c. Trace the ELF metadata and relocation offsets from dlmodule_load() into dlmodule_relocate(); the issue reports a crafted .mo causing an out-of-bounds kernel write. Done means malformed ELF ranges and indices are rejected safely across the listed metadata paths and architectures.
由索引模型根据 Issue 内容生成。
描述
RT-Thread Version
master @ 5d1fb66d (v5.4.0-dev; components/libc/posix/libdl/dlelf.c verified byte-identical to origin/master as of 2026-10-19). Affected since v3.1.0; introduced by commit efe45231 ("[Kernel] move module to libc/libdl", 2018-08-30) which added dlelf.c/arch/arm.c with the unchecked relocation write. Verified present in every release tag since (checked by reading arch/arm.c/dlelf.c at v3.1.0, v4.0.0, v5.3.0 and master).
Affected area
Components / libc (libdl dynamic module loader, RT_USING_MODULE)
Hardware/BSP vendor
Not applicable / Other
Architecture
ARM Cortex-A/R/M 32-bit (reproduced), RISC-V 32, x86 — every architecture with a dlmodule_relocate port (arch/arm.c, arch/riscv.c, arch/x86.c share the same pattern). Not applicable to aarch64 (no port).
Board and hardware details
QEMU vexpress-a9 (Cortex-A9), stock bsp/qemu-vexpress-a9 defconfig + RT_USING_MODULE=y, FAT16 SD image holding the module file. Any board with the dynamic-module feature enabled is affected.
Develop Toolchain
arm-none-eabi-gcc 10.3.1 (gcc / scons)
Describe the bug
The dynamic module loader (dlopen()/msh auto-exec of .mo files) performs no bounds validation whatsoever on the ELF relocation metadata it parses. The write target of every relocation is computed directly from the attacker-controlled 32-bit Elf32_Rel.r_offset in the module file:
// components/libc/posix/libdl/arch/arm.c:33-37 (arch/riscv.c & arch/x86.c: same pattern)
where = (Elf32_Addr *)((rt_uint8_t *)module->mem_space
+ rel->r_offset // <-- file-supplied, never checked
- module->vstart_addr);
...
case R_ARM_GLOB_DAT:
case R_ARM_JUMP_SLOT:
*where = (Elf32_Addr)sym_val; // arm.c:65-66
and the value sym_val is the address of any symbol the module names from the kernel export table (dlmodule_symbol_find(strtab + sym->st_name), dlelf.c:193). On 32-bit targets mem_space + r_offset wraps modulo 2^32, so a malformed .mo obtains an arbitrary-address, attacker-valued 4-byte kernel-mode write, executed during dlmodule_load() — before any module code runs, so this is not "modules are trusted code": the corruption comes purely from the loader parsing malformed input.
The whole metadata chain feeding that write is likewise unchecked (same root cause, independent read primitives): r_info symbol index (24-bit, symtab[ELF32_R_SYM()] OOB), sh_link/sh_link->sh_link (unbounded shdr[] indexing → wild rel/symtab/strtab pointers), e_shoff/e_phoff/e_shstrndx (never compared against the file length; dlmodule_load() only checks magic + ELF class), p_offset+p_filesz / sh_offset+sh_size (heap over-read as memcpy source), and st_value (wild read feeding module->priority/stack_size). e_machine is not validated either, so cross-architecture ELF files are also interpreted.
Reproduction (full chain: FAT file → dfs → msh evil → dlmodule_exec → loader; QEMU + gdb-multiarch):
- Build
bsp/qemu-vexpress-a9withRT_USING_MODULE=y; attach a FAT16 SD image containing a craftedevil.mo(minimal ET_DYN ARM ELF, one.rel.textentry:R_ARM_GLOB_DATon symbolrt_kprintf,r_offset = 0xfffe4074). - Type
evilin the msh shell (msh auto-triesevil.mo, msh.c:312dlmodule_exec). - Result:
*(_ptr_t)(mem_space + r_offset - vstart) = &rt_kprintflands at kernel.bss0x600ec26c (_rt_module_symtab_end, 0x1bf8c bytes before the module allocation) — hardware watchpoint capturesOld = 0x600c1a40 → New = 0x600a7870 (&rt_kprintf)with backtracedlmodule_relocate ← dlmodule_load_shared_object (dlelf.c:201) ← dlmodule_load ← dlmodule_exec ← msh_exec_module (msh.c:312) ← msh_exec ← finsh_thread_entry,rel->r_offset = 0xfffe4074vsmodule->mem_size = 0x2000. The kernel keeps running — silent, persistent corruption. - Crash variant (
r_offset = 0x9fef7e48, write to the null page):data abort,pc=0x600163f0(strindlmodule_relocate),r3=0x40(computed target),r2=0x600a7870(value), same 6-frame backtrace, kernel shuts down. In the same session a well-formed module built with the stock toolchain (hello.mo, singleR_ARM_JUMP_SLOTat a legitimate in-module offset0x10338 < 0x1033c) loads, runs and exits cleanly — the only difference between the benign and malicious files isr_offset. - Value control: pointing the same primitive at another address with symbol
rt_strlenwrites0x6005e984there (watchpointNew = &rt_strlen).
Generator, control module, GDB scripts and full serial/GDB logs: see attached (gen_mo.py produces the malformed module; hello.c is the control).
Expected behavior
The loader must validate, against module->mem_size and the file buffer length: every relocation target (vstart_addr <= r_offset within the mapped image), the symbol index in r_info, all section-header indices (sh_link, sh_info, e_shstrndx), the table base offsets (e_shoff/e_phoff + count*sizeof within the file), copy sources (p_offset+p_filesz, sh_offset+sh_size within the file), and e_machine against the running target. Anything outside bounds must fail the load with an error, not write.
Other additional context
- Attack preconditions (explicit): attacker can place a file on a filesystem the system loads modules from, and the module gets loaded via the documented entry points — msh executing
<name>.mo//bin/<name>.mo(components/finsh/msh.c:255-322) or an application callingdlopen(). Typical exposure: module/plugin hot-updates from SD cards or network downloads, field-deployed kiosks/devices with a shell. The corruption happens inside the loader, before any module code executes — a robustness defect in trusted kernel code parsing untrusted input, not a "malicious module runs code" scenario. - Impact: arbitrary kernel-mode memory write with attacker-chosen value (any
RTM_EXPORTed symbol address), plusR_ARM_ABS32/R_ARM_RELATIVEadd-write and read-modify-write variants → kernel control-flow hijack (overwrite any function pointer/vtable in kernel .data/.bss/heap) or deterministic kernel crash (DoS). CVSS 3.1: 7.1 (AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H), CWE-122/CWE-129 (read-side variants CWE-125). - Relationship to prior reports: none — the only recent libdl changes are #11247 (2026-03, unrelated
static unsolvedreset) and #11062 (2025-12, loop cleanup); neither touches bounds validation. Not related to #11882 (lwp sigaction) — different component, different root cause. - Fix suggestion (one systematic pass, mirrors the checks Zephyr added for LLEXT): centralize a
dlmodule_check_range()and apply it at every file-derived index/offset listed above.
- 主要语言
- C
- 星标
- 12.3k
- 派生
- 5.5k
- 平均合并
- 4 天 23 小时
- 30 天内合并 PR
- 27
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
RT-Thread/rt-thread 的其他 Issue
-
bug Component component: net
难度 2/5 1-3 小时 新手友好度 78/100
RT-Thread/rt-thread#11913 · 1 条评论 ·
维护者通常 1 天内回复
-
[Bug] dfs_v2 tmpfs: ftruncate(fd, 0) frees the file buffer but reports success — use-after-free read/write and double free from a legal POSIX call可能已有人在做 @remukisama 于 2 天前认领。 未关闭in progress
难度 2/5 1-3 小时 新手友好度 78/100
RT-Thread/rt-thread#11902 · 4 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
[bsp][stm32][bluepill] README「快速上手」缺少重新生成 MDK 工程这一步,按文档操作无法编译通过可能已有人在做 @moment-NEW 于 20 天前认领。 未关闭in progress
难度 2/5 1-3 小时 新手友好度 75/100
RT-Thread/rt-thread#11818 · 4 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
BSP BSP: Loongson bug RT-Smart
难度 2/5 1-3 小时 新手友好度 72/100
RT-Thread/rt-thread#11717 · 2 条评论 ·
维护者通常 1 天内回复
-
Arch: RISC-V BSP BSP: HPMicro bug
难度 2/5 1-3 小时 新手友好度 72/100
RT-Thread/rt-thread#11687 · 4 条评论 ·
维护者通常 1 天内回复
查看 RT-Thread/rt-thread 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 70/100
espressif/esp-adf-libs#89 · 1 条评论 ·
-
TLS: a stale OpenSSL error makes non-blocking reads fail with "Resource temporarily unavailable"未关闭
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复
-
enhancement severity: low
难度 2/5 1-3 小时 新手友好度 78/100
luainkernel/lunatik#2091 ·
维护者通常 1 天内回复
-
难度 1/5 1-3 小时 新手友好度 82/100
-
难度 2/5 1-3 小时 新手友好度 84/100
bigtreetech/BIGTREETECH-TouchScreenFirmware#3056 ·
维护者通常 1 天内回复