{
  "finding_id": "DF-4801",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "sd->waitlist is drained ONLY from the tail of a successful revive_block() (vinumrevive.c:198-218; the sole drain site in the tree). The revive is driven one block per ioctl by userland vinum start; when that process stops (killed, SIGSTOP, crash), conflicting writes queued on the waitlist (vinumrequest.c:327-355) sleep in PCATCH-less biowait forever. Reproduced deterministically on the stock kernel: one VINUM_SETSTATE(object_up) ioctl primes sd_reviving with the cursor at 64k; four writers straddling [64k,96k) all queue; all four freeze in D-state, survive kill -9 of writers and dd children, and REMAIN hung after an explicit vinum setstate -f down revive abort (run.log). The queued struct requests and buffers leak until reboot. With a filesystem mounted on the volume, the victims are unprivileged writers. Fix (drain_sd_waitlist() hooked into set_sd_state() on every real transition out of sd_reviving: relaunch if the sd came up, biodone(EIO)+freerq otherwise) validated on the same guest: post-abort ps shows no hung writers (fix_run.log) vs stocks four immortal D-state dds.",
  "exploit_chain": "root: mirrored volume + revive started then stopped (kill the vinum start process mid-revive, or let one VINUM_SETSTATE(object_up) ioctl prime sd_reviving and never drive it again); unprivileged: any write straddling the frozen cursor through a mounted filesystem queues on sd->waitlist -> uninterruptible biowait -> permanent unkillable hang, survives kill -9 and revive abort, only reboot clears it",
  "evidence": "run.log (stock: 4/4 writers D-state, >>> UNKILLABLE HUNG WRITERS <<< after kill -9 and >>> STILL HUNG AFTER ABORT <<< after setstate -f down); fix_run.log (patched: writers gone (drained: fix working))",
  "kernel_refs": [
    "sys/dev/raid/vinum/vinumrevive.c:198",
    "sys/dev/raid/vinum/vinumrevive.c:216",
    "sys/dev/raid/vinum/vinumrequest.c:327",
    "sys/dev/raid/vinum/vinumstate.c:286"
  ],
  "poc_changes": "wrote prime.c (single-ioctl revive primer) because userland vinum setstate -f up loops the ioctl internally until the revive completes; writers restricted to the straddle window [64k,96k) so the trigger never touches the REQUEST_DOWN/trailing-BAD launch path (DF-4721 arm) and shows the hang deterministically",
  "attempts": 2,
  "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": 900,
  "guest_dirty": 0,
  "build_cmd": "cc -O -o /tmp/prime /tmp/prime.c (fix: module rebuild with fix.diff stacked on DF-4482 v2)",
  "run_cmd": "sh hang.sh (setup + prime + 4 straddle writers + kill -9 tests + setstate -f down abort test)",
  "code_hash": "ee4df2159fe26a239b37cb150056131dfcac5b974c8e0e98b0a84c436dc182c2",
  "notes": "deterministic trigger; no race required. The waitlist also leaks the queued requests (kernleak aspect). Fix stacks on the DF-4482 v2 fix (cmpset head-push append + steal drain make the new drain race-safe)",
  "recommended_fix": "drain_sd_waitlist() on every set_sd_state() transition out of sd_reviving: relaunch queued requests when the sd comes up, biodone(EIO)+freerq otherwise (fix.diff, validated)",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "patched module: identical workload, writers queue as before while the revive is frozen, and the setstate -f down abort now drains them - post-abort ps shows no dd/writers left (fix_run.log), vs stock where all four remained D-state indefinitely",
  "fix_evidence": "fix_run.log"
}