[PATCH v4] flow_dissector: check device type before reading ETH_ADDRS
From: Yun Zhou <hidden>
Date: 2026-06-16 12:31:33
Also in:
lkml
Subsystem:
networking [general], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
__skb_flow_dissect() unconditionally reads 12 bytes from eth_hdr(skb) when FLOW_DISSECTOR_KEY_ETH_ADDRS is requested. This assumes the skb has a valid Ethernet header at mac_header, which is not always the case. The problem can be triggered by: 1. Creating a TUN device in L3 mode (IFF_TUN, hard_header_len=0) 2. Attaching a multiq qdisc with a flower filter matching on eth_src 3. Sending a packet through AF_PACKET Since TUN in L3 mode has no link-layer header, mac_header points to the L3 data area. The flow dissector reads 12 bytes of uninitialized skb memory, which then propagates through fl_set_masked_key() and is used as a rhashtable lookup key in __fl_lookup(), as reported by KMSAN. Rejecting the filter in the control path (at tc filter add time) is not feasible because TC filter blocks can be shared between arbitrary devices -- a filter installed on an Ethernet device may later classify packets on a headerless device through a shared block. The device association is not fixed at filter creation time. Fix this by gating the memcpy on dev->type == ARPHRD_ETHER, which ensures only true Ethernet-framed packets have their addresses read. This is more precise than the previous hard_header_len >= 12 check, which would incorrectly pass for non-Ethernet link types like IPoIB (ARPHRD_INFINIBAND, hard_header_len=24) and FDDI (hard_header_len=21) whose L2 headers are not in Ethernet format. Additionally check skb_mac_header_was_set() to guard against the pathological case where mac_header is the unset sentinel (~0U), which would cause eth_hdr() to return a wild pointer. For the act_mirred redirect case (Ethernet packet redirected to a non-Ethernet device sharing a TC block), zeroing the key is the correct behavior: the packet is now being classified on the target device, where Ethernet address matching is not semantically meaningful. Note: on non-Ethernet devices, the zeroed key will match a filter configured with all-zero MAC addresses. This is an improvement over the previous behavior where uninitialized memory could randomly match any filter. Reported-by: syzbot+fa2f5b1fb06147be5e16@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fa2f5b1fb06147be5e16 Fixes: 67a900cc0436 ("flow_dissector: introduce support for Ethernet addresses") Signed-off-by: Yun Zhou <redacted> --- v4: - Use dev->type == ARPHRD_ETHER instead of hard_header_len >= 12 to avoid false positives on non-Ethernet link types (IPoIB, FDDI) - Add skb_mac_header_was_set() guard against unset mac_header sentinel - Document act_mirred and all-zero key edge cases in commit message v3: - Replace skb_tail_pointer() - skb_mac_header() length check with skb->dev->hard_header_len check. v2: - Adjust commit message and comment. net/core/flow_dissector.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 2a98f5fa74eb..8aa4f9b4df81 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c@@ -1173,13 +1173,21 @@ bool __skb_flow_dissect(const struct net *net, if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { - struct ethhdr *eth = eth_hdr(skb); struct flow_dissector_key_eth_addrs *key_eth_addrs; key_eth_addrs = skb_flow_dissector_target(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS, target_container); - memcpy(key_eth_addrs, eth, sizeof(*key_eth_addrs)); + /* TC filter blocks can be shared across devices with + * different link types, so we cannot validate this + * when the filter is installed -- check at dissect time. + */ + if (skb && skb->dev && + skb->dev->type == ARPHRD_ETHER && + skb_mac_header_was_set(skb)) + memcpy(key_eth_addrs, eth_hdr(skb), sizeof(*key_eth_addrs)); + else + memset(key_eth_addrs, 0, sizeof(*key_eth_addrs)); } if (dissector_uses_key(flow_dissector,
--
2.43.0