REL/RELA sh_info accepted unvalidated β findbase()==0 β panic("lost base for reltab/relatab") on crafted module (also via the boot preload path)
| Field | Value |
|---|---|
| ID | DF-2772 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-20 / CWE-617 |
| File | sys/kern/link_elf_obj.c |
| Lines | 775/791 (store), 977/1004/1245/1266 (panics), preload 348/355/368 |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-30 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
The section scan stores each REL/RELA section's sh_info verbatim as the relocation target section without checking it names a section that actually gets a progtab entry. If sh_info points at the NULL section, a STRTAB/SYMTAB, an index β₯ e_shnum, or a size-0 PROGBITS section (skipped from progtab), findbase() returns 0 and both relocation drivers panic instead of rejecting the file β reached from kldload and from boot preload (making a corrupted preloaded boot module a boot-time brick). Root-supplied-input class; deterministic panic; no write primitive.
Proof of contest
VERIFIED 2/2 on fresh-reset stock guests (findings/poc/DF-2772/):
crafted .ko β panic: lost base for relatab with backtrace
link_elf_obj_reloc_local β link_elf_obj_load_file β linker_load_module
β sys_kldload. Patched kernel: clean ENOEXEC rejection, guest survives.
Recommended fix
Validate sh_info at scan time (target must be a non-empty PROGBITS/NOBITS section within e_shnum) and convert the four panics to ENOEXEC returns β validated fix.diff in the pack.
Timeline
- 2026-08-30 Discovered during pass-2 audit of link_elf_obj.c (GLM 5.3); panic reproduced 2Γ + fix validated same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2772 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.6 KB | β raw | |
| VERDICT.md | β | 3.8 KB | β raw | |
| gen_module.py | β | 3.7 KB | view raw | |
| build.sh | β | 53 B | view raw | |
| run.sh | β | 266 B | view raw | |
| run.log | β | 1.4 KB | view raw | |
| panic.txt | β | 658 B | view raw | |
| build.log | β | 5.7 MB | β download | |
| env.txt | β | 191 B | view raw | |
| fix.diff | β | 5.6 KB | view raw | |
| verdict.json | β | 4.0 KB | view raw |
DF-2772 β findbase()==0 β panic("lost base for relatab") on REL/RELA whose sh_info doesn't name a loaded section
Build (host)
python3 gen_module.py df2772.ko panic-rela
Run (guest, root β kldload is SYSCAP_NOKLD-gated)
scp -F dfbsd-qemu/config df2772.ko dfbsd:/tmp/ dfbsd-qemu/vm.sh run_root 'kldload /tmp/df2772.ko'
Expected β stock kernel (baseline, reproduced twice)
Serial console (panic.txt):
panic: lost base for relatab
link_elf_obj_reloc_local() at link_elf_obj_reloc_local+0x28f
link_elf_obj_load_file() at link_elf_obj_load_file+0xb6f
linker_load_file.part.3() ... sys_kldload() ...
Debugger("panic")
The module's single .rela section carries sh_info = 0 (the SHT_NULL
section). The scan stores it verbatim (sec = sh_info, link_elf_obj.c:355/:775),
and since section 0 never becomes a progtab entry, findbase() returns 0 and
link_elf_obj_reloc_local (called during load, :818β:1243-1245 / during boot
for preloads :368) executes panic("lost base for relatab") instead of
rejecting the file. Same shape at :975-977/:1002-1004 in relocate_file.
Expected β patched kernel (fix.diff applied, kernel #1 Sep 1 2026)
kldload: an error occurred while loading module /tmp/df2772.ko ... RC=1 dmesg: linker_load_file: Unsupported file type
Module rejected at scan time by elf_obj_reloc_target_ok(), no panic, guest up.
Threat model
Root-supplied-input class (SYSCAP_NOKLD), like DF-0040/0041/0042. The same panic is also reachable from the preload path (boot-time module metadata), so a corrupted/substituted preloaded module bricks the boot.
DF-2772 β VERDICT
REPRODUCED (panic / DoS, root-gated).
Root cause (sys/kern/link_elf_obj.c)
REL/RELA sections are recorded with their raw sh_info as the target-section
number, with no validation that the target is (or will be) a loaded
PROGBITS/NOBITS section:
772: case SHT_REL:
773: ef->reltab[rl].rel = kmalloc(shdr[i].sh_size, ...);
774: ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
775: ef->reltab[rl].sec = shdr[i].sh_info; /* raw, unchecked */
...
791: ef->relatab[ra].sec = shdr[i].sh_info; /* raw, unchecked */
Later, findbase() (:940-953) returns 0 unless some progtab entry carries
that exact section index, and both relocation drivers turn 0 into a panic:
975: base = findbase(ef, ef->reltab[i].sec);
976: if (base == 0)
977: panic("lost base for reltab");
...
1002: base = findbase(ef, ef->relatab[i].sec);
1003: if (base == 0)
1004: panic("lost base for relatab");
and identically in link_elf_obj_reloc_local (:1243-1245, :1265-1267), which
runs from link_elf_obj_load_file:818 (kldload path) and
link_elf_obj_preload_file:368 (boot path). Any crafted module with a
nonempty REL/RELA section whose sh_info names the NULL section (0), a
STRTAB/SYMTAB section, an index β₯ e_shnum, or a size-0 PROGBITS section
(size-0 sections are never entered into progtab, :518-519/:697-698) panics
the kernel instead of being rejected with an error.
Reproduction (baseline, stock kernel)
Guest: DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 β¦
X86_64_GENERIC, securelevel -1. gen_module.py df2772.ko panic-rela emits a
514-byte ET_REL object: .text, .symtab (1 null sym), .strtab, .shstrtab, and
one .rela (24 bytes, sh_info=0). kldload /tmp/df2772.ko β ssh hangs;
serial console (panic.txt):
panic: lost base for relatab
link_elf_obj_reloc_local() at link_elf_obj_reloc_local+0x28f
link_elf_obj_load_file() at link_elf_obj_load_file+0xb6f
linker_load_file.part.3() at linker_load_file.part.3+0x92
linker_load_module() at linker_load_module+0x116
sys_kldload() at sys_kldload+0xc6
Debugger("panic")
Reproduced twice on fresh vm.sh reset with-src boots (first run, then a
re-run on a fresh reset to capture the clean excerpt).
Impact ceiling
Deterministic kernel panic from a crafted module β a robustness/DoS defect,
not a corruption primitive (nothing is written; the panic is the whole
effect). kldload is root-only (caps_priv_check_self(SYSCAP_NOKLD),
kern_linker.c:792; blocked at securelevel>0), so unprivileged reach is nil;
the interesting real-world framing is a corrupted or substituted module
(incl. a preloaded one, which converts this into a boot-time brick). Low
severity, consistent with DF-0040/0041/0042 calibration.
Fix validation
fix.diff (same combined patch as DF-2771/DF-2773): new
elf_obj_reloc_target_ok() validates sh_info < e_shnum + target type
PROGBITS/NOBITS (+ nonempty for the file case) in both scan loops
(preload :234-239, file :530-535) before the section is counted, and the four
panic("lost base β¦") sites become return (ENOEXEC) with the error
propagated (link_elf_obj_reloc_local becomes int-returning; callers :368
and :818 check it). Rebuilt kernel (make -j6 nativekernel
KERNCONF=X86_64_GENERIC, build.log, -Werror clean), installed as #1: Tue
Sep 1 01:04:57 UTC 2026, rebooted. Same PoC:
kldload: an error occurred while loading module /tmp/df2772.ko. Please check dmesg(8) dmesg: linker_load_file: Unsupported file type RC=1, guest stays up (ALL-SURVIVED), 0 panics
Baseline panic gone β fix_status=fixed.
Note on scope
The scan-time rejection intentionally runs before the nprogtab/nsym
checks; the goto-out it takes was made safe by the DF-2773 unload guard in
the same patch (previously a scan-time rejection with nrelatab>0 and
relatab==NULL would itself have NULL-derefed in unload).
Fix verification
fixedPatched kernel rejects the identical module with ENOEXEC ('linker_load_file: Unsupported file type' in dmesg, RC=1), no panic, guest stays up; baseline kernel #0 panics on the same input.
run.log (baseline vs patched), build.log (kernel #1 build), dmesg 'linker_load_file: Unsupported file type'
Confirmed kernel references
Detail
Exploit chain
root crafts module (gen_module.py mode=panic-rela, .rela sh_info=0) -> kldload -> link_elf_obj_load_file -> link_elf_obj_reloc_local -> findbase()==0 -> panic('lost base for relatab') -> kernel halt in ddb. No memory corruption and no unprivileged reach (SYSCAP_NOKLD); ceiling is deterministic panic/DoS.
Evidence (decisive lines)
['panic.txt β baseline serial console: panic: lost base for relatab with full backtrace through link_elf_obj_load_file and sys_kldload', 'run.log β baseline (2 runs, ssh hang + panic) vs patched (clean rejection RC=1) transcripts', 'gen_module.py β trigger generator (mode panic-rela): shdr[5] type=SHT_RELA size=0x18 link=2 info=0', 'build.log β full untrimmed nativekernel build of the fix (-Werror clean)', 'VERDICT.md β path:line trace incl. preload reachability and all four panic sites', 'fix.diff β scan-time sh_info validation + panic-to-error conversion, baseline-vs-patched validated']
PoC changes
n/a β trigger authored fresh (hand-crafted ET_REL); no prior seed for this finding
Verified recommended fix
Validate REL/RELA sh_info at scan time (index < e_shnum and target is a to-be-loaded PROGBITS/NOBITS section) and return ENOEXEC instead of panic()ing at the findbase() call sites.
Verdict
link_elf_obj stores each REL/RELA section's sh_info verbatim as the relocation target (sys/kern/link_elf_obj.c:775/:791) and later turns any target that is not a loaded PROGBITS/NOBITS section into panic('lost base for reltab/relatab') (:975-977, :1002-1004, and link_elf_obj_reloc_local :1243-1245/:1265-1267, reached from kldload via :818 and from boot preload via :368) instead of rejecting the file. A 514-byte crafted ET_REL module with one .rela whose sh_info=0 panics the stock guest deterministically (reproduced twice on fresh resets) β panic backtrace link_elf_obj_reloc_local <- link_elf_obj_load_file <- sys_kldload. Robustness/DoS only (no write primitive); trigger is root-supplied (SYSCAP_NOKLD), and the same panic from the preload path converts a corrupted boot module into a boot-time brick. fix.diff validates sh_info at scan time (elf_obj_reloc_target_ok) and converts the panics to ENOEXEC error returns; on rebuilt kernel #1 the same module is cleanly rejected ('Unsupported file type', RC=1, no panic, guest up).
No comments yet.