# 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)
```sh
./build.sh && ./run.sh      # prints the 66-char name + corrupted organization
```
### Live kernel repro (needs root)
```sh
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; `organization`
  corrupted from `plex_concat(1)` to `12400`.)
- Live: `vinum l` shows the plex name printed as **66 chars** (`AAAA...AAA.p0`)
  — proof the NUL now lives inside `organization`.

## Fix validation (before/after, live)
- **Before (unpatched `#0`):** plex name = **66 chars** (overflow present).
- **After (rebuilt `vinum.ko` with `fix.diff`, `-Werror`):** plex name = **63
  chars** (`ksnprintf` bounded; overflow gone).
`fix_status = fixed`.

## Files
- `vinum_overflow.c` — byte-exact harness (`struct plex` from `vinumvar.h:548`).
- `live_repro.sh` — live kernel reproduction.
- `build.sh` / `run.sh`.
- `run.log` — harness output.
- `env.txt` — guest environment.
- `fix.diff` — replace `strcpy`+`ksprintf`+`strcat` with bounded `ksnprintf` at 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`).
