# DF-4800 verdict — REPRODUCED (silent stale data on an "up" mirror, 3/3), fix validated

- finding: concurrent `revive_block()` calls race `sd->revived` (read at
  `sys/dev/raid/vinum/vinumrevive.c:89`, advanced at `:187` after two
  biowaits): both callers copy the same block and both advance the
  cursor, so the block between the two advances is silently skipped.
  `set_sd_state(sd_up)` at `:190` then brings the subdisk up with
  never-revived content; volume reads round-robin across plexes and
  serve the stale data. No lock anywhere on the path
  (`vinumioctl.c:230` VINUM_SETSTATE → `setstate` → `start_object`
  `vinumstate.c:880` → `revive_block`).
- guest: DragonFly dfbsd 6.5-DEVELOPMENT #0 (stock kernel, stock
  module), 3 MB mirrored concat volume on two GPT partitions of a
  second virtio-blk disk (async completions open the mplock window that
  the inline md backend coincidentally closes).

## Runs (stock)

| # | workload | result |
|---|----------|--------|
| 1 | 2 racers × 150 ioctls + single finisher | **152** corrupted 4k blocks persist after `up` |
| 2 | same | **152** |
| 3 | same | **153** |

Mid-race snapshot (run 1): 620 differing blocks = 468 uncopied tail +
152 already-skipped. After the single-reviver finish, exactly the
skipped blocks remain (~620 KB of zeros vs pattern). `vinum list`
reports both plexes `up`; no error is logged anywhere — the corruption
is completely silent.

Control: the same two-racer workload on **md (malloc) backing** does not
skip (cursor advanced exactly once per ioctl): the non-MPSAFE vinum
ioctl (`vinum.c:58 { "vinum", 0, D_DISK }`, no D_MPSAFE →
`dev_needmplock`) holds the MP lock across the whole call, and inline md
completions never sleep, so the kernel passively-never releases the mp
lock. On real (async) disks the biowait sleeps release it
(lwkt passive release) and the race is the default behavior. This also
explains why the interlock-free code only ever "worked" by accident.

## Fix validation

`fix.diff` (per-sd `revive_busy` cmpset interlock, EBUSY to the second
caller, cleared on both exits), built as a module on the same guest
(together with the DF-4482 v2 fix):

```
child 907: 1 ioctls, final error=16 msg=            (EBUSY, refused)
child 908: stopped after 150 ioctls (bounded)
RESULT: DATA WINDOWS IDENTICAL (no skipped blocks)   (fix_run.log)
```

fix_status: **fixed** (baseline_reproduced=1, patched_reproduced=0).

## Exploitability

Demonstrated impact is silent data corruption (integrity), not memory
corruption. Trigger needs root to start two revives of the same
subdisk (double `vinum start`, retry scripts, or a stuck starter plus a
manual restart); victims are any readers of the volume (e.g. an
unprivileged user reading a filesystem mounted on it).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
