β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4715

Second-bad-subdisk stale-marking loop indexes plex->sdnos[] by subdisk COUNT instead of the participating ordinals -> healthy subdisks force-marked stale

Summary

The stale-marking walk uses the participating-data COUNT as an ordinal index - unless the request starts at ordinal 0, the WRONG (healthy) subdisks are force-marked sd_stale and persisted. GUEST-VERIFIED: write spanning ordinals 1,2 with both down made healthy parity s0 (ordinal 0) go up->stale; patched kernel keeps it up. Amplifies double-faulted arrays into unavailability. Walk-the-ordinals fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4715 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.8 KB ↓ raw
run.sh β€” 122 B view raw
patched.step2.txt β€” 996 B view raw
run.patched.log β€” 1.2 KB view raw
verdict.json β€” 1.9 KB view raw

DF-4715 β€” vinumraid5 second-bad-subdisk stale loop indexes plex->sdnos[] by COUNT, not by ordinal β†’ healthy subdisks force-marked stale

What this proves

When a RAID-5 write meets a second unusable data subdisk (or any unusable data subdisk while the parity subdisk is down), bre5() "makes all involved subdisks stale" (sys/dev/raid/vinum/vinumraid5.c:413-417). The loop indexes plex->sdnos[sdno] with sdno running 0..m.sdcount-1 β€” a COUNT of participating subdisks, not their ordinals. Unless the request starts at ordinal 0, the wrong (healthy) subdisks are force-marked sd_stale ("invalid data") and the state change is saved to disk.

Guest-verified: 3-sd RAID-5 plex, write spanning data ordinals 1 and 2 with both down → refusal path fires → s0 (the healthy parity subdisk, ordinal 0) transitions up→stale while the actually-involved (already down) subdisks are untouched. On the fixed kernel (ordinal walk) s0 stays up.

Impact: spurious degradation of healthy subdisks on an array that is already failing (double fault): further reads/writes of stripes whose parity was healthy now fail or take recovery paths; an admin reacting to the bogus "stale" labels may re-initialize good subdisks. Data-integrity mislabeling + availability loss (local DoS amplifier).

Setup / trigger

Same array as DF-4714 (../DF-4714/setup_vinum.sh). Trigger is embedded in ../DF-4714/run.sh step [2] and reproduced by run.sh here:

vinum setstate -f down rv.p0.s1; vinum setstate -f down rv.p0.s2; then ONE 32KB write at volume sectors [128,192) (EIO expected) β†’ vinum list shows the wrong subdisk staled.

Fix validation

Included in DF-4714's fix.diff (same block): the loop now walks the participating ordinals (firstsdno + parity-skip + wrap). Patched kernel: same write EIOs but s0 stays up (../DF-4714/run.patched.log step 2).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

patched kernel: EIO preserved (correct refusal) and s0 no longer staled - run.patched.log step 2

patched.step2.txt
per-fix-DF-4715

Confirmed kernel references

Detail

Exploit chain

root: RAID-5 array with two data subdisks of one stripe down (drive faults); any write spanning both -> healthy parity subdisk wrongly invalidated (saved to config) -> array degraded beyond the actual fault

Evidence (decisive lines)

DF-4714/run.log step [2] (baseline: s0 up->stale), run.patched.log / patched.step2.txt (fixed: s0 stays up)

PoC changes

reuses DF-4714's setup and single-32KB-write trigger methodology

Verified recommended fix

walk the participating ordinals (firstsdno, parity-skip, wrap) instead of [0..sdcount) - included and validated in findings/poc/DF-4714/fix.diff

Verdict

bre5()'s refusal-path loop (vinumraid5.c:413-417) marks plex->sdnos[0..m.sdcount-1] stale - indexing by COUNT instead of the participating ordinals. Guest-verified: with the write spanning data ordinals 1,2 (both down), the healthy parity subdisk s0 (ordinal 0) was force-marked sd_stale and the transition persisted; involved (already down) sds untouched. Patched kernel (ordinal walk in DF-4714 fix.diff): same EIO, s0 stays up.