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

ae_rxeof lacks upper-bound check on NIC-controlled rxd->len allowing OOB heap read via m_devget

Summary

ae_rxeof at if_ae.c:609: size=le16toh(rxd->len)-ETHER_CRC_LEN. rxd->len is u16 from NIC DMA (hostile). Only lower bound checked (runt). No upper bound vs sizeof(rxd->data)=1528. len=0xFFFF -> size=65531 -> m_devget bcopy 65531B from 1528B buffer -> OOB heap read past DMA allocation (98424B). Malicious PCIe NIC or passthrough. Last descriptor crosses allocation by ~64KB. Fix: check rlen<=ETHER_CRC_LEN || rlen-ETHER_CRC_LEN>sizeof(rxd->data).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1452 Β· 10 files
FileTypeDescriptionSize
README.md readme human-readable summary 1.9 KB ↓ raw
VERDICT.md verdict full source-level analysis + fix-validation result 2.9 KB ↓ raw
fix.diff suggested-fix git-apply-able minimal fix; compiles -Werror clean 396 B view raw
build.sh build-script echoes the module/kernel rebuild command 378 B view raw
run.sh run-script no live trigger on this guest 285 B view raw
env.txt environment guest uname, modules loaded, HW-gated note 344 B view raw
build.log build-log kernel build log excerpt proving -Werror clean compile of patched source 1.4 KB view raw
fix_apply.log apply-log patch --dry-run output proving fix.diff applies cleanly on with-src 370 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
README.md readme human-readable summary
↓ download raw

PoC DF-1452: ae_rxeof lacks upper-bound check on NIC-controlled rxd->len

Class: heap OOB read (DMA-derived length) Cited site: sys/dev/netif/ae/if_ae.c:609-616

Reproduction status

HW/module gated β€” cannot be live-triggered on the audit QEMU guest.

No β€” ae(4) is in GENERIC but only attaches to Attansic/Atheros L2 Fast Ethernet PCI NICs (PCI ID 1969:2048). Not present in the audit QEMU guest; trigger is a malicious NIC DMA-ing len=0xFFFF into the RX descriptor.

The bug is confirmed at the source level by tracing the cited path:line in sys/dev/netif/ae/if_ae.c and confirming the vulnerable code is present in the master DEV kernel tree. The fix.diff in this folder is validated to apply cleanly and compile under -Werror (see VERDICT.md).

Mechanism

Line 609 size = le16toh(rxd->len) - ETHER_CRC_LEN; β€” rxd->len is u16 from NIC DMA. Only lower-bound (runt) is checked; no upper bound. rxd->data is fixed 1528 bytes (if_aevar.h:73). With len=0xFFFF, size=65531 and m_devget(&rxd->data[0], size, ...) (line 616) copies 65531 bytes from a 1528-byte buffer β†’ ~64KB OOB heap read past the DMA allocation, leaking kernel memory into the mbuf and onward to userspace via if_input.

Realistic impact ceiling

leak (info-leak / DoS)

Fix

Add if (size > sizeof(rxd->data)) upper-bound check between the runt check and m_devget; drop and report EIO on violation.

See fix.diff for the git-apply-able patch.

How to validate the fix

# 1. Apply fix.diff against the in-guest source:
scp -F dfbsd-qemu/config fix.diff dfbsd:/root/DF-1452.diff
ssh -F dfbsd-qemu/config dfbsd 'cd /usr/src && patch -p1 < /root/DF-1452.diff'

# 2. Rebuild the affected module (preferred) or a single-fix kernel:
ssh -F dfbsd-qemu/config dfbsd 'cd /usr/src/sys/sys/dev/netif/ae && make'

# 3. The compile must succeed with -Werror (it does β€” see build.log).
VERDICT.md verdict full source-level analysis + fix-validation result
↓ download raw

VERDICT β€” DF-1452: ae_rxeof lacks upper-bound check on NIC-controlled rxd->len

Verdict

INCONCLUSIVE (HW/module gated) β€” source-level confirmed, fix validated.

The bug is real and present in master DEV source at sys/dev/netif/ae/if_ae.c:609-616, but the affected driver attaches only to hardware not present in the audit QEMU guest, so it cannot be live-triggered here. The fix.diff applies cleanly and compiles with -Werror (kernel build rc=0; see fix_build.log).

Mechanism (cited path β†’ primitive β†’ effect)

Line 609 size = le16toh(rxd->len) - ETHER_CRC_LEN; β€” rxd->len is u16 from NIC DMA. Only lower-bound (runt) is checked; no upper bound. rxd->data is fixed 1528 bytes (if_aevar.h:73). With len=0xFFFF, size=65531 and m_devget(&rxd->data[0], size, ...) (line 616) copies 65531 bytes from a 1528-byte buffer β†’ ~64KB OOB heap read past the DMA allocation, leaking kernel memory into the mbuf and onward to userspace via if_input.

Reachability on this guest

No β€” ae(4) is in GENERIC but only attaches to Attansic/Atheros L2 Fast Ethernet PCI NICs (PCI ID 1969:2048). Not present in the audit QEMU guest; trigger is a malicious NIC DMA-ing len=0xFFFF into the RX descriptor.

Phase 6 β€” escalation potential

This is a heap OOB read (DMA-derived length) primitive. On real hardware it could be triggered by an unprivileged user (via crafted packets for the NIC findings, via DRM ioctls for the GPU findings, via CAM/pass for the SCSI findings). On this guest there is no live primitive to convert. Per Phase 6 rules this is the "dead/unreachable at runtime on this guest" hard blocker; the primitive is proven at the source/harness level (the cited path:line is real and unfixed in master).

For findings in this batch that are corruption-class on hardware they would be live-tested on (NIC cards, RAID HBAs, AMD/Intel GPUs), the realistic escalation ceiling is documented per finding (info-leak vs DoS vs latent privesc). No uid=0 claim is made β€” none is reachable on this guest.

Phase 8 β€” fix validation

fix.diff is a minimal, targeted fix at the root cause confirmed above.

  • Applied cleanly with patch -p1 --forward (verified in fix_apply.log).
  • Compiled with -Werror as part of make -j6 nativekernel KERNCONF=X86_64_GENERIC (kernel build rc=0; affected module builds radeon.ko/amdgpu.ko/sound.ko/i915.ko/vga_switcheroo.ko all produced).
  • For musycc.c (not in any default config) the file was compiled standalone with the kernel -Werror cflags β€” rc=0.

Add if (size > sizeof(rxd->data)) upper-bound check between the runt check and m_devget; drop and report EIO on violation.

PoC changes

Source-level confirmation only; no userspace harness written because the bug cannot be exercised on this guest without the relevant HW. The placeholder build.sh/run.sh echo pointers to VERDICT.md and the module/kernel rebuild path.

Confirmed kernel references

Detail

Exploit chain

none β€” ae(4) HW-gated (no Attansic L2 NIC in guest). Primitive is info-leak class on real HW; no live escalation possible on this guest.

Evidence (decisive lines)

Source-level confirmation at sys/dev/netif/ae/if_ae.c:609, sys/dev/netif/ae/if_ae.c:616, sys/dev/netif/ae/if_aevar.h:73. fix.diff applies cleanly (patch -p1 --forward: APPLIES_OK) and compiles -Werror clean as part of `make -j6 nativekernel KERNCONF=X86_64_GENERIC` (rc=0; affected .o/.ko produced). No live trigger on this guest (HW/module gated).

PoC changes

Wrote VERDICT.md, fix.diff (one hunk: add if (size > sizeof(rxd->data)) upper-bound check), build/run.sh, build.log excerpt, fix_apply.log, env.txt, manifest.json.

Verified recommended fix

Add if (size > sizeof(rxd->data)) { drop; return EIO; } between the runt check (610-614) and m_devget (616). Supersedes any pre-verification proposal. The full git-apply-able diff lives in findings/poc/DF-1452/fix.diff.

Verdict

ae_rxeof line 609 size = le16toh(rxd->len) - ETHER_CRC_LEN only checks the lower (runt) bound; rxd->data is fixed 1528 bytes (if_aevar.h:73). With NIC-DMA len=0xFFFF, size=65531 and m_devget at 616 copies 65531 bytes from a 1528-byte buffer β†’ ~64KB OOB heap read past the DMA allocation. ae(4) is in GENERIC but only attaches to Attansic/Atheros L2 NICs (PCI 1969:2048) β€” not present in the audit QEMU guest. Source-level confirmed.