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

format_config() OOB heap read: vinum_conf.plex[sd->plexno] with plexno==-1 for detached subdisks persists heap bytes into the on-disk config

Summary

The subdisk branch unconditionally prints plex[sd->plexno].name; a detached subdisk keeps plexno==-1, so plex[-1].name reads one struct before the dynamically allocated array and the %s copies OOB heap bytes into the config text written to every vinum drive. Direct trigger needs root, but the same code runs when a crafted vinum disk's config is restored by scandisk (the DF-1732/1733 media surface). REPRODUCED on guest (crafted FS_VINUM partition + detached sd: hexdump shows OOB bytes in the persisted config; value tracks the plex[] allocation). Patched module validated (detached sds print no plex token). Guard-plexno>=0 fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4592 Β· 9 files
FileTypeDescriptionSize
run.log β€” 4.1 KB view raw
run.patched.log β€” 4.2 KB view raw
leak_sample.txt β€” 967 B view raw
fix.diff β€” 1.1 KB view raw
fix.combined.diff β€” 1.8 KB view raw
label.bin β€” 512 B ↓ download
run.sh β€” 158 B view raw
build.sh β€” 178 B view raw
env.txt β€” 291 B view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched module (fix.diff applied to /usr/src, rebuilt vinum.ko, rebooted, identical run.sh): the plex-name field and its OOB byte are gone from the detached-sd line; baseline stock module shows the garbage byte in the same run.

run.log vs run.patched.log, 'DF-4592 evidence' sections; module md5 c25e05df61176fed080646a67ef6a41c (patched) vs a0152b6fbfd886fe8c0783d3b608aa97 (stock rebuild)
↓ fix.diffper-fix-DF-4592

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log 'DF-4592 evidence' section (hexdump shows 31 20 70 6c 65 78 20 <OOB byte> 20 6c 65 6e = '1 plex <XX> len'); run.patched.log same section shows clean 'sd name det1 drive d1 len 2048s ... detached'; leak_sample.txt collects both observed bytes; fix.diff is the validated hunk

PoC changes

No seed PoC existed. Wrote the full harness: crafted disklabel32 (fstype 14) injected into the guest's md0 malloc disk to satisfy vinum's DIOCGPART fstype check, vinum(8) command files, sector-aligned dd dumps of the config area (bs=1 I/O is rejected by md), and hexdump extraction of the exact leaked byte.

Verified recommended fix

In format_config(), only print the 'plex %s' field when sd->plexno >= 0 (see fix.diff); detached sds keep the existing 'detached' suffix.

Verdict

Reproduced on the guest: creating a detached subdisk (sd name det1 drive d1 len 1m detached) makes format_config() print vinum_conf.plex[-1].name (vinumio.c:522, plexno==-1 per vinumconfig.c:1259); the leaked out-of-bounds heap byte (observed 0x01 and 0x20 in different runs, changing when the plex[] array was reallocated) is persisted into the on-disk vinum config area of every drive. Root-triggerable directly, and also reachable from a crafted vinum disk (detached sd config line parsed by vinum_scandisk). Fix validated: with fix.diff the detached-sd config line omits the plex token entirely and no OOB byte appears; round-trip parse preserved via the existing 'detached' keyword.