# DF-1213 — VERDICT

**Finding:** Unchecked device-supplied `rxcd->rxd_idx` yields OOB array
access, OOB write, arbitrary-pointer write, and a possible infinite loop in
the vmxnet3 RX completion path (`sys/dev/virtual/vmware/vmxnet3/if_vmx.c`).
**Status:** NOT TESTABLE on this audit guest. **Confidence (bug is real):** certain.
**Impact ceiling:** OOB read + OOB/arbitrary write via device-controlled mbuf
pointer deref + DoS loop; **device-controlled** (malicious vmxnet3 device).
**Fix:** authored in `fix.diff`, applied clean, compile-validated into GENERIC.

## Mechanism (confirmed line-by-line in `sys/`)

RX completion descriptors are written by the vmxnet3 device into a ring the
kernel reads. The per-descriptor `rxd_idx` selects which RX buffer the
descriptor refers to.

1. **`vmxnet3_rxq_eof()`** (`if_vmx.c`):
   - `if_vmx.c:2202` reads `rxcd = &rxc->vxcr_u.rxcd[rxc->vxcr_next]`.
   - `if_vmx.c:2215` — `idx = rxcd->rxd_idx;` (12-bit device value, 0-4095).
   - `if_vmx.c:2217-2220` picks `rxr` by `rxcd->qid`.
   - `if_vmx.c:2221` — `rxd = &rxr->vxrxr_rxd[idx];` **NO bounds check**
     (`vxrxr_rxd` has `vxrxr_ndesc` = 32/64/128/256/512/1024/2048 entries).
   - `if_vmx.c:2223` — `m = rxr->vxrxr_rxbuf[idx].vrxb_m;` **OOB read.**
   - `if_vmx.c:2232-2237` — catch-up loop `while (rxr->vxrxr_fill != idx)`
     writes `vxrxr_rxd[fill].gen` (more OOB writes) and can loop forever if
     `idx` is out of the fill range.
   - `if_vmx.c:2265-2266` — `m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len =
     m->m_len = length;` — if the OOB `m` is a non-NULL attacker-influenced
     pointer, this is an **arbitrary write** through `m_pkthdr`/`m_len`.

2. **`vmxnet3_rxq_discard_chain()`** (`if_vmx.c:2062`):
   - `if_vmx.c:2084` — `idx = rxcd->rxd_idx;`
   - `if_vmx.c:2090` — `vmxnet3_rxq_eof_discard(rxq, rxr, idx);`
   - `if_vmx.c:2056-2057` — `rxd = &rxr->vxrxr_rxd[idx]; rxd->gen = ...;`
     **OOB write** of `rxd->gen` at an attacker-chosen `idx`.

`vxrxr_ndesc` (the valid bound) is `u_int` (`if_vmxvar.h:92`); it is set at
ring init to one of the power-of-two ring sizes and is the natural check.

## Why it is NOT TESTABLE on this guest

- `ifconfig` shows only `vtnet0` (virtio-net) and `lo0`; `ifconfig vmx0` →
  "interface does not exist". `device vmx` is in `X86_64_GENERIC` but
  `vmxnet3` only attaches to the VMware vmxnet3 PCI device
  (vendor 0x15ad), which the QEMU virtio machine does not present.
- The malicious `rxd_idx` is written by the device into the completion ring;
  there is no userspace syscall that makes a benign device emit a bad index.

Valid "device-controlled, not reachable from an unprivileged user on this
guest" case. Bug is genuine (no bounds check at either `idx` consumption
site); threat model = malicious vmxnet3 device model (host→guest).

## Exploit chain
None developed: device-controlled primitive with no unprivileged syscall
path, and no vmxnet3 device on this guest. Documented impact ceiling: OOB
write of `rxd->gen` (discard), OOB read of `vrxb_m` + arbitrary write
through the returned mbuf pointer (eof), and a possible infinite loop.

## Fix
`fix.diff` adds `if (idx >= (int)rxr->vxrxr_ndesc) { device_printf(...);
break; }` at both consumption sites — in `vmxnet3_rxq_discard_chain` before
calling `vmxnet3_rxq_eof_discard`, and in `vmxnet3_rxq_eof` before indexing
`vxrxr_rxd[]`. A bad index means desync with the device; the safest action is
to stop processing that ring (break), matching the existing "host skip"
handling philosophy. Uses `sc->vmx_dev` (the real softc field, `if_vmxvar.h:202`).

## Build / run on this guest
`./run.sh` is a reachability probe; on this guest it reports "no vmx0 → vmxnet3
RX path unreachable". Bug confirmed by the trace above; fix compile-validated
into GENERIC.
