4-byte OOB read past PT_NOTE segment/page end in bsd_trans_osrel (note_overflow never bounds the descriptor)
Summary
note_overflow bounds only n_namesz while bsd_trans_osrel unconditionally reads *(int32*)(note+24): a note placed at first-page end with p_filesz=22 reads image_header[4096..4099] - fully past the lwbuf-mapped page into the adjacent physical page via DMAP (kmalloc-adjacent variant in the beyond-first-page path). KLD-harness differential: control p_osrel=0x44332211 (in-file) vs bug 0x200a2a2f (adjacent page bytes, present nowhere in the crafted file) 3/3. p_osrel has no userspace reader today so impact is none now, but it is a ready-made 4-byte kernel-memory oracle the moment procstat/sysctl exposes it. Bound-the-whole-note fix validated on a rebuilt kernel.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5630 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| make_note.c | β | 4.8 KB | view raw | |
| df_osrel.c | β | 1.4 KB | view raw | |
| kld/Makefile | β | 56 B | β download | |
| run_guest.sh | β | 1.5 KB | view raw | |
| run.log | β | 1.4 KB | view raw | |
| verdict.json | β | 4.2 KB | view raw | |
| VERDICT.md | β | 3.4 KB | β raw | |
| fix.diff | β | 1.6 KB | view raw | |
| env.txt | β | 287 B | view raw |
DF-5630 VERDICT
Status: reproduced (OOB read proven by value differential) β fix validated
Guest: DragonFly 6.5-DEVELOPMENT x86_64 (X86_64_GENERIC), cc 8.3.
Trigger runs as unprivileged maxx (uid 1001); observability via a
root-loaded KLD sysctl harness (df_osrel.ko, in this pack) that exposes
struct proc::p_osrel β the exact field the OOB read fills.
Chain
execve(crafted ELF, uid 1001)
β exec_elf64_imgact (imgact_elf.c:602)
β get_brandinfo loop 1 (imgact_elf.c:550-561) β DragonFly brand has
BI_BRAND_NOTE (elf_machdep.c:66) with brandnote {10,4,1,"DragonFly"}
(imgact_elf.c:129-136)
β check_note β check_PT_NOTE (imgact_elf.c:1721)
β note_overflow (imgact_elf.c:1700) bounds ONLY n_namesz β 22 bytes
proven; note fields {10,4,1} + vendor "DragonFly\0" all within those
22 bytes β match (imgact_elf.c:1786-1790)
β BN_TRANSLATE_OSREL set (imgact_elf.c:134) β bsd_trans_osrel called
(imgact_elf.c:1792-1794)
β *osrel = *(const int32_t *)(note + 1 + roundup2(10,4)) =
(int32_t)(note+24) (imgact_elf.c:1870-1872) β reads 4 bytes at
offsets 24..27 of a 22-byte-proven region
β with the note at file offsets [4072, 4094): read lands at
image_header[4096..4099] β past the first mapped page into the
adjacent physical page via the DMAP (kern_exec.c:838-839 β
cpu/x86_64/misc/lwbuf.c:95 PHYS_TO_DMAP)
β imgp->proc->p_osrel =
Proof (vulnerable kernel #0)
- control binary (full 28-byte note, descriptor 0x44332211 inside the
segment): execs; KLD harness reads
p_osrel = 1144201745 (0x44332211)β the harness and note plumbing work exactly. - bug binary (22-byte note, declared descriptor beyond segment/page):
execs;
p_osrel = 537537071 (0x200a2a2f)= bytes "/*\n " β present nowhere in the 4095-byte crafted file. 3/3 runs. Only possible source: memory past the mapped first page (DMAP neighbor physical page), i.e. the OOB read. - nonote binary: ENOEXEC (the note was the only branding path).
Impact ceiling on this guest: the 4 bytes land in p_osrel which has no userspace-visible reader (checked: only writes at kern_exec.c:298, imgact_elf.c:867, init_main.c:385) and the read hits DMAP/kmalloc-adjacent memory that stays mapped β no panic, no disclosure today. It is a genuine CWE-125 whose impact is one future p_osrel consumer away (e.g. procstat/sysctl exposure would turn it into a repeatable 4-byte kernel memory oracle), and it can read past a kmalloc chunk in the heap path (imgact_elf.c:1757-1769).
Fix validation (kernel #1, patched, built 23:30 UTC)
- bug binary β "Exec format error" (ENOEXEC): the truncated note is now rejected by the strengthened note_overflow (whole declared note must fit), so no brand, no trans_osrel call.
- control binary β still execs,
p_osrel = 0x44332211β legitimate ABI notes (every real DragonFly binary) are unaffected. - nonote binary β ENOEXEC as before.
Root cause lines: sys/kern/imgact_elf.c:1700-1707 (note_overflow) + 1866-1875 (bsd_trans_osrel), enabled by elf_machdep.c:66-67 brandnote wiring.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedPatched kernel (kernel #1 with fix.diff): bug-note binary rejected with ENOEXEC (was: execs + p_osrel=0x200a2a2f from OOB); control note still execs with p_osrel=0x44332211 (no regression for every real DragonFly binary's ABI note); nonote still ENOEXEC.
run.log 'DF-5630 fix validation' section; VERDICT.md; fix.diff in pack.
Confirmed kernel references
Detail
Exploit chain
execve(crafted ELF EI_OSABI=255, uid 1001) -> get_brandinfo loop 1 (BI_BRAND_NOTE) -> check_PT_NOTE accepts {10,4,1}+'DragonFly\0' within 22 proven bytes -> BN_TRANSLATE_OSREL callback bsd_trans_osrel reads (int32_t)(note+24) past segment/page end (DMAP neighbor physical page) -> imgp->proc->p_osrel = OOB bytes; observable only via the root-loaded df_osrel.ko sysctl harness (evidence gathering, not part of the attack).
Evidence (decisive lines)
run.log: case2 control p_osrel=0x44332211 vs case3 bug p_osrel=0x200a2a2f (3/3 runs), case1 nonote ENOEXEC; fix validation: bug binary ENOEXEC on patched kernel while control still yields 0x44332211. df_osrel.c KLD harness in pack.
PoC changes
Authored from scratch. Three harness iterations were needed: (1) note offsets must be 4-aligned or check_PT_NOTE breaks at aligned(note, Elf32_Addr); (2) e_entry must lie within [p_vaddr, p_vaddr+p_memsz) or exec_elf64_imgact leaves entry=0 (SIGSEGV at address 0) - used p_memsz=258 to span the stub at 0x400100; (3) CTLTYPE_STRING sysctl harness replaced with RW-int pid + RD-int value pair.
Verified recommended fix
note_overflow(): require the whole declared note (header + rounded name + descriptor) to fit within the remaining segment bytes (see fix.diff).
Verdict
Unprivileged-triggerable 4-byte OOB read: note_overflow() (imgact_elf.c:1700-1707) bounds only n_namesz, but bsd_trans_osrel() (imgact_elf.c:1866-1875) reads the 4-byte descriptor at note+24 of any note whose header matches the DragonFly brandnote {10,4,1,'DragonFly'} bound into the exec path at elf_machdep.c:66-67. A note truncated right after the vendor name (22 bytes) at the end of the first page makes the kernel read image_header[4096..4099] - the adjacent physical page via the DMAP. Proven by value differential with a KLD sysctl harness: control note -> p_osrel=0x44332211 (exact in-file descriptor), bug note -> p_osrel=0x200a2a2f ('/*\n ') which appears nowhere in the crafted file. Silent on this guest (p_osrel has no userspace reader; DMAP read never faults) - Low severity now, a 4-byte kernel-memory oracle the day p_osrel gains exposure. Fix validated: truncated note rejected with ENOEXEC, full notes unaffected.
No comments yet.