Unchecked device-supplied rxcd->rxd_idx yields OOB array access, OOB write, arbitrary-pointer write, and infinite loop in RX completion path
Summary
vmxnet3_rxq_eof() and vmxnet3_rxq_discard_chain(): rxcd->rxd_idx is 12-bit (0-4095 per if_vmxreg.h:144) but used to index vxrxr_rxd[]/vxrxr_rxbuf[] at if_vmx.c:2221/2223 with no check against rxr->vxrxr_ndesc(32-2048). Consequences: (a) OOB WRITE rxd->gen at :2057 in discard path, (b) OOB READ vrxb_m at :2223 -> arbitrary write via m->m_pkthdr.rcvif=ifp/:2265/:2266 if non-NULL, (c) infinite loop at :2233 because vxrxr_fill wraps at ndesc can never equal idx>=ndesc. Malicious/buggy VMware hypervisor (or nested-virt L1) injects crafted RX completion. Same defect class fixed in FreeBSD vmxnet3, never ported to DFly. Fix: validate idx<ndesc before any array use.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1213 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full path:line trace + reachability analysis | 3.7 KB | β raw |
| README.md | readme | claim, verdict, threat model, fix | 1.9 KB | β raw |
| fix.diff | suggested-fix | bounds-check rxcd->rxd_idx vs vxrxr_ndesc at both consumption sites (eof + discard_chain) | 985 B | view raw |
| run.sh | reachability-probe | shell probe: is a vmxnet3 interface present? | 570 B | view raw |
| build.sh | build-noop | no userspace build (trigger is device-written rxcd) | 283 B | view raw |
| fix_build.log | build-log | GENERIC kernel build with the fix applied (rc=0; if_vmx.o/if_vmx.ko clean) | 5.6 MB | β download |
| env.txt | environment | guest uname, cc, device topology | 718 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1213 β Unchecked device-supplied rxcd->rxd_idx β OOB in vmxnet3 RX path
Claim
vmxnet3_rxq_eof() and vmxnet3_rxq_discard_chain() use the
device-supplied completion descriptor field rxcd->rxd_idx (12-bit,
0-4095 per if_vmxreg.h:144) to index rxr->vxrxr_rxd[] /
rxr->vxrxr_rxbuf[] (if_vmx.c:2221/2223) and vmxnet3_rxq_eof_discard()
(if_vmx.c:2056) with no check against rxr->vxrxr_ndesc (32-2048).
Consequences of a malicious rxd_idx:
(a) OOB WRITE rxd->gen at if_vmx.c:2057 in the discard path;
(b) OOB READ vrxb_m at if_vmx.c:2223, then if non-NULL an
arbitrary write via m->m_pkthdr.rcvif = ifp (:2265) and
m->m_pkthdr.len = m->m_len = length (:2266);
(c) infinite loop if idx never equals vxrxr_fill (the catch-up at
:2232-2237).
Verdict
NOT TESTABLE on this audit guest (real bug, traced line-by-line; fix
authored and compile-validated into the GENERIC kernel). See VERDICT.md.
Why not reproduced here
- The guest NIC is vtnet0 (virtio-net);
vmxnet3(vmx0) is not attached (ifconfig vmx0β "does not exist").device vmxis in GENERIC but only attaches to a VMware vmxnet3 PCI device, which QEMU (virtio machine) does not present. - The malicious value comes from the vmxnet3 RX completion ring (device-written), not from any userspace syscall; a benign device always returns valid indices.
Trigger (threat model)
A malicious VM host / hypervisor whose vmxnet3 device model writes RX
completion descriptors with rxd_idx >= ndesc. This is the hostβguest
(guest-escape-direction) attack surface for any VM running on a hostile or
compromised host, plus buggy device models. Most directly relevant in
multi-tenant virtualization.
Fix
fix.diff validates idx < rxr->vxrxr_ndesc at both consumption sites
(discard chain :2084 and eof :2215), logging and breaking out of the
RX loop on a bad index (desync with the device β stop processing).
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.
-
vmxnet3_rxq_eof()(if_vmx.c): -if_vmx.c:2202readsrxcd = &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-2220picksrxrbyrxcd->qid. -if_vmx.c:2221βrxd = &rxr->vxrxr_rxd[idx];NO bounds check (vxrxr_rxdhasvxrxr_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 loopwhile (rxr->vxrxr_fill != idx)writesvxrxr_rxd[fill].gen(more OOB writes) and can loop forever ifidxis 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 OOBmis a non-NULL attacker-influenced pointer, this is an arbitrary write throughm_pkthdr/m_len. -
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 ofrxd->genat an attacker-chosenidx.
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
ifconfigshows onlyvtnet0(virtio-net) andlo0;ifconfig vmx0β "interface does not exist".device vmxis inX86_64_GENERICbutvmxnet3only attaches to the VMware vmxnet3 PCI device (vendor 0x15ad), which the QEMU virtio machine does not present.- The malicious
rxd_idxis 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.
Fix verification
not_testablecompile validated -Werror
module/kernel build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. vmxnet3 rxcd->rxd_idx 12-bit no bounds vs vxrxr_ndesc. No vmxnet3 NIC. (Finding said bnx2x but file is vmxnet3.)
No comments yet.