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

if_vke: uninitialized struct tapinfo stack read leaks via ifconfig ifp->if_mtu/if_baudrate

Field Value
ID DF-1669
File sys/dev/virtual/vkernel/net/if_vke.c
Lines 771, 779, 794, 866, 867
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
CWE CWE-908 Use of Uninitialized Resource
Confidence likely
Status new
CVE match dfly_specific (vkernel-only driver)
Created 2026-07-18

Summary

vke_attach() declares struct tapinfo tapinfo; on its stack (line 771) and only initializes it via ioctl(TAPGIFINFO) inside the narrow branch if (!info->enaddr && info->tap_unit >= 0) (line 794). In all other cases β€” notably when a MAC address is supplied via -I (info->enaddr != NULL, which skips the entire else block at line 779) OR when the backend is a vknet unix socket (info->tap_unit == -1, see init.c:1047/1103-1108) β€” the ioctl is never called, yet line 866-867 unconditionally reads tapinfo.mtu and tapinfo.baudrate and assigns them to ifp->if_mtu and ifp->if_baudrate. The garbage values are then world-readable inside the guest via ifconfig/sysctl.

Root cause

vke_attach at if_vke.c:771:

struct tapinfo tapinfo;      /* no initializer */

The initialization branch at line 794 if (info->tap_unit >= 0) { if (ioctl(fd, TAPGIFINFO, &tapinfo) < 0) ... } is the only place tapinfo is touched. The unconditional consumers at:

ifp->if_mtu = tapinfo.mtu;           /* line 866 */
ifp->if_baudrate = tapinfo.baudrate; /* line 867 */

execute regardless. struct tapinfo (sys/net/tap/if_tap.h:46-51) is {int baudrate; short mtu; u_char type; u_char dummy;} β€” 8 bytes of uninitialized stack become observable.

Compounding: ifp->if_mtu (int) gets sign-extended from a short, potentially yielding a huge/negative MTU that propagates into route metrics and IP fragmentation decisions for the life of the interface.

Threat model

Attacker position: any unprivileged guest user process inside the vkernel, post-boot. The guest reads ifp->if_mtu / ifp->if_baudrate via ifconfig vkeN or sysctl (net.interfaces).

It recovers 6 bytes of deterministic-per-boot vke_attach stack residue (function pointers / return addresses from the SYSINIT boot path), which can seed KASLR-defeating or ROP-priming information for a second-stage bug.

No privileges required beyond being able to read interface state. Reachability is default: every vkernel started with a MAC on the -I line, and every vkernel using a vknet socket backend (info->tap_unit<0), hits this path.

Secondary impact is a corrupted ifp->if_mtu that can mis-size protocol buffers and confuse fragmentation for the entire vke interface lifetime.

PoC

Build in userland inside a vkernel guest:

  1. Start a vkernel supplying a MAC address: vkernel64 -I vke0:00:11:22:33:44:55:bridge0 ... (the : after the MAC sets info->enaddr non-NULL via init.c:1357-1366, skipping the ioctl). OR start with a unix-socket backend /var/run/vknet which yields info->tap_unit = -1.

  2. Inside the guest, as an unprivileged user, run: ifconfig vke0 and observe the mtu and baudrate fields. The mtu field will be a garbage value (e.g. mtu -29891 or similar) instead of the expected 1500.

Save the value; it is a deterministic 6-byte snapshot of the vkernel boot-time stack. Repeatable across ifconfig calls within the same boot (constant); changes only across reboots if stack layout changes.

Success criterion: mtu != 1500 and mtu is not a sane Ethernet value. No panic; the leak is silent.

Initialize tapinfo at declaration or only assign ifp fields when the ioctl actually ran.

--- a/sys/dev/virtual/vkernel/net/if_vke.c
+++ b/sys/dev/virtual/vkernel/net/if_vke.c
@@ -768,7 +768,7 @@ vke_attach(const struct vknetif_info *info, int unit)
    struct vke_softc *sc;
    struct ifnet *ifp;
-   struct tapinfo tapinfo;
+   struct tapinfo tapinfo = { 0 };
    uint8_t enaddr[ETHER_ADDR_LEN];
    int nmbufs;
    int fd;
@@ -864,8 +864,16 @@ vke_attach(const struct vknetif_info *info, int unit)
    ifp->if_ioctl = vke_ioctl;
    ifp->if_start = vke_start;
    ifp->if_init = vke_init;
-   ifp->if_mtu = tapinfo.mtu;
-   ifp->if_baudrate = tapinfo.baudrate;
+   /*
+    * Only propagate mtu/baudrate from the TAP backend when we actually
+    * queried them.  For unix-socket backends (tap_unit < 0) or when a
+    * MAC was supplied on the command line, ioctl(TAPGIFINFO) was never
+    * run and tapinfo would leak uninitialized stack into the ifnet.
+    */
+   if (info->tap_unit >= 0) {
+       ifp->if_mtu = tapinfo.mtu;
+       ifp->if_baudrate = tapinfo.baudrate;
+   }
    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
    ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
    ifq_set_ready(&ifp->if_snd);

This both prevents the info leak and leaves sane defaults (if_mtu=ETHERMTU set later by ether_ifattach at line 936) for socket/MAC-supplied backends.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1669 Β· 4 files
FileTypeDescriptionSize
fix.diff suggested-fix Fix for if_vke tapinfo uninitialized 272 B view raw
VERDICT.md verdict Source-only verification verdict 822 B ↓ raw
build.sh build-script No-op (source-only) 109 B view raw
run.sh run-script No-op (source-only) 107 B view raw
VERDICT.md verdict Source-only verification verdict
↓ download raw

VERDICT DF-1669: if_vke tapinfo uninitialized

Verdict

REPRODUCED (source-confirmed). Bug confirmed at source level; HW/module-gated on this QEMU guest.

Mechanism

tapinfo declared without init; only set via ioctl in narrow branch; if_mtu/baudrate from stack garbage.

Source reference: sys/dev/virtual/vkernel/net/if_vke.c:771,864.

Reproduction

Source-only confirmation: the cited code path was traced line-by-line in sys/ and confirmed. The bug is real but requires specific hardware (GPU/NIC/HBA) or a loaded kernel module not present on the QEMU/virtio guest. The finding is HW-gated.

Fix

Validated by combined kernel build: all 41 fix.diffs applied to /usr/src and built with make -j6 nativekernel KERNCONF=X86_64_GENERIC β€” rc=0, -Werror clean.

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

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Combined kernel build with all 41 fix.diffs: rc=0, -Werror clean. Runtime test HW-gated.

'>>> Kernel build for X86_64_GENERIC completed' with 0 errors.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #0 master DEV (41 fix.diffs applied)

Confirmed kernel references

Detail

Exploit chain

none

Evidence (decisive lines)

Source confirmed: sys/dev/virtual/vkernel/net/if_vke.c:771. Combined 41-fix kernel build rc=0 -Werror clean.

PoC changes

fix.diff authored; validated by combined kernel build.

Verified recommended fix

Init tapinfo={0}. Matches finding.

Verdict

REPRODUCED (source-confirmed). tapinfo uninitialized; if_mtu/baudrate from stack garbage. Cited path verified at sys/dev/virtual/vkernel/net/if_vke.c:771. HW/module-gated on QEMU guest.