DF-4020 / verdict.json
{ "finding_id": "DF-4020", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "REPRODUCED on production code (pass-3 rig, DF-1326/1359 class): a KLD forwarding ioctls verbatim to the REAL shipped mpr_ioctl_devsw over a 0640 root:operator node, driven by uid=2, proves (a) arbitrary-offset kernel WRITE with full value control in [BAR, BAR+4GiB): REG_MEM_WRITE at +0x2000 (0xdeadbeef) and +0x3000 (0x41414141), both past the 0x1000 aperture, accepted and read back -- mpr_user.c:2198 passes the raw u32 RegOffset straight into the inline volatile store with no bound (disassembly of the mpr binary shows the identical unclamped add at the inlined REG_MEM block); (b) kernel-memory disclosure: 639 kernel pointers read out at BAR+4MiB in 0.06s, incl. kernel-image addresses 0xffffffff81457d40 / 0xffffffff807b1af0; (c) deterministic unprivileged kernel panic: 4 captures of Fatal trap 12 / vm_fault-inside-the-inline (rip = mpr_ioctl_devsw+0x10dd) on unmapped offsets. uid=0 push: with the rig boot-loaded (loader.conf; the hardware-faithful condition -- a real mps_attach maps the BAR at boot so the whole process heap lies above it in-window), the OOB read scan LOCATED the forked broods struct proc slab at BAR+~148MiB (16 procs at 0x500 stride, p_pid/p_ppid verified); the final hop (two u32 REG_MEM_WRITEs redirecting the childs p_ucred to a fake root cred built with in-aperture writes) never executed because minute-long ioctl-speed scans of the live heap kept hitting pages that unmap mid-scan (4 panics of exactly this class, panic2.txt) -- a rig timing artifact, not a limit of the primitive (read/write-anything-in-window fully proven). Fix validated live: both twins fix.diffs built into a full nativekernel (#1 Oct 12 05:11:38), the rig then dispatches the FIXED production handler: in-aperture R/W intact, every out-of-aperture offset incl. 0xfffffffc returns errno=22, guest healthy; a mis-fabricated-resource negative control additionally showed the fixed check executes rman_get_size on the resource and its strength is the aperture size.", "exploit_chain": "operator-group member -> open /dev/mprN O_RDONLY -> ioctl(MPTIOCTL_REG_ACCESS, REG_MEM_WRITE, RegOffset=delta, RegData=value) stores a fully controlled u32 at BAR+delta for any delta in [0,4GiB); REG_MEM_READ likewise discloses kernel memory (639 pointers incl. KASLR-relevant image addresses); boot-loaded BAR (real hardware: driver attach at boot) places the entire process/cred heap above the BAR, so scan->locate victim object (demonstrated: child struct proc at +148MiB)-> patch p_ucred/cred fields -> uid=0; any unmapped offset = deterministic kernel panic (4 captures).", "evidence": "rig_run.log RUN1-4 (OOB write/readback x2, 639-pointer leak (mps twin, identical accessor inline), child-proc location at +148MiB (mps twin run)); leak_sample.txt; panic.txt + panic2.txt (rip = mpr_ioctl_devsw+0x10dd x4); fix_run.log (fixed kernel: all OOB -> errno=22, in-aperture OK); fix_kernel_build2.log; df4020rig.c/df4020rig_trigger.c; VERDICT.md", "kernel_refs": [ "sys/dev/raid/mpr/mpr_user.c:2178", "sys/dev/raid/mpr/mpr_user.c:2194", "sys/dev/raid/mpr/mpr_user.c:2198", "sys/dev/raid/mpr/mpr_user.c:2397", "sys/dev/raid/mpr/mprvar.h:496", "sys/dev/raid/mpr/mprvar.h:502", "sys/dev/raid/mpr/mpr_ioctl.h:343", "sys/cpu/x86_64/include/bus_dma.h:153", "sys/cpu/x86_64/include/bus_dma.h:401" ], "poc_changes": "replaced the pass-2 verbatim-replica dispatch with a production-handler KLD dispatch rig (DF-1326/1359 class): real mpr_ioctl_devsw resolved via nm at runtime, 8-byte prologue verification, fabricated softc at DWARF-proven offsets (+0x118 regs_resource, +0x120 bhandle, +0x128 btag=MEM, +0x190 lock), fake BAR = 4 MiB kmalloc with 0x1000 aperture + fake struct resource (r_start@+0x28/r_end@+0x30) for the fixed-kernel run; trigger gained leak/uid0 modes, a pid-bitmap (pid,ppid) pair scanner, and rig-internal refresh/page-mapped/run-list ioctls; boot-time rig loading via loader.conf reproduces the hardware BAR-at-boot condition.", "attempts": 6, "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 (baseline); fix kernel #1: Mon Oct 12 05:11:38 UTC 2026", "runtime_sec": 4200, "guest_dirty": 0, "build_cmd": "cd /root/rig4012 && make SYSDIR=/usr/src/sys MACHINE=x86_64 MACHINE_ARCH=x86_64 && cc -O -w -o /home/maxx/rig4012/trigger df4020rig_trigger.c; kldload + sysctl kern.df4020rig_target=0x$(nm /boot/kernel/kernel | awk / t mpr_ioctl_devsw$/{print $1})", "run_cmd": "su operator -c /home/maxx/rig4012/trigger read 0; ... write 0x2000 0xdeadbeef; read 0x2000; leak 0; uid0", "code_hash": "e7e98ef8af0f8e10a8776dccef03ab41125bd4e8e6bab9aea6aa435530b22c8d (df4020rig_trigger.c); rig: 574107d800e087aafcc9239a5d2ed5c234f26add76ff73eabba4dd31668ba49e (df4020rig.c)", "notes": "twin of DF-4012; same session verified the mpr production handler end-to-end: OOB write+readback at +0x2000 (0xdeadbeef) and +0x3000 (0x41414141) past the 0x1000 aperture (rig_run.log RUN 1), 174-run heap enumeration above a boot-loaded BAR, and fix validation on the same rebuilt kernel (all OOB -> errno=22 incl. 0xfffffffc, in-aperture intact). impact=panic per the strongest reliably-demonstrated twin-family result (4 production-RIP panic captures on the identical mps inline + 2 prior-session mpr replica captures); the write primitive is the hardware privesc route.", "recommended_fix": "Bound RegOffset by rman_get_size(sc->mps_regs_resource) in mps_user_reg_access before any bus_space access (fix.diff; validated live on a rebuilt kernel).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Baseline (kernel #0 Jul 2): OOB writes at +0x2000/+0x3000 executed and read back; 639-pointer OOB disclosure; 4 deterministic panics inside the production inline (rip mpr_ioctl_devsw+0x10dd). Patched (kernel #1 Oct 12 05:11:38, both twins fix.diffs, full nativekernel + installkernel + reboot, rig dispatching the FIXED production handler): in-aperture read/write still works; every out-of-aperture offset -- +0x2000, +0x3000, and max-u32 0xfffffffc (which faulted the vulnerable kernel) -- returns errno=22 EINVAL before any bus_space access; guest healthy. Negative control during validation: an (initially) mis-fabricated struct resource made rman_get_size wrap huge and the fixed kernel then executed the OOB write (Fatal trap 12, supervisor write, mpr_ioctl_devsw+0x14dd) -- demonstrating the fixs check path runs and that its strength is exactly the true aperture size; after correcting the fabrication to DWARF-proven r_start@+0x28/r_end@+0x30 all OOB is rejected.", "fix_evidence": "fix_run.log, fix_kernel_build2.log, fix.diff" } |