# DF-4714 — VERDICT

**status: reproduced** (4/4 decisive runs across 2 baseline boots + patched control boot)
**impact: silent data loss on RAID-5 writes (data corruption) + kernel panic via the DF-4721 arm**
**fix: validated — bad behaviors eliminated on patched kernel**

## Root cause (code trace)

`sys/dev/raid/vinum/vinumraid5.c`, write path of `bre5()`:

- `:363-364` — if the parity subdisk's state < `sd_reborn` (down/crashed),
  `m.badsdno = m.psdno`.
- `:403-404` — for each participating data subdisk with `state != sd_up`,
  `s = checksdstate(...)`; the refusal for a **second** bad subdisk is
  `if (s && (m.badsdno >= 0))` — it requires `checksdstate()` to have FAILED.
- `sys/dev/raid/vinum/vinumstate.c:676-678` — `checksdstate()` returns
  `REQUEST_OK` (0) for **`sd_reborn`** data subdisks on writes (and for
  `sd_reviving` in a parity plex with `sddowncount <= 1`, `:651-661`).
- `:420` — therefore `m.badsdno = mysdno` **overwrites** the earlier
  (parity) marker, or the first bad data subdisk's marker.
- `:443-453` — the `badsdno == psdno` → `XFR_PARITYLESS_WRITE` conversion
  consequently never runs for the parity-down+reborn-data case.

Reachability of the states: a drive flap (down→up) moves every subdisk on
the drive `sd_up→sd_crashed→sd_reborn` (`vinumstate.c:427-438`,
`set_drive_state` calls `update_sd_state` per subdisk). Two subdisks of a
RAID-5 plex on one drive is a legal vinum configuration (used here; also
arises with reborn+reviving pairs because `sd_reborn` is not counted in
`sddowncount`, `vinumstate.c:744-745`).

## What was run (guest DragonFly 6.5-DEVELOPMENT, X86_64_GENERIC)

Setup: 2 md (malloc) disks, GPT vinum partitions, RAID-5 plex
`stripesize 32s`, sds: s0→d1, s1→d2@0, s2→d2@4361s. Volume sectors
[128,192) are stripe group 2 → parity ordinal 0 (s0), data ordinals 1 (s1)
and 2 (s2). All triggers use SINGLE write()/read() calls (a 512-byte dd
issues per-sector bios that only ever touch one data column and hide the
bug — see run.log history for that trap).

### Baseline boot 1 (`run.log`)

1. **Scenario A** — s0 forced down, s1 forced reborn, 16KB single write of
   0xBB at [128,160):
   - write returned SUCCESS;
   - readback returned the OLD contents (0x75) → `*** SILENT DATA LOSS ***`;
   - physical evidence: s0's (DOWN subdisk) drive region changed to 0xCE =
     0xBB^0x75 — the user's new data was folded into parity and written to
     the down subdisk while the data block itself was discarded; s1's
     region never changed.
2. Scenario B first attempt (pre-reset boot, `panic.txt`): 32KB single
   write with s1+s2 reborn → **Fatal trap 12, `launch_requests+0xc2:
   movq (%rax),%rax`, fault VA 0x0** (the DF-4721 launch_requests UAF arm —
   reachable here because the overwrite leaves a trailing
   XFR_BAD_SUBDISK rqe).

### Baseline boot 2

Re-ran scenario B cleanly (plex/volume lifted): identical panic, 2/2.
(`panic.txt`.)

### Patched boot (`run.patched.log`)

Kernel rebuilt with `fix.diff` (this finding + DF-4715 correction):

1. Scenario A: write → **EIO** (`write: Input/output error`) — correct
   refusal instead of silent loss.
2. Scenario B: write → **EIO, no panic**.
3. Degraded-but-correct write (single sd down, DF-4721 case): still
   SUCCEEDS, readback matches — the fix does not break legitimate
   degraded writes.
4. Control roundtrip: OK.

## Non-goals / limits

- No uid=0 chain: the bug corrupts storage data / panics; it does not
  grant privilege. The trigger needs a vinum array (root-created); after
  that, any user I/O to a mounted filesystem on the volume reaches the
  path.
- Scenario B's panic is attributed to the DF-4721 UAF (also reachable
  without this bug); scenario A is the pure, panic-free demonstration of
  THIS bug's data-loss impact.

## Fix

`fix.diff` — guard on `m.badsdno >= 0` alone (refuse regardless of
`checksdstate()` result), returning `REQUEST_DOWN` when `s == REQUEST_OK`;
co-located with the DF-4715 ordinal-loop correction. Validated above.

## 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.
