rtnl_fill_vfinfo() declares struct ifla_vf_broadcast on the stack without initialisation: struct ifla_vf_broadcast vf_broadcast; The struct contains a single fixed 32-byte field: /* include/uapi/linux/if_link.h */ struct ifla_vf_broadcast { __u8 broadcast[32]; }; The function then copies dev->broadcast into it using dev->addr_len as the length: memcpy(vf_broadcast.broadcast, dev->broadcast, dev->addr_len); On Ethernet devices (the overwhelming majority of SR-IOV NICs) dev->addr_len is 6, so only the first 6 bytes of broadcast[] are written. The remaining 26 bytes retain whatever was previously on the kernel stack. The full struct is then handed to userspace via: nla_put(skb, IFLA_VF_BROADCAST, sizeof(vf_broadcast), &vf_broadcast) leaking up to 26 bytes of uninitialised kernel stack per VF per RTM_GETLINK request, repeatable. The other vf_* structs in the same function are explicitly zeroed for exactly this reason - see the memset() calls for ivi, vf_vlan_info, node_guid and port_guid a few lines above. vf_broadcast was simply missed when it was added. The pattern used elsewhere in this file for the regular IFLA_BROADCAST attribute also avoids the issue by sending only dev->addr_len bytes rather than a fixed-size struct, but for IFLA_VF_BROADCAST the wire format is the fixed 32-byte struct, so the right fix is to zero the struct before the partial memcpy. Reachability and impact ----------------------- The leak is reachable by any unprivileged local process. AF_NETLINK with NETLINK_ROUTE requires no capabilities. The only environmental requirement is that the host has at least one SR-IOV-capable interface present (a parent device with VFs), which is the common case for cloud, datacenter and HPC hosts. Trigger: send RTM_GETLINK with an IFLA_EXT_MASK attribute whose value has the RTEXT_FILTER_VF bit set. The kernel will then walk each VF and emit IFLA_VFINFO_LIST, including IFLA_VF_BROADCAST, which carries the 26 bytes of uninitialised stack per VF. Stack residue at this call site can include return addresses (useful as a KASLR / function-pointer disclosure primitive) and transient sensitive data left over by whatever ran on the same kernel stack just prior. KASAN with stack instrumentation, or KMSAN, will flag the nla_put() when reproduced. Reproducer (unprivileged): import socket, struct IFLA_EXT_MASK = 29 RTEXT_FILTER_VF = 1 s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE) s.bind((0, 0)) hdr = struct.pack('=IHHII', 0, 18, 0x301, 0, 0) ifi = struct.pack('=BxHiII', 0, 0, 0, 0, 0) attr = (struct.pack('=HH', 8, IFLA_EXT_MASK) + struct.pack('=I', RTEXT_FILTER_VF)) msg = hdr + ifi + attr msg = struct.pack('=I', len(msg)) + msg[4:] s.send(msg) data = s.recv(65536) # Parse IFLA_VF_BROADCAST from the response. Bytes 7..32 of the # broadcast[] field are uninitialised kernel stack on Ethernet. Fix --- Zero the on-stack struct before the partial memcpy, matching the existing pattern used for the other vf_* structs in the same function. Reported-by: Kai Aizen Signed-off-by: Kai Aizen --- Note for reviewers: this is v1. I have not yet identified the exact introducing commit for the Fixes: tag and would appreciate a pointer, or I will resend as v2 once I have run git blame on a local checkout. The bug is present at least as far back as the introduction of struct ifla_vf_broadcast in net-next. net/core/rtnetlink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1572,6 +1572,7 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb, port_guid.vf = ivi.vf; memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac)); + memset(&vf_broadcast, 0, sizeof(vf_broadcast)); memcpy(vf_broadcast.broadcast, dev->broadcast, dev->addr_len); vf_vlan.vlan = ivi.vlan; vf_vlan.qos = ivi.qos; -- 2.43.0