Merge branch 'net-bridge-fix-two-mst-bugs'

Nikolay Aleksandrov says:

====================
net: bridge: fix two MST bugs

Patch 01 fixes a race condition that exists between expired fdb deletion
and port deletion when MST is enabled. Learning can happen after the
port's state has been changed to disabled which could lead to that
port's memory being used after it's been freed. The issue was reported
by syzbot, more information in patch 01. Patch 02 fixes an issue with
MST's static key which Ido spotted, we can have multiple bridges with MST
and a single bridge can erroneously disable it for all.
====================

Link: https://patch.msgid.link/20251105111919.1499702-1-razor@blackwall.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-11-06 07:32:19 -08:00
5 changed files with 22 additions and 8 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ static inline int should_deliver(const struct net_bridge_port *p,
vg = nbp_vlan_group_rcu(p);
return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
(br_mst_is_enabled(p->br) || p->state == BR_STATE_FORWARDING) &&
(br_mst_is_enabled(p) || p->state == BR_STATE_FORWARDING) &&
br_allowed_egress(vg, skb) && nbp_switchdev_allowed_egress(p, skb) &&
!br_skb_isolated(p, skb);
}
+1
View File
@@ -386,6 +386,7 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
del_nbp(p);
}
br_mst_uninit(br);
br_recalculate_neigh_suppress_enabled(br);
br_fdb_delete_by_port(br, NULL, 0, 1);
+2 -2
View File
@@ -94,7 +94,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
br = p->br;
if (br_mst_is_enabled(br)) {
if (br_mst_is_enabled(p)) {
state = BR_STATE_FORWARDING;
} else {
if (p->state == BR_STATE_DISABLED) {
@@ -429,7 +429,7 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
return RX_HANDLER_PASS;
forward:
if (br_mst_is_enabled(p->br))
if (br_mst_is_enabled(p))
goto defer_stp_filtering;
switch (p->state) {
+8 -2
View File
@@ -22,6 +22,12 @@ bool br_mst_enabled(const struct net_device *dev)
}
EXPORT_SYMBOL_GPL(br_mst_enabled);
void br_mst_uninit(struct net_bridge *br)
{
if (br_opt_get(br, BROPT_MST_ENABLED))
static_branch_dec(&br_mst_used);
}
int br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids)
{
const struct net_bridge_vlan_group *vg;
@@ -225,9 +231,9 @@ int br_mst_set_enabled(struct net_bridge *br, bool on,
return err;
if (on)
static_branch_enable(&br_mst_used);
static_branch_inc(&br_mst_used);
else
static_branch_disable(&br_mst_used);
static_branch_dec(&br_mst_used);
br_opt_toggle(br, BROPT_MST_ENABLED, on);
return 0;
+10 -3
View File
@@ -1935,10 +1935,12 @@ static inline bool br_vlan_state_allowed(u8 state, bool learn_allow)
/* br_mst.c */
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
DECLARE_STATIC_KEY_FALSE(br_mst_used);
static inline bool br_mst_is_enabled(struct net_bridge *br)
static inline bool br_mst_is_enabled(const struct net_bridge_port *p)
{
/* check the port's vlan group to avoid racing with port deletion */
return static_branch_unlikely(&br_mst_used) &&
br_opt_get(br, BROPT_MST_ENABLED);
br_opt_get(p->br, BROPT_MST_ENABLED) &&
rcu_access_pointer(p->vlgrp);
}
int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
@@ -1952,8 +1954,9 @@ int br_mst_fill_info(struct sk_buff *skb,
const struct net_bridge_vlan_group *vg);
int br_mst_process(struct net_bridge_port *p, const struct nlattr *mst_attr,
struct netlink_ext_ack *extack);
void br_mst_uninit(struct net_bridge *br);
#else
static inline bool br_mst_is_enabled(struct net_bridge *br)
static inline bool br_mst_is_enabled(const struct net_bridge_port *p)
{
return false;
}
@@ -1987,6 +1990,10 @@ static inline int br_mst_process(struct net_bridge_port *p,
{
return -EOPNOTSUPP;
}
static inline void br_mst_uninit(struct net_bridge *br)
{
}
#endif
struct nf_br_ops {