# DF-4758 — VERDICT

**REPRODUCED** — status `reproduced`, impact `dos` (demonstrated wrong-object
swap sizing with OOB indexing; ceiling is silent disk/memory corruption),
confidence `certain`.

## What was run

Guest: DragonFly dfbsd 6.5-DEVELOPMENT #0 (stock snapshot, X86_64_GENERIC,
2026-09-23). Module: stock `/boot/kernel/vinum.ko`. All as root (the swapon
path is `SYSCAP_RESTRICTEDROOT`-gated, `sys/vm/vm_swap.c:202`).

Config (run.sh): vinum drive `d1` on `/dev/md0s0a`, volume `v0` (up,
6144 kB), concat plex `p0`, subdisks `s0..s5` (1024 kB each, all up).
`volumes_allocated == 4` (`INITIAL_VOLUMES`).

## The decisive observation

`swapon /dev/vinum/sd/s0` — a **1 MB subdisk** — succeeded and installed
**12288 512-blocks (6 MB)** of swap, exactly the size of volume `v0`:

```
== swapon(/dev/vinum/sd/s0): OK
Device          512-blocks     Used    Avail Capacity  Type
/dev/vinum/sd/s0      12288        0    12288     0%    Interleaved
```

Root cause chain, line by line:
1. swapctl(2) → `swap_on` (vm_swap.c:296-308): `VOP_OPEN` then
   `dev_dpsize(dev)` because `nblks == 0`.
2. `dev_dpsize` → `vinum_ops.d_psize = vinumsize` (vinum.c:58-70 — one
   ops table for every vinum device type).
3. `vinumsize` (vinum.c:564): `vol = &VOL[Volno(dev)]` — no `DEVTYPE`
   check, no bounds check.
4. `Volno(dev)` = `minor & 0xff` (vinumutil.c:239-243); raw-sd minors are
   `VINUM_SD(sdno) = (RAWSD_TYPE<<28) | (sdno & 0xff) | ...`
   (vinumvar.h:129-132) → `Volno(/dev/vinum/sd/s0) == 0`.
5. `VOL[0]` is volume `v0` (up) → returns **v0's size** for the subdisk.

`s0` is the in-bounds proof (wrong object, deterministic). `s4`/`s5`
(`sdno >= volumes_allocated`) prove the OOB: `VOL[4]`/`VOL[5]` read
`4/5 * sizeof(struct volume)` bytes past the 4-entry kmalloc'd array
(`state` at +64, `size` at +104). On this boot the OOB `state` words were
not `volume_up` (3), so swapon returned ENXIO — the observable result of an
OOB read is heap-layout-dependent; a matching word installs a garbage size
(huge → vm_swap.c:344 "exceeded maximum" ENXIO; mid-range → quietly wrong
swap size, as demonstrated in-bounds by s0).

## Impact ceiling

The kernel trusts `vinumsize()` for swap sizing: a 6× oversized swap device
on a 1 MB subdisk means swap-out at offsets past the subdisk extent →
writes onto unrelated drive contents (silent corruption of adjacent vinum
objects / config / data) and swap-in of garbage pages (memory corruption).
All gated on root (swapon + the vinum config), hence severity Medium, not
a privilege boundary crossing; the heap OOB read itself can also fault or
read attacker-influenceable heap (panic / limited info side channel via
installed swap size).

## Fix validation (performed in the same run)

`fix.diff` (this pack) adds `DEVTYPE == VINUM_VOLUME_TYPE` dispatch and an
`index >= volumes_allocated` bounds check. Rebuilt `vinum.ko` standalone
from patched `/usr/src` (`make` in `sys/dev/raid/vinum`, BUILD_RC=0),
installed, reloaded:

* `swapon /dev/vinum/sd/s0|s1|s4|s5` → **ENXIO** (baseline: s0 OK with the
  bogus 12288-block size). `swapinfo` clean. BAD BEHAVIOR GONE.
* Positive control `swapon /dev/vinum/vol/v0` → **OK, 12288 blocks**
  (the volume's true size) → legit volume psize still works.

`fix_baseline_reproduced=1`, `fix_patched_reproduced=0` (bug does not
reproduce on the patched module), `fix_status=fixed`.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
