launch_requests() keeps walking the request group after an inline completion freed it (trailing XFR_BAD_SUBDISK rqe) - kernel panic on the ORDINARY degraded RAID-5 write (new, waitlist-independent arm of the DF-4482 UAF cluster)
Summary
The designed degraded write layout puts the skipped BAD entry AFTER the last launched one; on synchronously-completing drives (md) the last launch's biodone runs inline through complete_rqe -> freerq while launch_requests is still looping - it then reads rqe->flags from freed memory and dereferences the freed group's kmalloc freelist pointer. REPRODUCED 2/2 with a single 32KB write; fix (snapshot nextrqg + bound loop to last launchable rqe) validated: write completes, readback matches, no panic. The third arm DF-4482's verifier predicted (their fixes still panicked). Filed under vinumraid5 batch; root lives in vinumrequest.c launch loop.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4721 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| trigger.c | β | 2.1 KB | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| panic.txt | β | 802 B | view raw | |
| run.patched.log | β | 1.2 KB | view raw | |
| verdict.json | β | 3.1 KB | view raw | |
| manifest.json | β | 767 B | view raw |
DF-4721 β vinum launch_requests() walks the freed request group when the last group element is a skipped XFR_BAD_SUBDISK rqe and the drive completes inline (md) β kernel panic
What this proves
The DESIGNED degraded RAID-5 write (ONE down data subdisk β no reborn trickery, no revive, no waitlist) builds the request group as
rqe[0] = parity read (launched) rqe[1] = good data block (launched) rqe[2] = down data block, XFR_BAD_SUBDISK (skipped at launch)
The last LAUNCHED element is rqe[1]; rqe[2] is a TRAILING skipped
element. With md (malloc) backing, biodone() runs inline inside
dev_dstrategy(), so rqe[1]'s completion runs complete_rqe() ->
complete_raid5_write() (XOR + phase-2 data write + parity write), whose
own inline completions drive rq->active to 0 and freerq() the entire
request while launch_requests() is still inside its launch loop
(sys/dev/raid/vinum/vinumrequest.c:395-440). The loop then reads
rqg->rqe[2].flags and rqg->next (:408-409) from the freed group;
kmalloc's freelist pointer overwrites rqg->next and the loop dereferences
garbage:
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x0 Stopped at launch_requests+0xc2: movq (%rax),%rax
Reproduced 2/2 baseline runs (once via the plain designed-degraded write
with rv.p0.s2 down, once via DF-4714's two-reborn trigger β identical
signature). This is the "at least one further lifetime defect in the
relaunch path" that DF-4482's verification predicted after its two
waitlist fixes failed to stop that panic: this arm needs no revive and
no waitlist at all β just a degraded RAID-5 write whose trailing rqe
is a skipped BAD_SUBDISK, on any drive completing synchronously in
strategy (md; also plausible with other stacking).
Trigger
Same array as DF-4714. sh ../DF-4714/setup_vinum.sh;
vinum setstate -f down rv.p0.s2; ./trigger (single 32KB write at
volume sectors [128,192)) β panic within milliseconds.
Fix validation
fix.diff snapshots rqg->next before any firing and bounds the launch
loop to the last non-BAD rqe (precomputed), so no group memory is read
after an inline completion can free it. Applied + kernel rebuilt:
write succeeds (correct degraded semantics), readback matches, no
panic (run.patched.log step 3); DF-4714's panicking trigger now gets
a clean EIO instead.
Fix verification
fixedpatched kernel: the designed degraded write completes with correct readback and no panic; DF-4714's panicking trigger gets a clean EIO - run.patched.log steps 3,4
run.patched.log
Confirmed kernel references
Detail
Exploit chain
root: RAID-5 vinum volume on md-backed drives with one data subdisk down; unprivileged: any single write() spanning both data columns of a stripe (e.g. filesystem I/O on the mounted volume) -> inline completion frees the request mid-launch -> launch_requests dereferences freed/garbage pointers -> kernel panic
Evidence (decisive lines)
panic.txt (Fatal trap 12, launch_requests+0xc2, designed-degraded run); ../DF-4714/panic.txt (same signature via DF-4714-B); run.patched.log step 3 (fixed: write OK, readback matches, no panic)
PoC changes
no seeded PoC; trigger.c written from scratch (single 32KB write, distinct per-block patterns)
Verified recommended fix
snapshot rqg->next before any I/O and bound the launch loop to the last non-BAD rqe so no group memory is read after an inline completion can free it (fix.diff, validated)
Verdict
launch_requests() (vinumrequest.c:395-440) continues iterating after an inline (md) completion has run complete_rqe()->complete_raid5_write()->freerq() and freed the whole request: trailing skipped XFR_BAD_SUBDISK entries make the loop read rqe->flags from freed memory and rqg->next from the freed group header (kmalloc freelist pointer) -> wild pointer dereference. Reached by the ORDINARY degraded RAID-5 write (single down data subdisk; trailing bad rqe is the standard layout); reproduced 2/2 with identical Fatal-trap-12 signature at launch_requests+0xc2. This is the previously-unidentified arm of the DF-4482 UAF cluster (their patched kernels still panicked; this arm needs no revive/waitlist). Patched kernel: degraded write completes correctly, no panic.
No comments yet.