{
  "finding_id": "DF-4800",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "Concurrent revive_block() calls race sd->revived (read at vinumrevive.c:89, advanced at :187 across two biowaits, no lock on the whole VINUM_SETSTATE->start_object path): both callers copy the same block and both advance the cursor, so the block between the advances is silently skipped; set_sd_state(sd_up) at :190 then declares the subdisk up with never-revived content. Reproduced 3/3 on async (virtio) backing with 152/152/153 corrupted 4k blocks of 1536 persisting after a clean single-reviver finish (~620 KB of silent stale data served by an up mirror; no error logged). On md (malloc) backing the non-MPSAFE vinum ioctls mplock plus inline completions serialize the callers by accident - real async disks release the mplock in biowait and the race becomes the default. Fix (per-sd revive_busy cmpset interlock returning EBUSY to the second caller) validated on the same guest: second racer refused at ioctl 1, survivor copies every block, data windows identical.",
  "exploit_chain": "root: mirrored volume on async-backed drives, sd stale, TWO concurrent vinum start (or one VINUM_SETSTATE(object_up) ioctl racing the revive) -> both read cursor R, both copy [R,R+N), both advance -> [R+N,R+2N) skipped -> sd up with holes -> unprivileged readers of a mounted filesystem silently receive the pre-revive content; concurrent completion also races set_sd_state/save_config on the shared config",
  "evidence": "run.log/run.2.log/run.3.log (stock: total corrupted 4k blocks: 152/152/153 of 1536, state lists plexes up); fix_run.log (patched: child 907: 1 ioctls, final error=16 + DATA WINDOWS IDENTICAL)",
  "kernel_refs": [
    "sys/dev/raid/vinum/vinumrevive.c:89",
    "sys/dev/raid/vinum/vinumrevive.c:187",
    "sys/dev/raid/vinum/vinumrevive.c:190",
    "sys/dev/raid/vinum/vinumstate.c:880",
    "sys/dev/raid/vinum/vinumioctl.c:230"
  ],
  "poc_changes": "wrote racer.c (N tight-loop VINUM_SETSTATE(object_up,blocksize) drivers) after discovering userland vinum start sleeps between ioctls (revive interval) which hides the race; async virtio backing required because md inline completions keep the non-MPSAFE ioctls mplock held end-to-end",
  "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": 1500,
  "guest_dirty": 0,
  "build_cmd": "cc -O -o /tmp/racer /tmp/racer.c (fix: cd /usr/src/sys/dev/raid/vinum && make obj && make, module)",
  "run_cmd": "sh setup_async.sh && sh run.sh  (2 racers x 150 ioctls + single-reviver finish + data-window compare)",
  "code_hash": "f338d6256cc9c2238293af35fb3845b911c001bccf2e263b8319be7c5c4c2c8a",
  "notes": "corruption impact=none in the memory-safety sense; classified dos/data-integrity. md-only guests cannot reproduce (mplock+inline serialization) - use async backing",
  "recommended_fix": "per-sd revive_busy atomic interlock in revive_block(): second concurrent caller gets EBUSY (fix.diff, validated)",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "patched module: second racer refused with EBUSY on its first ioctl; surviving racer advanced the cursor exactly once per block; after the single-reviver finish the two plex data windows were byte-identical (fix_run.log) vs 152-153 corrupted 4k blocks on stock in 3/3 runs",
  "fix_evidence": "fix_run.log"
}