Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[Bug] libdl/dlmodule: no bounds validation on ELF relocation metadata - malformed .mo gives arbitrary kernel-mode memory write with controlled value during load

Đang mở
#11,898 2 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
25/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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):

  1. Build bsp/qemu-vexpress-a9 with RT_USING_MODULE=y; attach a FAT16 SD image containing a crafted evil.mo (minimal ET_DYN ARM ELF, one .rel.text entry: R_ARM_GLOB_DAT on symbol rt_kprintf, r_offset = 0xfffe4074).
  2. Type evil in the msh shell (msh auto-tries evil.mo, msh.c:312 dlmodule_exec).
  3. Result: *(_ptr_t)(mem_space + r_offset - vstart) = &rt_kprintf lands at kernel .bss 0x600ec26c (_rt_module_symtab_end, 0x1bf8c bytes before the module allocation) — hardware watchpoint captures Old = 0x600c1a40 → New = 0x600a7870 (&rt_kprintf) with backtrace dlmodule_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 = 0xfffe4074 vs module->mem_size = 0x2000. The kernel keeps running — silent, persistent corruption.
  4. Crash variant (r_offset = 0x9fef7e48, write to the null page): data abort, pc=0x600163f0 (str in dlmodule_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, single R_ARM_JUMP_SLOT at a legitimate in-module offset 0x10338 < 0x1033c) loads, runs and exits cleanly — the only difference between the benign and malicious files is r_offset.
  5. Value control: pointing the same primitive at another address with symbol rt_strlen writes 0x6005e984 there (watchpoint New = &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 calling dlopen(). 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), plus R_ARM_ABS32/R_ARM_RELATIVE add-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 unsolved reset) 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.
Ngôn ngữ chính
C
Star
12.3k
Fork
5.5k
Merge trung bình
4 ngày 23 giờ
Pull request đã merge (30 ngày)
27

Chuẩn bị môi trường

Mở trong Codespaces

Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của RT-Thread/rt-thread

Tất cả issue của RT-Thread/rt-thread

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.