[PATCH] netfilter: ebtables: ebt_802_3: drop short frames before the match reads LLC
From: Guo Zihao <hidden>
Date: 2026-09-28 08:29:07
Also in:
bridge, lkml, netfilter-devel
Subsystem:
ethernet bridge, networking [general], the rest · Maintainers:
Nikolay Aleksandrov, Ido Schimmel, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
ebt_802_3_mt() takes the frame pointer from skb_mac_header() and reads the LLC union fields (dsap, ssap, ctrl, type) without making sure that those bytes are in the linear area. For an 802.3 frame (length field below 1536) the payload is LLC, but only ETH_HLEN bytes are guaranteed linear by the time the bridge hooks run, and neither ebt_do_table() nor ebt_basic_match() pulls more. A frame that carries nothing past the 14 byte Ethernet header therefore reads into the skb tailroom. Check that sizeof(struct ebt_802_3_hdr) bytes are actually linear before reading, and drop the packet through par->hotdrop when they are not, the same signalling other ebt matches use for a packet they cannot examine. No Fixes tag. The unchecked skb_mac_header() dereference comes from the initial ebtables merge and has not been touched since. Reviewed-by: Liu Chao <redacted> Signed-off-by: Guo Zihao <redacted> --- net/bridge/netfilter/ebt_802_3.c | 36 +++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c
index 68c2519bd..533f0d36e 100644
--- a/net/bridge/netfilter/ebt_802_3.c
+++ b/net/bridge/netfilter/ebt_802_3.c@@ -14,27 +14,43 @@ #include <linux/skbuff.h> #include <uapi/linux/netfilter_bridge/ebt_802_3.h> -static struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb) -{ - return (struct ebt_802_3_hdr *)skb_mac_header(skb); -} - static bool ebt_802_3_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_802_3_info *info = par->matchinfo; - const struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb); - __be16 type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type; + struct ebt_802_3_hdr _frame; + const struct ebt_802_3_hdr *frame; + int mac_offset = skb_mac_offset(skb); + __be16 type; + + /* skb->data sits past the Ethernet header when the match runs, so the + * frame starts before skb->data. skb_header_pointer() takes an offset + * relative to skb->data and cannot express that, so check the linear + * area against the mac header ourselves and use skb_mac_header() + * directly once the check has passed. + */ + if (mac_offset < 0) + mac_offset = -mac_offset; + if (mac_offset + sizeof(_frame) > skb_headlen(skb)) { + /* The frame is too short to hold an LLC header. */ + par->hotdrop = true; + return false; + } + + frame = (const struct ebt_802_3_hdr *)skb_mac_header(skb); + type = frame->llc.ui.ctrl & IS_UI ? frame->llc.ui.type : + frame->llc.ni.type; if (info->bitmask & EBT_802_3_SAP) { - if (NF_INVF(info, EBT_802_3_SAP, info->sap != hdr->llc.ui.ssap)) + if (NF_INVF(info, EBT_802_3_SAP, info->sap != frame->llc.ui.ssap)) return false; - if (NF_INVF(info, EBT_802_3_SAP, info->sap != hdr->llc.ui.dsap)) + if (NF_INVF(info, EBT_802_3_SAP, info->sap != frame->llc.ui.dsap)) return false; } if (info->bitmask & EBT_802_3_TYPE) { - if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE)) + if (!(frame->llc.ui.dsap == CHECK_TYPE && + frame->llc.ui.ssap == CHECK_TYPE)) return false; if (NF_INVF(info, EBT_802_3_TYPE, info->type != type)) return false;
--
2.50.1