read_drive_label() ignores the read_drive() error and classifies stale/uninitialized heap as a vinum label (wrong-drive acceptance)
Summary
vhdr comes from non-zeroing Malloc and the read error is never tested before the magic check; on a failed label read the buffer very likely holds the previous scan's valid header (same-size alloc/free in the loop), so a failing device is accepted as DL_OURS with another drive's label - misdirected subdisk I/O. Requires failing media at a fixed offset. Check-error-before-magic fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4595 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 1.9 KB | β raw |
DF-4595 β read_drive_label() ignores the read_drive() error and decides on stale/uninitialized heap
- File:
sys/dev/raid/vinum/vinumio.c:336-337 - Severity: Low Β· Confidence: certain (missing check) / likely (impact)
What
vhdr = (struct vinum_hdr *) Malloc(VINUMHEADERLEN); /* NOT zeroed */
...
drive->state = drive_up; /* be optimistic */
error = read_drive(drive, (void *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET);
if (vhdr->magic == VINUM_MAGIC) { /* error never tested */
Malloc is plain kmalloc(M_DEVBUF, β¦) (vinumext.h:258) β no M_ZERO. If
read_drive() fails (media error, device yanked, I/O fault), vhdr holds
whatever the heap had. Because the previous call in the same scandisk loop
does Malloc(VINUMHEADERLEN) β¦ Free(vhdr) of the same size class, a
failing read very likely resurrects the previous partition's valid header
(same 512-byte block reused), so vhdr->magic == VINUM_MAGIC still holds and
the failed drive is classified DL_OURS with the previous drive's label:
drive->label = vhdr->label copies the stale identity, check_drive() then
merges the device into the configuration under a wrong name.
Impact
- Wrong-drive acceptance on read error β the config may bind a different device to a drive name; subsequent I/O for that drive's subdisks goes to the device that failed (or nothing at all if open fails later). On a mirrored setup this is a data-integrity hazard, not a memory-safety one.
- If the recycled buffer contains no NUL in
label.name-adjacent bytes, thestrcmp(drive->label.name, vhdr->label.name)at line 339 walks beyond the 512-byte allocation (OOB read; both operands are in-struct here so it usually terminates, but it is unbounded in principle).
Why untested (no guest run)
Forcing a failing sector read at exactly VINUM_LABEL_OFFSET of an
otherwise valid β₯1 MiB vinum partition requires either real failing media or
emulator-level fault injection (QEMU blkdebug on the nata path); not feasible
on the standard guest snapshot within this run. The missing if (error) is
however directly visible in the source and trivially fixed.
Fix
error = read_drive(drive, (void *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET);
- if (vhdr->magic == VINUM_MAGIC) {
+ if (error == 0 && vhdr->magic == VINUM_MAGIC) {
(and let the error fall through to the DL_CANT_OPEN/DL_NOT_OURS handling
in check_drive()).
DF-4595 β VERDICT
Status: untested (code-certain defect; impact argument, no guest run β see README "Why untested").
Analysis
sys/dev/raid/vinum/vinumio.c:332βvhdr = Malloc(VINUMHEADERLEN):Mallocβkmalloc(β¦, M_DEVBUF, M_WAITOK)(vinumext.h:258-259), not zeroed.sys/dev/raid/vinum/vinumio.c:336βerror = read_drive(...):driveio()(vinumio.c:272-304) leavesbufuntouched/partial on error and returns the error.sys/dev/raid/vinum/vinumio.c:337βif (vhdr->magic == VINUM_MAGIC): the error is never consulted before interpreting the buffer. Theerrorvariable is dead untilread_drive_label()returns andcheck_drive()looks atdrive->lasterrorβ which the success-shapedDL_OURSresult never set.
Reachability: read_drive_label() is called from check_drive()
(vinumio.c:376), which vinum_scandisk() calls for every partition of every
scanned device, and from config_drive() (vinumconfig.c:1001). A read error
at offset 4096 of an opened vinum-typed partition is the trigger; the
allocator-reuse argument (same-size Malloc/Free in the scan loop) makes the
stale-magic case realistic rather than theoretical.
Classification: a correctness bug with data-integrity consequences and a
possible unbounded strcmp read; root/attacker-media adjacent; Low.
Not run on the guest
Requires fault-injected media at a fixed offset (blkdebug-style) β outside
this snapshot's capability; recorded as untested rather than guessed.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md analysis with path:line citations
Verified recommended fix
Treat a non-zero read_drive() return as DL_CANT_OPEN before interpreting vhdr.
Verdict
Code-certain: read_drive_label() (vinumio.c:336-337) never tests the read_drive() error before comparing vhdr->magic, and vhdr comes from non-zeroing Malloc; on a failed label read the previous same-size Malloc/Free in the scandisk loop makes stale VINUM_MAGIC content likely, misclassifying the failed device as DL_OURS with another drive's label (wrong-drive acceptance, data-integrity risk; unbounded strcmp read possible). Not exercised on the guest: forcing a failing read at exactly VINUM_LABEL_OFFSET needs fault-injected media (blkdebug-class tooling) unavailable on the standard snapshot.
No comments yet.