β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1214

Unchecked rxcd->len vs cluster size yields mbuf OOB read (guest kernel heap info leak) on RX

Summary

vmxnet3_rxq_eof(): length=rxcd->len (14-bit 0-16383) written directly to m->m_len/m_pkthdr.len at :2266/:2300 without checking vs cluster size. HEAD descriptors backed by MCLBYTES(2048)-ETHER_ALIGN(2)=2046, BODY by MJUMPAGESIZE(4096). len=8192 on HEAD -> m_len exceeds cluster -> if_input reads 6KB+ past cluster into adjacent kernel heap -> info leak to guest sockets. Malicious hypervisor injects crafted RX completion. Fix: clamp length to cluster size based on btype.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1214 Β· 9 files
FileTypeDescriptionSize
VERDICT.md verdict full narrative + path:line trace 3.7 KB ↓ raw
README.md readme reproduce / preconditions 1.0 KB ↓ raw
fix.diff suggested-fix clamp rxcd->len to cluster size based on btype 1.1 KB view raw
build.sh build-log nativekernel build validation 318 B view raw
run.sh run-log no runtime trigger; static trace 147 B view raw
env.txt environment guest uname, PCI devices, GENERIC config 587 B view raw
build.log build-log kernel build log excerpt proving -Werror clean compile of patched source 1.1 KB 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
README.md readme reproduce / preconditions
↓ download raw

DF-1214 β€” vmxnet3 RX length unchecked vs cluster size

Reproduce

Not runnable on the audit guest β€” no vmxnet3 NIC is attached (the guest uses virtio-net). The verification is a static source trace + a compiling fix.diff; see VERDICT.md.

To validate the fix compiles, after applying fix.diff to /usr/src:

cd /usr/src
make -j6 nativekernel KERNCONF=X86_64_GENERIC

Bug location

sys/dev/virtual/vmware/vmxnet3/if_vmx.c:2216 β€” length = rxcd->len (hypervisor-supplied, 14-bit) is written into m->m_len / m->m_pkthdr.len at :2266 (HEAD, MCLBYTES-2 max) and :2300 (BODY, MJUMPAGESIZE max) with no clamp. An oversized length makes if_input() read past the cluster into kernel heap β†’ guest info leak / corruption.

Trigger preconditions (NOT met on this guest)

  • A vmxnet3 NIC (VMware) that emits an RX completion with len exceeding the backing cluster size β€” i.e. a malicious or buggy hypervisor.

Files

  • VERDICT.md, fix.diff, env.txt, build.log (kernel-build excerpt).
VERDICT.md verdict full narrative + path:line trace
↓ download raw

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:

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.

Fix verification

not_testable

compile validated

kernel/module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. vmxnet3 rxcd->len no clamp -> m_len past cluster -> heap OOB. vmx in GENERIC, no vmxnet3 NIC.