DF-2854 / verdict.json
{ "finding_id": "DF-2854", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED on stock kernel as UNPRIVILEGED user (uid 1001, securelevel -1): reading hw.bus.rman returns partially-initialized kernel stack - u_resource.r_devname[32] and u_rman.rm_descr[32] are only written up to their NUL (strlcpy at :684; ksnprintf/'nomatch'/'\\0' at :707-716) yet SYSCTL_OUT copies the full 80/64-byte structs (:690, :722). 88 resources + 5 rman descriptors exported; 99 leak marks per run, byte-identical across 3 runs; the residue contains the live kernel pointer 0xfffff80117ee5800 in every r_devname tail plus ff-byte tail padding (DF-0092's 4 bytes are a subset). Sibling handler sysctl_devices bzeros its export struct (subr_bus.c:3892) - sysctl_rman forgot; this finding is the partial-field-initialization surface beyond DF-0092's documented padding-only scope. Fix (bzero before fill) validated on rebuilt kernel: 0 leak marks, all tails zero.", "exploit_chain": "unpriv user -> sysctl hw.bus.rman.<gen>.<rman>.<res> -> kernel copies 80-byte stack struct with only named prefix fields set -> up to 31 uninitialized stack bytes per entry (kernel pointer 0xfffff80117ee5800 demonstrated) -> KASLR-defeat / heap-layout reconnaissance for adjacent memory bugs. Read-only primitive, no write.", "evidence": [ "run.log: r_devname tail of 'acpi0' entry: '61 63 70 69 30 00 ff ff 00 58 ee 17 01 f8 ff ff' (0xfffff80117ee5800 leaked)", "run.log: 'tail pad bytes 76-79: ff ff ff ff' (DF-0092 subset)", "leak_sample.txt: identical output across runs 2 and 3 (stable residue)", "fix_run.log: patched kernel => leakmarks=0, r_devname tails and pad bytes all zero" ], "kernel_refs": [ "sys/kern/subr_rman.c:653-654", "sys/kern/subr_rman.c:684", "sys/kern/subr_rman.c:702-722", "sys/kern/subr_rman.c:734", "sys/kern/subr_bus.c:3892", "lib/libdevinfo/devinfo.c:263-345" ], "poc_changes": "No seed. Walker mirrors lib/libdevinfo OID resolution. First cut mis-declared struct u_businfo ({u32 generation,int devices} instead of kernel's {int ub_version,int ub_generation}, sys/sys/bus.h:67) so every sysctl returned EINVAL until fixed. Run via vm.sh run_user (unprivileged maxx).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 95, "guest_dirty": 0, "build_cmd": "in guest as any user: cc -O -Wall -o rman_leak rman_leak.c", "run_cmd": "./rman_leak (unprivileged)", "code_hash": "7eb4e418b54f3a8b2beedb4f4b6f681e65db46034cf39d98ab238bbb9de67cc6", "notes": "Beyond DF-0092's 4 padding bytes: the partial-field (post-NUL) surface is up to 31 bytes/entry and demonstrably carries kernel pointers. The intentional pointer exports (r_handle/r_parent/r_device) are by-design upstream (devinfo correlation) and are NOT the finding; the uninitialized stack is.", "recommended_fix": "bzero(&urm,sizeof(urm)) and bzero(&ures,sizeof(ures)) before filling in sysctl_rman (mirrors sysctl_devices' bzero at subr_bus.c:3892); subsumes DF-0092's padding fix.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep 2 12:02:45 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (two bzeros) applied together with DF-2853's guard; single nativekernel rebuild + installkernel + reboot. Exact PoC re-run as unprivileged user: leakmarks=0 (was 99), r_devname/rm_descr tails and tail pad bytes all zero. Leak is gone.", "fix_evidence": [ "findings/poc/DF-2854/fix_run.log (patched-kernel full run)", "findings/poc/DF-2854/fix.diff" ] } |