β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2811

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.

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2811 Β· 10 files
FileTypeDescriptionSize
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

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

VERDICT.md
↓ download raw

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

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched kernel: 'kldload: Symbol table too large', RC=1, guest up, no allocator panic (run_fix.log).

['run_fix.log', 'fix_build.log']
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Tue Sep 1 15:20:35 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

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