# DF-4134 — `give_plex_to_volume()` writes `vol->plex[MAXPLEX]` when the
# "too many plexes" throw is swallowed by disk-config mode

## Root cause
`sys/dev/raid/vinum/vinumconfig.c:198-206`:

```c
if (vol->plexes == MAXPLEX)              /* all plexes allocated */
    throw_rude_remark(ENOSPC, "Too many plexes for volume %s", vol->name);
...
vol->plex[vol->plexes] = plexno;          /* <-- plexes == MAXPLEX == 8 */
vol->plexes++;
```

`throw_rude_remark()` **returns normally** (it does not longjmp) when
`VF_READING_CONFIG` is set (vinumconfig.c:125-132), i.e. exactly when the
configuration is being read from a vinum-labelled disk
(`vinum_scandisk`, vinumio.c:682).  Execution then falls through to
`vol->plex[8] = plexno` — one `int` past the end of `int plex[MAXPLEX]`,
which is the **last member** of `struct volume` (vinumvar.h:634), so the
write lands past the end of the `VOL[n]` element:

* for the last element of the `VOL` heap array it writes 4 bytes past the
  object (on DragonFly's power-of-two kmalloc buckets this stays inside
  the bucket's slack — no cross-object corruption in practice),
* for a non-last element it clobbers the first 4 bytes of
  `VOL[n+1].name[0..3]` (config corruption),
* `vol->plexes` becomes 9 and every later `for (i = 0; i < vol->plexes; i++)`
  walk (vinumconfig.c:210-211, 2040-2044) reads the out-of-bounds
  `plex[8]` slot as a plex index.

## Threat
Crafted vinum disk with a volume line and 9+ `plex` definitions that
reference it (`plex name=pN volume=v org concat`), read via
`vinum read` / boot autodetect.  Root/medium-integrity trigger, malicious
content.

## Fix
```diff
--- a/sys/dev/raid/vinum/vinumconfig.c
+++ b/sys/dev/raid/vinum/vinumconfig.c
@@ -197,8 +197,9 @@ give_plex_to_volume(int volno, int plexno)
     vol = &VOL[volno];					/* point to volume */
     if (vol->plexes == MAXPLEX)			/* all plexes allocated */
 	throw_rude_remark(ENOSPC,
 	    "Too many plexes for volume %s",
 	    vol->name);
+    else if (vol->plexes > MAXPLEX)			/* belt and braces */
+	throw_rude_remark(ENOSPC, "Too many plexes for volume %s", vol->name);
     else if ((vol->plexes > 0)				/* we have other plexes */
```
(or: make the reading-config path of throw_rude_remark longjmp-safe here by
returning an error and having give_plex_to_volume return immediately; the
minimal robust fix is to `return vol->plexes - 1;` right after the throw).

## Verification
Analysis-only (skipped live): severity Low — the 4-byte OOB write lands in
kmalloc bucket slack for the last element (200*n is never a power of two,
so `sizeof(struct volume) * n` never coincides with the bucket size), and
for non-last elements only renames the next volume's name; the OOB read of
`plex[8]` returns the plexno just written, which is in-bounds.  The
defect is nonetheless a genuine bounds violation reachable from crafted
media and should be fixed.
