{
  "finding_id": "DF-5327",
  "status": "reproduced",
  "reproduced": true,
  "impact": "peripheral refcount desync (deterministic 2->1->0 with the ch device OPEN, then close 0->0xFFFFFFFF silent underflow) plus premature free: cam_periph_invalidate() at refcount==0 runs camperiphfree()/chcleanup() and kfrees softc+periph while the descriptor is still open (dmesg removing device entry + postmortem of the freed chunk) -- same memcorrupt class and privilege gate as DF-5273, but via chopen()s flag-before-hold ordering in a driver DF-5273 did not cover",
  "confidence": "certain",
  "verdict": "chopen() sets CH_FLAG_OPEN (or drops a secondary openers acquire) before cam_periph_hold(PCATCH); on a signal-interrupted hold its error path releases one reference too many, undercounting the periph refcount per interrupted open. Reproduced deterministically on the guest: after the interrupted open#2, open#1 completes with refcount==0 while /dev/ch0 is open; device removal then frees periph+softc under the open fd (premature free / UAF-enabling state), and a plain close wraps the count to 0xFFFFFFFF silently (DF-5329). Root-gated (SYSCAP_RESTRICTEDROOT) like the rest of the CAM family; exact-chunk attacker regroom was attempted but the freed chunk stayed parked in a full zalloc magazine (documented miss); no uid0 chase for the same reason as DF-5273 (privileged trigger).",
  "exploit_chain": "open#1 parked via device stall (holds CAM_PERIPH_LOCKED + acquire) -> open#2 signal-interrupted in cam_periph_hold -> chopen error path drops open#1s backing reference (flag still set) -> open#1 finishes at refcount==0 with fd open -> (a) AC_LOST_DEVICE frees periph+softc under the open descriptor, giving any in-flight chclose/chioctl between cam_extend_get() and cam_periph_lock() a use-after-free of the 144-byte M_CAMPERIPH chunk; or (b) plain close -> 0xFFFFFFFF zombie (silent per DF-5329). Escalation beyond the premature-free state is RESTRICTEDROOT-gated and was not chased.",
  "evidence": "run.log (stage A+B: [child1] open#1 returned: refcount=0 then after close: refcount=-1; stage B removing device entry with fd open + postmortem dump of freed chunk 0xfffff8008d640ba0), fixedA.log (patched kernel: 2/1/0 -- bug gone), VERDICT.md table, dmesg in run.log",
  "kernel_refs": [
    "sys/bus/cam/scsi/scsi_ch.c:415",
    "sys/bus/cam/scsi/scsi_ch.c:420",
    "sys/bus/cam/scsi/scsi_ch.c:432",
    "sys/bus/cam/cam_periph.c:382",
    "sys/bus/cam/cam_periph.c:402",
    "sys/bus/cam/cam_periph.c:573",
    "sys/bus/cam/cam_periph.c:581"
  ],
  "poc_changes": "rig evilcd2.c derived from DF-5271/5273s evilcd: added target-1 fake T_CHANGER (inq type 0x08, MODE SENSE pages 0x1d/0x1f), watch=2 ch-periph refcnt/postmortem, defer (park SCSI CCBs), invalidate (AC_LOST_DEVICE), sprayfake; poc_chopen/poc_chopen2 fork children so the parent controls the defer valve (first draft deadlocked by arming defer after open#1)",
  "attempts": 5,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Fri Oct  2 21:19:55 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/EVIL  x86_64 (GENERIC + device ch; vulnerable + patched builds both verified)",
  "runtime_sec": 25,
  "guest_dirty": 1,
  "build_cmd": "cd /root/df5327 && make && cc -O -o poc_chopen poc_chopen.c && cc -O -o poc_chopen2 poc_chopen2.c && kldload ./evilcd2.ko",
  "run_cmd": "sysctl -n debug.evilcd.watch=2 >/dev/null; usched :1 ./poc_chopen2; ./poc_chopen",
  "code_hash": "37b77eace559995826059237af5a79c2849da72b3d18f28292ce87f449308d9a",
  "notes": "kernel rebuilt in-guest as KERNCONF=EVIL (GENERIC+device ch) because ch(4) is not in X86_64_GENERIC and a kld-loaded scsi_ch never gets chinit() called (periphdriver_register does not run the init; boot does). stage B regroom miss: freed 144B chunk parked in a full zalloc magazine, 16384-alloc + 4096-churn spray could not recycle it (documented).",
  "recommended_fix": "chopen(): take cam_periph_hold() BEFORE setting CH_FLAG_OPEN / dropping the secondary openers acquire (mirror cdopen/daopen/saopen)",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff applied in guest /usr/src, kernel rebuilt/reinstalled/rebooted, exact same PoC: interrupted open#2 leaves rc=2 (was 1), open#1 completes at rc=1 with fd open (was 0), close -> 0 (was -1). Desync and underflow eliminated; ordering now matches cdopens proven-safe shape.",
  "fix_evidence": "fixedA.log (patched run) vs run.log stage A (vulnerable run); fix.diff in pack"
}