SHT_SYMTAB/SHT_STRTAB sh_size truncated into signed int symcnt/strcnt β negative kmalloc()/vn_rdwr() lengths β kmem_slab_alloc panic; modulo-wrap and short-read/uninitialized-buffer variants
| Field | Value |
|---|---|
| ID | DF-2811 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:H |
| CWE | CWE-190 / CWE-197 |
| File | sys/kern/link_elf.c |
| Lines | 610-623 (:590 product; buffers non-M_ZERO) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-31 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | memcorrupt |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
The ddb-symbol path stores 64-bit sh_size into int symcnt/strcnt and
uses them as kmalloc sizes and vn_rdwr lengths unchecked. sh_size=
0x80000100 β β2147483392 β kmalloc(0xFFFFFFFF80000100) β panic.
sh_size β₯ 2^32 wraps modulo 2^32 to under/zero-sized allocations
silently accepted; the reads ignore resid so short files leave the
non-M_ZERO kmalloc buffers holding stale kernel heap that the
exhaustive lookup and search then parse as Elf_Sym/strings. Also covers
the e_shnum*e_shentsize int product at :590. VERIFIED on stock:
panic: kmem_slab_alloc(): kernel_map ran out of space! with trace
_kmalloc β link_elf_load_file. Patched: "Symbol table too large"
ENOEXEC.
Recommended fix
Reject sh_size > INT_MAX for symtab/strtab before the kmallocs, bound symstrindex < e_shnum, validate e_shentsize == sizeof(Elf_Shdr) (fix.diff hunks 4-5).
Timeline
- 2026-08-31 Discovered during pass-2 audit of link_elf.c (GLM 5.3); allocator panic reproduced + fix validated same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2811 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| gen_exec_ko.py | β | 6.3 KB | view raw | |
| build.sh | β | 62 B | view raw | |
| run.sh | β | 319 B | view raw | |
| run.log | β | 692 B | view raw | |
| panic.txt | β | 2.2 KB | view raw | |
| env.txt | β | 512 B | view raw | |
| fix.diff | β | 4.1 KB | view raw | |
| run_fix.log | β | 1.4 KB | view raw |
DF-2811 β SHT_SYMTAB/SHT_STRTAB sh_size truncated into int symcnt/strcnt β negative kmalloc()/vn_rdwr() lengths β kernel panic
Build
python3 gen_exec_ko.py shsize-trunc.ko shsize-trunc
Run
scp -F dfbsd-qemu/config shsize-trunc.ko dfbsd:/tmp/ dfbsd-qemu/vm.sh run_root 'kldload /tmp/shsize-trunc.ko; echo RC=$?' dfbsd-qemu/vm.sh log 30
Expected (stock kernel, verified 2026-09-01)
panic: kmem_slab_alloc(): kernel_map ran out of space! Trace: kmem_slab_alloc() at kmem_slab_alloc+0x42b _kmalloc() at _kmalloc+0x5be link_elf_load_file() at link_elf_load_file+0x5cb linker_load_file.part.3() ...
Expected (patched kernel)
kldload: Symbol table too large, RC=1, guest stays up
Why (root cause, sys/kern/link_elf.c)
The ddb-symbol loading path stores 64-bit section sizes into signed 32-bit ints and feeds them to kmalloc()/vn_rdwr() unchecked:
414: int symcnt;
415: int strcnt;
...
610: symcnt = shdr[symtabindex].sh_size; /* Elf64_Xword -> int */
611: ef->symbase = kmalloc(symcnt, ...); /* 0x80000100 -> -2147483392 */
612: strcnt = shdr[symstrindex].sh_size;
613: ef->strbase = kmalloc(strcnt, ...);
614: vn_rdwr(UIO_READ, vp, ef->symbase, symcnt, ...); /* huge size_t */
sh_size = 0x80000100 (> INT_MAX) truncates to the negative int
-2147483392; sign-extension turns it into a ~2^63 kmalloc() which
kmem_slab_alloc cannot satisfy β panic (with M_WAITOK and no M_NULLOK the
allocator panics instead of returning NULL). A sh_size in
(0x100000000+x) wraps modulo 2^32 and yields an under-sized allocation
silently accepted as a "successfully loaded" symbol table; the reads at
:614/:619 also ignore resid, so short files leave the non-M_ZERO buffers
partially populated with stale kernel heap that is then parsed as
Elf_Sym/string data by link_elf_lookup_symbol's exhaustive fallback
(:846-858) and link_elf_search_symbol (:899-913).
Sibling int math in the same path: nbytes = hdr->e_shnum *
hdr->e_shentsize (:590) can overflow int for e_shentsize near 0xffff
(kept in check only after the e_shentsize==sizeof(Elf_Shdr) validation
added by fix.diff).
DF-2811 VERDICT β reproduced (panic via sh_sizeβint truncation)
Status: reproduced. Stock guest kernel
DragonFly 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026 X86_64_GENERIC,
fresh vm.sh reset with-src before the run.
Evidence
link_elf_lookup_symbol: missing symbol hash table (from the earlier localsym run)
panic: kmem_slab_alloc(): kernel_map ran out of space!
cpuid = 1
Trace beginning at frame 0xfffff80118497608
kmem_slab_alloc() at kmem_slab_alloc+0x42b
kmem_slab_alloc() at kmem_slab_alloc+0x42b
_kmalloc() at _kmalloc+0x5be
link_elf_load_file() at link_elf_load_file+0x5cb
linker_load_file.part.3() at linker_load_file.part.3+0x92
linker_load_module() at linker_load_module+0x116
Debugger("panic")
(run.log, panic.txt)
The module's SHT_SYMTAB has sh_size = 0x80000100. int symcnt
(sys/kern/link_elf.c:414, assigned at :610) receives the truncated value
-2147483392; kmalloc(symcnt, ...) at :611 sign-extends it to
0xFFFFFFFF80000100 bytes; the oversized path in _kmalloc reaches
kmem_slab_alloc which, unable to satisfy a ~2^63 request and not allowed
to return NULL for this allocation class, panics the kernel.
Root cause
sys/kern/link_elf.c:610-623 β 64-bit Elf64 section sizes (sh_size) stored into signed 32-bit ints (declared :414-415) and used as kmalloc() sizes and vn_rdwr() lengths with no range validation, no resid (short-read) check, and no M_ZERO on the buffers (:611/:613 use M_WAITOK only). Variants: - sh_size > INT_MAX with bit 31 set β negative β this panic; - sh_size β₯ 2^32 β modulo truncation β under-sized/zero-sized allocation silently accepted as a valid symbol table; - truncated file β short read accepted (resid ignored) β stale heap parsed as Elf_Sym/strings by the ddb lookup paths (:846-:858, :899-:913).
Distinct from DF-0057 (e_shentsize) / DF-0058 (sh_link) which concern the shdr array itself; this is the sh_size consumption sink.
Impact
Root-gated (kldload) crafted-module kernel panic (DoS). Low per the root-supplied rubric (cf. DF-2772's sh_info panic, Low).
Fix validation
fix.diff rejects sh_size > INT_MAX for both symtab and strtab before the
kmallocs (and bounds symstrindex, validates e_shentsize, so the
e_shnum*e_shentsize int product can no longer overflow either). Patched
kernel: kldload: Symbol table too large, RC=1, guest stays up. See
run_fix.log.
Fix verification
fixedPatched kernel: 'kldload: Symbol table too large', RC=1, guest up, no allocator panic (run_fix.log).
['run_fix.log', 'fix_build.log']
Confirmed kernel references
Detail
Exploit chain
crafted .ko -> kldload(root) -> int symcnt = 0x80000100 (negative) -> kmalloc(sign-extended ~2^63) -> kmem_slab_alloc panic (M_WAITOK, no NULL return path).
Evidence (decisive lines)
['run.log: panic trace kmem_slab_alloc() <- _kmalloc() <- link_elf_load_file()', 'panic.txt', "run_fix.log: 'kldload: Symbol table too large', RC=1, guest up"]
PoC changes
authored fresh this pass (gen_exec_ko.py shsize-trunc mode)
Verified recommended fix
reject sh_size > INT_MAX for symtab/strtab before the kmallocs, bound symstrindex < e_shnum, validate e_shentsize (fix.diff)
Verdict
ddb-symbol loading truncates 64-bit sh_size into signed int symcnt/strcnt (link_elf.c:414-415, 610-613) and feeds them to kmalloc()/vn_rdwr() unchecked. Reproduced with sh_size=0x80000100: symcnt=-2147483392 -> kmalloc(0xFFFFFFFF80000100) -> panic 'kmem_slab_alloc(): kernel_map ran out of space!' with trace _kmalloc <- link_elf_load_file. Variants by inspection (certain): sh_size>=2^32 wraps modulo 2^32 to under/zero-sized allocations silently accepted; the :614/:619 reads ignore resid so short files leave the non-M_ZERO buffers holding stale heap later parsed as Elf_Sym/strings by the exhaustive lookup (:846-858) and search (:899-913). Root-gated DoS; Low (cf. DF-2772).
No comments yet.