mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
flow_dissector: check device type before reading ETH_ADDRS
[ Upstream commitbf6e8af2c8] __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:67a900cc04("flow_dissector: introduce support for Ethernet addresses") Signed-off-by: Yun Zhou <yun.zhou@windriver.com> Link: https://patch.msgid.link/20260616123057.482154-1-yun.zhou@windriver.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
f104794507
commit
9a65860959
@@ -1149,13 +1149,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,
|
||||
|
||||
Reference in New Issue
Block a user