Heap overflow in plex/subdisk auto-naming via strcat past filled 64-byte name field
Summary
config_plex at vinumconfig.c:1455-1464: strcpy(plex->name,VOL[volno].name) fills 64-byte name exactly (ksnprintf max 63+NUL). strcat(plex->name,".pN") writes 3-5 bytes past end into plex->organization. Same at :1269-1278 for sd->name into sd->state. Triggered by 63-char volume name + unnamed plex. Via vinum create (root) or on-disk config (vinum_scandisk autoconfig at boot). Fix: ksnprintf with sizeof(name) bound or length check before strcat.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1397 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| vinum_overflow.c | trigger-source | byte-exact harness replicating config_plex strcpy+strcat overflow | 3.7 KB | view raw |
| live_repro.sh | trigger-source | live kernel repro: kldload vinum + vinum create with 63-char volume name | 822 B | view raw |
| build.sh | build-script | cc -O2 -o vinum_overflow vinum_overflow.c | 216 B | view raw |
| run.sh | run-script | run harness + note live repro | 260 B | view raw |
| run.log | run-log | harness output: OVERFLOW CONFIRMED, name=66, organization corrupted | 683 B | view raw |
| env.txt | environment | uname, cc version | 227 B | view raw |
| fix.diff | suggested-fix | replace strcpy+strcat with bounded ksnprintf at plex+sd sites | 1.8 KB | view raw |
| VERDICT.md | verdict | full analysis incl live before/after (66->63 chars) | 5.3 KB | β raw |
| README.md | readme | reproduce guide | 2.1 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1397 β vinum config_plex auto-naming strcpy/strcat overflow (PoC)
Summary
A 63-char vinum volume name + an unnamed plex makes config_plex
(sys/dev/raid/vinum/vinumconfig.c:1459-1464) do strcpy(plex->name,
VOL[].name) then strcat(plex->name, ".pN"), overflowing the 64-byte
plex->name into the adjacent enum plexorg organization field. Symmetric
subdisk site at :1274-1278 (strcpy+strcat(".sN") into sd->state).
Reachability
vinum is a loadable module (vinum.ko, prebuilt at /boot/kernel/), not in
GENERIC. The bug is live-reproducible here: kldload vinum + vinum create
(root) with a 63-char volume name + unnamed plex. Realistic untrusted vector:
on-disk vinum config auto-parsed at boot.
Build / run
Byte-exact harness (userspace, any user)
./build.sh && ./run.sh # prints the 66-char name + corrupted organization
Live kernel repro (needs root)
kldload vinum
sh live_repro.sh # creates a 63-char volume + unnamed plex
Expected output (bug present)
- Harness:
OVERFLOW CONFIRMED: strcat(".p%d") wrote past the 64-byte name into the organization enum field.(printed name walks to 66 chars;organizationcorrupted fromplex_concat(1)to12400.) - Live:
vinum lshows the plex name printed as 66 chars (AAAA...AAA.p0) β proof the NUL now lives insideorganization.
Fix validation (before/after, live)
- Before (unpatched
#0): plex name = 66 chars (overflow present). - After (rebuilt
vinum.kowithfix.diff,-Werror): plex name = 63 chars (ksnprintfbounded; overflow gone).fix_status = fixed.
Files
vinum_overflow.cβ byte-exact harness (struct plexfromvinumvar.h:548).live_repro.shβ live kernel reproduction.build.sh/run.sh.run.logβ harness output.env.txtβ guest environment.fix.diffβ replacestrcpy+ksprintf+strcatwith boundedksnprintfat both sites.VERDICT.md/manifest.json.
Fix
Replace the strcpy+ksprintf+strcat sequence at both sites with a single
bounded ksnprintf(.., sizeof(name), "%s.p%d", volname, pindex) / "%s.s%d"
(fix.diff).
DF-1397 β vinum config_plex auto-naming strcpy/strcat overflow
Verdict: REPRODUCED (live kernel + byte-exact harness). Fix VALIDATED (live before/after).
A 63-character vinum volume name combined with an unnamed plex causes
config_plex to overflow the 64-byte plex->name buffer into the adjacent
organization enum field. This was reproduced live on the DragonFly guest
(kldload vinum + vinum create) and deterministically with a byte-exact
userspace harness, and the authored fix was validated with a true before/after
kernel-module rebuild + re-test.
Mechanism (trigger β primitive β effect)
struct plex (sys/dev/raid/vinum/vinumvar.h:548-550):
struct plex {
char name[MAXPLEXNAME]; /* MAXPLEXNAME = 64 (vinumvar.h:103) */
enum plexorg organization; /* IMMEDIATELY AFTER name -> overflow target */
...
};
Volume names are bounded to MAXVOLNAME-1 = 63 chars + NUL by
ksnprintf(vol->name, sizeof(vol->name), "%s", name) at
vinumconfig.c:925. When an unnamed plex is auto-named, config_plex
runs (vinumconfig.c:1459-1464):
strcpy(plex->name, VOL[plex->volno].name); /* 63 chars + NUL fills name[0..63] exactly */
ksprintf(plexsuffix, ".p%d", pindex); /* ".p0" */
strcat(plex->name, plexsuffix); /* writes ".p0\0" at name[63..66] -> OOB */
strcat finds the NUL at name[63], writes .,p,0,\0 at indices
63,64,65,66 β indices 64/65/66 are past the 64-byte buffer, corrupting the
organization enum. (A symmetric site exists for subdisks at :1274-1278:
strcpy(sd->name, PLEX[].name) + strcat(".sN").)
The same primitive appears for subdisks (config_sd, :1269-1278):
strcpy(sd->name, PLEX[sd->plexno].name) + strcat(".s%d") with a 63-char
plex name; the sd->state field is the overflow target.
Primitive
- Class: heap (in-object) overwrite β 3-5 bytes past a 64-byte name into the
adjacent
enum plexorg organization(orenum sdstate state) field of the same kmalloc'dstruct plex/struct sdarray element. - Content: low-attacker-controlled ASCII (
.pN\0/.sN\0). - Effect: corrupts plex organization β the immediately-following
if (isstriped(plex))(:1466) reads the corrupted enum; misbehavior / potential panic. Because the overflow lands inside the SAME struct (not a slab boundary), INVARIANTS slab checks do NOT trip β corruption is silent.
Reachability / threat model
vinumis a loadable module (vinum.ko, prebuilt at/boot/kernel/), NOT inX86_64_GENERIC.- The live trigger is
vinum create(root) with a 63-char volume name + unnamed plex. The realistic untrusted-input vector is on-disk vinum config auto-parsed at boot/autoconfig: an attacker who can write a vinum config to a disk (e.g. viavfs.usermount+ a user-owned image, or a USB drive) can place a 63-char volume name that the kernel parses on autoconfig. - This is NOT a privilege boundary the bright-line rule forbids for
demonstration (kldload is used only to load the vulnerable module; the bug
path itself is
vinum create/ on-disk config). Nouid=0chain is claimed β the realistic impact is memory corruption (DoS / misbehavior), not priv-esc, because the controlled bytes are limited ASCII into a single enum.
Live reproduction (proof)
On unpatched 6.5-DEVELOPMENT #0:
# kldload vinum ; vinum create -f vc.conf (vc.conf: 63-char "volume AAAA..." + "plex org concat") P AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.p0 ... # plex name printed length = 66 (buffer is 64 -> 2 bytes overflowed into `organization`)
The plex name printing as 66 characters is direct proof: the terminating
NUL now lives inside the organization field, so the char* walk runs past the
64-byte name. See run.log (harness) and the live output in this VERDICT.
Fix validation (before/after, live)
Authored fix.diff: replace strcpy+ksprintf+strcat at both sites with a
single bounded ksnprintf(.., sizeof(name), "%s.p%d", volname, pindex) /
"%s.s%d".
- Baseline (unpatched
#0): rebuilt module is the stockvinum.ko; live repro β plex name = 66 chars (overflow present). - Patched (rebuilt
vinum.kowith fix,cc ... -Werror, RC=0): kldload + re-run repro β plex name = 63 chars (ksnprintf bounded; overflow GONE).
fixed module: /usr/obj/usr/src/sys/dev/raid/vinum/vinum.ko (rebuilt, -Werror clean) BEFORE (stock): plex name length: 66 AFTER (fixed): plex name length: 63 -> overflow into organization eliminated
fix_status = fixed. Both the compile (-Werror) and the runtime
before/after confirm the fix closes the bug.
PoC changes
vinum_overflow.cβ byte-exact userspace harness replicating thestruct plexlayout and thestrcpy+ksprintf+strcatlogic; shows the printed name walking to 66 chars andorganizationcorrupted fromplex_concat(1)to12400.live_repro.shβ the live kernel reproduction (kldload vinum+vinum createwith a 63-char volume name).fix.diffβ the verified fix.
Kernel references
sys/dev/raid/vinum/vinumconfig.c:1459-1464(plex auto-name sink)sys/dev/raid/vinum/vinumconfig.c:1274-1278(subdisk auto-name sink)sys/dev/raid/vinum/vinumconfig.c:925(volume-name 63-char bound)sys/dev/raid/vinum/vinumvar.h:103(MAXPLEXNAME=64),:548-550(struct plex layout)
Fix verification
fixedcompile+harness validated
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
REPRODUCED (live+harness). vinum config_plex strcpy+strcat 63-char vol name -> 3B overflow into organization enum. vinum.ko module. Fixed: ksnprintf.
No comments yet.