initsd() verify loop retries a mismatched block forever (verified=0 with no bound): unbounded uninterruptible kernel loop in the ioctl thread
Summary
The while(!verified) loop rewrites/re-reads on every mismatch with no retry counter; biowaits are PCATCH-less so a persistent mismatch spins the ioctl thread unkillably. Loop code-certain; trigger NOT reachable on the md guest (buffer-cache coherence shields the verify across 3 full verified inits under a hammering writer - documented negative result). Bound-the-retry fix drafted.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4802 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| initlivelock.c | β | 4.0 KB | view raw | |
| env.txt | β | 312 B | view raw | |
| README.md | β | 2.6 KB | β raw | |
| verdict.json | β | 2.7 KB | view raw | |
| manifest.json | β | 1.0 KB | view raw |
DF-4802 β initsd() verify loop retries a mismatched block forever: unbounded uninterruptible kernel loop (CWE-835); the mismatch was not reachable on this guest
- File:
sys/dev/raid/vinum/vinumrevive.c:526-565(initsdverify path) - Severity: Low (speculative; needs a device that returns data other
than what was just written while the subdisk is
sd_initializing) - Bucket: base:raid
- Status: not reproduced on the md-backed guest (see analysis)
The defect
verified = 0;
while (!verified) { /* vinumrevive.c:526 */
... write zeroes; biowait; /* :527-536 */
if (error) ...
if ((error == 0) && verify) { /* :539 */
... read back; biowait; /* :540-548 */
else if ((*bp->b_data != 0)
||(bcmp(bp->b_data, &bp->b_data[1], bp->b_bcount - 1))) {
kprintf("vinum: init error ...");
verified = 0; /* :560 <-- retry forever */
} else
verified = 1;
} else
verified = 1;
}
There is no retry bound: a persistent readback mismatch (flaky
media, a lying device, or any concurrent writer that lands between the
zero-write and the readback) spins this loop in the ioctl thread
forever. The biowaits have no PCATCH, so the thread is unkillable
while it loops. (initsd is reached per-block from
start_object/VINUM_SETSTATE(object_up, verify=1),
vinumstate.c:883-887.)
Why it did not reproduce here (negative result with evidence)
The concurrent-writer angle was tested thoroughly on the guest
(initlivelock.c): a tight pwrite loop on the raw subdisk device
(/dev/vinum/sd/vol.p0.s0, vinumstrategy VINUM_SD_TYPE β sdio
directly, vinumrequest.c:135-139) hammering the exact block being
initialized β zero mismatches in 3 full 3 MB inits; the readback
never saw the concurrent garbage. Reason: sdio() dispatches via
vn_strategy(drive->vp, ...) (vinumrequest.c:981) β the init's
write+readback pair is served coherently from the vnode/buffer-cache
view, while the raw physio writer bypasses it; the verify effectively
re-reads its own just-written zeros. A raw concurrent writer therefore
cannot desync the pair on this backing, and the mismatch arm stayed
unreachable (no vinum: init error line was ever produced).
On real hardware with media that returns corrupted data (the case the verify exists for), the loop is provably unbounded: there is no counter and no error exit on the mismatch branch.
Files
initlivelock.cβ the attempted trigger (init ioctls with verify=1 + concurrent raw-sd garbage writer; documents the buffer-cache shielding)verdict.json,README.md,env.txt
Fix verification
not_testablenot validated - the trigger itself was not reachable on this guest, so there is no baseline behavior to flip
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md analysis; initlivelock.c (the attempted trigger); dmesg in the guest session showed zero 'vinum: init error' lines across three full verified inits with the hammer active
PoC changes
driver had to re-fill the ioctl msg template every iteration (the IOC_OUT reply overlays the request buffer, clobbering type/state)
Verified recommended fix
bound the retry: after 3 consecutive mismatches of the same block set error=EIO and verified=1 (draft hunk included in the DF-4800/4801 combined fixwork, not validated separately)
Verdict
initsd()'s verify loop (vinumrevive.c:526-565) sets verified=0 on a readback mismatch (:555-560) with no retry bound, so a persistent mismatch spins the ioctl thread in an unbounded, uninterruptible (PCATCH-less biowait) kernel loop. The loop and the missing bound are certain from the code; the mismatch itself was NOT reachable on this guest: a tight pwrite hammer on the raw subdisk device hammering the exact block being initialized produced zero mismatches across three full 3 MB verified inits, because sdio() dispatches via vn_strategy(drive->vp) (vinumrequest.c:981) and the init's write+readback pair is served coherently from the buffer-cache view while the raw physio writer bypasses it. On hardware whose media returns corrupted data (the case verify exists for) the loop provably never exits. Filed Low/speculative: code-certain defect, trigger not demonstrated.
No comments yet.