[PATCH nf] netfilter: bridge: verify device is a bridge port

Florian Westphal <[email protected]>
Newsgroups gmane.comp.security.firewalls.netfilter.devel
Message-ID <[email protected]>
commit ccb9fd4b8753 ("netfilter: revalidate bridge ports") closed an
rcu escape where a bridge port is removed from the bridge and then reassigned
to a different device.

Unfortunately there are gadgets other than nfqueue that allow such
escapes, one way is br_netfilter: skb will be queeud to defrag engine,
leave rcu protection, then, on reinject, stored device index may point
to a different device:

BUG: KASAN: slab-out-of-bounds in br_forward+0x304/0x370 [bridge]
[..]
Call Trace:
 br_forward+0x304/0x370 [bridge]
 nft_reject_bridge_eval+0x441/0x10c1 [nft_reject_bridge]
 nft_do_chain+0x21f/0x1610 [nf_tables]
 nft_do_chain_bridge+0x21b/0x1220 [nf_tables]
 nf_hook_slow+0xaa/0x1e0
 br_nf_hook_thresh+0x1f1/0x3b0 [br_netfilter]
 br_nf_pre_routing_finish+0x78f/0x14d0 [br_netfilter]
 br_nf_pre_routing+0xd96/0x1630 [br_netfilter]
 nf_hook_bridge_pre+0x290/0x4b0 [bridge]
 __netif_receive_skb_core.constprop.0+0x55b/0x2ba0

Revalidate unconditionally.  The fixes tag is intentionally wrong; that
change is a dependency and is also a required bug fix.

Fixes: 9874808878d9 ("netfilter: bridge: replace physindev with physinif in nf_bridge_info")
Reported-by: [email protected]
Signed-off-by: Florian Westphal <[email protected]>
---
 include/linux/netfilter_bridge.h | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h
index 743475ca7e9d..46cfebc587d8 100644
--- a/include/linux/netfilter_bridge.h
+++ b/include/linux/netfilter_bridge.h
@@ -55,12 +55,21 @@ static inline int nf_bridge_get_physoutif(const struct sk_buff *skb)
 	return nf_bridge->physoutdev ? nf_bridge->physoutdev->ifindex : 0;
 }
 
+static inline struct net_device *
+__nf_bridge_get_physport(struct net_device *dev)
+{
+	return dev && netif_is_bridge_port(dev) ? dev : NULL;
+}
+
 static inline struct net_device *
 nf_bridge_get_physindev(const struct sk_buff *skb, struct net *net)
 {
 	const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
-	return nf_bridge ? dev_get_by_index_rcu(net, nf_bridge->physinif) : NULL;
+	if (!nf_bridge)
+		return NULL;
+
+	return __nf_bridge_get_physport(dev_get_by_index_rcu(net, nf_bridge->physinif));
 }
 
 static inline struct net_device *
@@ -68,7 +77,7 @@ nf_bridge_get_physoutdev(const struct sk_buff *skb)
 {
 	const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
-	return nf_bridge ? nf_bridge->physoutdev : NULL;
+	return nf_bridge ? __nf_bridge_get_physport(nf_bridge->physoutdev) : NULL;
 }
 
 static inline bool nf_bridge_in_prerouting(const struct sk_buff *skb)
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.