DragonFlyBSD Kernel Audit
← dashboard
DF-0401

Heap buffer overflow via unchecked slot->len in VALE bridge forwarding: pkt_copy up to 65536 bytes into 2048-byte buffer

Field Value
ID DF-0401
Status new
Severity High
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE CWE-122 Heap-based Buffer Overflow
File sys/net/netmap/netmap_vale.c
Lines 988, 1330-1346
Area net (netmap VALE switch)
Confidence certain
Discovered 2026-07-01
Reported pending

Summary

The VALE software switch forwarding path copies packet data using a user-supplied length field (slot->len) without validating it against the netmap buffer size (~2048 bytes). An attacker who has mapped a VALE TX ring via /dev/netmap can set slot->len to any value up to 65535, causing pkt_copy/copyin to write up to 65536 bytes into a ~2048-byte kernel heap buffer — a controlled heap overflow with attacker-chosen data and length. The sibling code in netmap.c validates slot->len against NETMAP_BDG_BUF_SIZE in three separate places, but this check was omitted in the VALE forwarding fast path.

Root cause

nm_bdg_preflush() at sys/net/netmap/netmap_vale.c:988:

ft[ft_i].ft_len = slot->len;    /* no bounds check */

slot->len is a uint16_t from the user-mapped shared ring (writable via mmap of /dev/netmap). It can be any value 0–65535.

nm_bdg_flush() at sys/net/netmap/netmap_vale.c:1330-1346:

size_t len = (ft_p->ft_len + 63) & ~63;    /* round up; up to 65536 */
...
dst = BDG_NMB(&dst_na->up, slot);          /* ~2048-byte kernel buffer */
...
if (ft_p->ft_flags & NS_INDIRECT) {
    if (copyin(src, dst, len)) { ... }     /* line 1339: writes len bytes */
} else {
    pkt_copy(src, dst, (int)len);          /* line 1345: writes len bytes */
}

The destination dst is a netmap buffer of NETMAP_BDG_BUF_SIZE (default 2048 bytes). With slot->len = 65535, len rounds to 65536, writing 32× the buffer capacity.

The validation that exists in sibling code: - netmap.c:748: if (slot->len < 14 || slot->len > NETMAP_BDG_BUF_SIZE(...)) - netmap.c:1124: same check - netmap.c:2017: if (kring->nr_hwavail >= lim) (different but related)

None of these checks exist in the VALE forwarding path.

Threat model & preconditions

  • Attacker position: local user with access to /dev/netmap (mode 0660 root:wheel, or root in a jail where netmap is exposed).
  • Privileges gained or impact: kernel heap corruption with full attacker-controlled data and length. Enables kernel code execution, KASLR bypass, and jail escape.
  • Required config: netmap loaded (kldload netmap), a VALE bridge created.
  • Reachability: mmap the VALE TX ring, set slot->len to a large value, and trigger forwarding by sending packets through the bridge.

Proof of concept

PoC source: findings/poc/DF-0401/poc.c

Build & run

cc -o poc poc.c -lnetmap
./poc vale1:0        # requires /dev/netmap access

Expected output

Fatal trap 12: page fault while in kernel mode
KDB: stack backtrace:
#1 pkt_copy at netmap.c:...
#2 nm_bdg_flush at netmap_vale.c:1345
#3 netmap_bwrap_intr_notify at netmap_vale.c:...

Impact

  • Kernel heap overflow with 100% attacker-controlled data and length.
  • Heap grooming of the netmap buffer slab enables controlled overwrite of adjacent kernel objects, leading to arbitrary kernel code execution.
  • In a jail with exposed /dev/netmap, this is a reliable jail escape to host root.
  • The overflow also over-reads the source buffer (src via BDG_NMB), leaking adjacent netmap buffer contents.

Validate slot->len in nm_bdg_preflush() before storing it:

--- a/sys/net/netmap/netmap_vale.c
+++ b/sys/net/netmap/netmap_vale.c
@@ -985,6 +985,9 @@
        struct netmap_slot *slot = &ring->slot[j];
        char *buf;

+       if (slot->len > NETMAP_BDG_BUF_SIZE(na->up.nm_mem)) {
+           D("dropping oversize slot len %d", slot->len);
+           continue;
+       }
        ft[ft_i].ft_len = slot->len;

This mirrors the existing check at netmap.c:748.

References

  • NETMAP_BDG_BUF_SIZE is defined in netmap_kern.h and defaults to 2048.
  • The sibling validation in netmap.c at lines 748, 1124, 2017 proves the intended contract: slot lengths must be bounded by the buffer size.

Timeline

  • 2026-07-01 Discovered during automated audit.
  • 2026-07-01 Reported to DragonFlyBSD security contact (pending).