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)
PoC verification
Evidence pack
findings/poc/DF-4715 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
fixedpatched kernel: EIO preserved (correct refusal) and s0 no longer staled - run.patched.log step 2
patched.step2.txt
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.
No comments yet.