[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