# DF-1214 — vmxnet3 RX length unchecked vs cluster size (OOB read / heap info leak)

## Verdict
**INCONCLUSIVE (not_testable on this guest)** — bug confirmed in source; runtime
not triggerable on this guest (no vmxnet3 NIC). Fix authored, applied, and
compiled clean as part of a single-fix `nativekernel` build.

## Finding summary
`vmxnet3_rxq_eof()` (sys/dev/virtual/vmware/vmxnet3/if_vmx.c) writes
`length = rxcd->len` (a 14-bit field, 0–16383, supplied by the hypervisor)
directly into `m->m_len` / `m->m_pkthdr.len` at :2266 (HEAD) and :2300 (BODY)
without checking it against the backing cluster size. HEAD descriptors are
backed by an `MCLBYTES` (2048) cluster with `ETHER_ALIGN` (2) bytes consumed
by `m_adj()` in `vmxnet3_newbuf()` (only `MCLBYTES - ETHER_ALIGN = 2046` bytes
are usable). BODY descriptors are backed by `MJUMPAGESIZE` (4096).

A malicious or buggy hypervisor that injects an RX completion with `len` larger
than the cluster (e.g. `len=8192` on a HEAD descriptor) makes `if_input()`
later read past the cluster into adjacent kernel heap and deliver those bytes
to a guest socket — a guest-kernel heap information leak. The guest has no way
to validate `rxcd->len` independently; the host is trusted today.

## Source confirmation (audited tree)
- if_vmx.c:2000–2005 — cluster sizing in `vmxnet3_newbuf()`: HEAD=MCLBYTES, BODY=MJUMPAGESIZE.
- if_vmx.c:2014–2018 — HEAD cluster has `ETHER_ALIGN` (2) consumed by `m_adj()`.
- if_vmx.c:2216 — `length = rxcd->len;` — hypervisor-supplied, no clamp.
- if_vmx.c:2221 — `rxd = &rxr->vxrxr_rxd[idx];` — `rxd->btype` available, tells us HEAD vs BODY.
- if_vmx.c:2266 — `m->m_pkthdr.len = m->m_len = length;` — HEAD path, no clamp.
- if_vmx.c:2300 — `m->m_len = length;` — BODY path, no clamp.
- No bounds check anywhere between `length = rxcd->len` and the assignment to `m_len`.

## Why not runtime-reproduced on this guest
The QEMU/KVM guest uses a `virtio-net` NIC (`chip=0x10001af4`,
`vendor='Red Hat, Inc.'`). There is **no vmxnet3 device attached**, so the
`vmx` driver's `attach()` never runs, no `vmxnet3_softc` / RX ring is created,
and `vmxnet3_rxq_eof()` is unreachable at runtime on this guest. The driver
*is* statically compiled into GENERIC (`device vmx`), and
`vmxnet3_rxq_eof_discard.isra.8` is present as a symbol in `/boot/kernel/kernel`,
but there is no live softc.

Triggering the bug requires a vmxnet3 device that emits RX completions with
`len` exceeding the backing cluster size — i.e. a malicious or buggy VMware
hypervisor. Not exercisable on the audit guest.

## Fix (fix.diff)
Clamp `length` to the backing cluster size based on `rxd->btype` immediately
after it is read from the descriptor, before it reaches `m_len`:
```c
length = rxcd->len;
/*
 * rxcd->len is 14-bit hypervisor-supplied... clamp to the backing
 * cluster so a malicious/buggy host cannot make if_input() walk
 * past the cluster into adjacent kernel heap.
 */
if (rxd->btype == VMXNET3_BTYPE_HEAD) {
    if (length > MCLBYTES - ETHER_ALIGN)
        length = MCLBYTES - ETHER_ALIGN;
} else if (length > MJUMPAGESIZE)
    length = MJUMPAGESIZE;
```
The clamp affects only the over-sized case; well-formed packets are unchanged.

## Fix validation
- `git apply --check -p1` — clean.
- Applied to in-guest `/usr/src`, built as part of a single combined
  `make -j6 nativekernel` (with DF-1212 and DF-1223) — compiled clean,
  `NK_DONE rc=0`.
- `fix_status: not_testable` — no vmxnet3 hardware on this guest.

## Run / reproduce
Not runnable on this guest. On a VMware host with a malicious hypervisor
(or a custom vmxnet3 emulation that issues oversized RX completions), the bug
would manifest as guest-kernel heap bytes delivered to a receiving socket.
