DF-4749 / verdict.json
{ "finding_id": "DF-4749", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "Reproduced live on the DragonFly 6.5-DEVELOPMENT guest: pt_prison is never assigned (kmalloc M_ZERO, no writer anywhere; sole reference is the guard at nmdm.c:211), so the nmdm prison check is inert for unjailed users, degenerates to unconditional EBUSY-on-reopen for jailed users (same-jail reopen of /dev/nmdm0A failed Device busy while the jail itself held it), and never enforces pair<->prison affinity (unprivileged host user maxx successfully opened the jail-held half). Not memory-safety: pure logic/isolation defect; impact capped Low because direction 2 needs a jail devfs that exposes nmdm. Fix (pin pt_prison + prison_hold on first open) validated by rebuilding nmdm.ko on the guest and A/B-ing: post-fix same-jail reopen succeeds and cross-prison attach returns EBUSY.", "exploit_chain": "", "evidence": "run.log (pre-fix both directions: Part A inert guard, Part B same-jail EBUSY + unpriv host attach ok); jailtest_baseline.log (raw run, holder pid verified); fix_run.log (post-fix inversion, decisive lines open#1... = 3 [ok] inside jail and holder: open(/dev/nmdm0A) FAILED: Device busy for maxx); fix_build.log (patched module build)", "kernel_refs": [ "sys/dev/misc/nmdm/nmdm.c:88", "sys/dev/misc/nmdm/nmdm.c:126", "sys/dev/misc/nmdm/nmdm.c:211", "sys/kern/kern_prot.c:1223", "sys/kern/kern_jail.c:154" ], "poc_changes": "Probe written fresh (open-matrix + holder); jailed side uses legacy jail(8) syntax `jail / name 127.0.0.1 cmd` since the guest jail(8) has no -c support; devfs note: jail path=/ shares host devfs.", "attempts": 3, "guest_uname": "DragonFly dfbsd 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": 240, "guest_dirty": 1, "build_cmd": "cc -O -Wall -o /tmp/nmdm_probe nmdm_probe.c && cc -O -o /tmp/holder holder.c", "run_cmd": "./run.sh", "code_hash": "f2539dc7ce9413f4d7e8b7448d770a0c06d6e781acf2afdf20c97d34b9a1e3f8", "notes": "prison_hold in fix bounded by 128 pairs max (nmdminit: n & ~0x7f). Direction-2 reachability requires jail devfs exposing nmdm (non-default); direction-1 (same-jail reopen broken) is unconditional for jails. Baseline module restored path: guest was reset after the run.", "recommended_fix": "Assign pt_prison (with prison_hold) on first open of a half in nmdmopen(), restoring the FreeBSD pty-heritage prison pinning.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src copy, nmdm.ko rebuilt (fix_build.log), module swapped via kldunload/kldload, PoC re-run (fix_run.log): pre-fix buggy behaviors (same-jail EBUSY on open#1; unpriv host attach succeeding) are gone \u2014 post-fix same-jail open#1/#2 succeed and cross-prison attach returns Device busy.", "fix_evidence": "fix_run.log lines: open#1(/dev/nmdm0A) = 3 [ok, TS_ISOPEN set] (inside jail, holder holding) and holder: open(/dev/nmdm0A) FAILED: Device busy (as maxx, jail-held half)" } |