# DF-5063 — vinum Plexno()/Sdno() composite-minor decode walks tables unvalidated (latent OOB)

## What

`Plexno()` and `Sdno()` (sys/dev/raid/vinum/vinumutil.c:250-297) decode the
composite `VINUM_PLEX_TYPE` / `VINUM_SD_TYPE` minor layouts with **no bounds
checks**, and propagate the `-1` "empty slot" sentinel that
`get_empty_volume()` stores in `vol->plex[]` (vinumconfig.c:896-897):

```c
case VINUM_PLEX_TYPE:              /* vinumutil.c:261 */
case VINUM_SD_TYPE:
    return VOL[Volno(dev)].plex[(minor(dev) >> 16) & MASK(3)];   /* may be -1 */
...
case VINUM_SD_TYPE:                /* vinumutil.c:289 */
    return PLEX[Plexno(dev)].sdnos[(minor(dev) >> 20) & MASK(8)]; /* PLEX[-1]! */
```

A `VINUM_SD_TYPE` minor whose plex ordinal addresses an empty slot makes
`Plexno()` return −1, and `Sdno()` then reads `PLEX[-1].sdnos[ordinal]` —
a heap out-of-bounds read up to ~1 KB before the plex[] array whose value
is then used as an index into `SD[]`. Callers only check `>=`
(vinum.c:430-440), which −1 survives as a signed value.

## Reachability — why this is only Info

The kernel never *creates* composite-minor device nodes: the only
`make_dev` sites are `VINUM_SD(sdno)` (rawsd), `VINUM_PLEX(plexno)`
(rawplex) and volume nodes (vinumconfig.c:740,763,780). The sole way to
feed a composite minor to vinum is a hand-made node (`mknod`).

**On this DragonFly (6.5-DEVELOPMENT guest) that route dies before vinum.**
Reading a hand-mknod'ed character node — even with a *valid* vinum rawplex
minor (0x50000000) — panics the kernel in the generic device-resolution
layer: `Fatal trap 12 ... Stopped at dev_dflags: movq 0xa8(%rdi),%rax`
with `rdi = 0` (NULL cdev), current process `dd` (panic.txt; reproduced
twice). UFS-mknod'ed VCHR vnodes never get a resolved `v_rdev`
(`addaliasu()` only stores v_umajor/v_uminor, sys/kern/vfs_subr.c:1246-1252)
and some path dereferences it unchecked. So `Plexno()`/`Sdno()`'s composite
branches are dead code today — reported as hardening (defense-in-depth, and
the identical `PLEX[-1]` sink is already exploitable through the config
path per DF-1399).

## What was run (root on the guest)

* `df5063_setup.sh` — vn0 drive, v0 (plex p0, sds s0/s1), up.
* `df5063_sdcomp.sh` — original mknod(1) sweep: the guest has **no mknod
  binary** (run.log).
* `df5063_mknod.c` — mknod(2)+open(2) probe tool (built clean).
* Step-by-step probe: reading `/tmp/px0` (mknod'ed **valid** rawplex
  minor) wedged the guest → `Fatal trap 12` in `dev_dflags` (panic.txt).
  Composite minors never reached vinum.

## Fix (validated as part of DF-5062's rebuild)

`fix.diff` (same file as DF-5062's pack) adds explicit bounds checks to
both composite branches of `Plexno()`/`Sdno()` (`volno` against
`volumes_allocated`, `plexno` against `plexes_allocated`, `-1` never used
to index). Patched-module regression (`fix_run.log`, `fix_run2.log`):
raw sd / rawplex / volume read-write all intact.

## Cross-file observation for the orchestrator

The mknod'ed-node read NULL-deref (`dev_dflags(NULL)`) is a **separate,
non-vinum bug** in the UFS→spec/devfs chain, root-gated local DoS,
reproducible on demand (see panic.txt + run.log/run.2.log). Suggest a
spawn on `sys/vfs/devfs/devfs_vnops.c` / `sys/kern/vfs_subr.c`.
