summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolay Aleksandrov <razor@blackwall.org>2026-09-11 13:50:21 +0300
committerPaolo Abeni <pabeni@redhat.com>2026-09-15 12:27:41 +0200
commit18a6fe05fb6e18de29fa90d388bb34044114b3d8 (patch)
tree2c4acf60475d91b24f7d3cd3978a353b51b2c59f
parent7c8810c2e69c3d9ca6df870b40ae9218e50b4fb1 (diff)
downloadlinux-next-18a6fe05fb6e18de29fa90d388bb34044114b3d8.tar.gz
linux-next-18a6fe05fb6e18de29fa90d388bb34044114b3d8.zip
net: bridge: mst: move switchdev call outside rcu
This is a follow-up of one of sashiko's pre-existing bug reports. br_mst_set_state() calls switchdev_port_attr_set() for nonzero MSTIs while holding rcu_read_lock() which invokes the blocking switchdev notifier chain and may sleep. Nonzero MSTI changes come from netlink with rtnl held. Move the switchdev call before entering the rcu section and assert that rtnl is held. The call cannot be deferred because netlink needs its error and extack. Also DSA reads the old bridge MST state during the callback and checks it. A deferred callback will be late and will see the updated state. Fixes: 3a7c1661ae13 ("net: bridge: mst: fix vlan use-after-free") Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Link: https://patch.msgid.link/20260911105021.1385934-1-razor@blackwall.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r--net/bridge/br_mst.c20
1 files changed, 12 insertions, 8 deletions
diff --git a/net/bridge/br_mst.c b/net/bridge/br_mst.c
index 43a300ae6bfa..1654efd3045b 100644
--- a/net/bridge/br_mst.c
+++ b/net/bridge/br_mst.c
@@ -107,21 +107,24 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
struct net_bridge_vlan *v;
int err = 0;
- rcu_read_lock();
- vg = nbp_vlan_group_rcu(p);
- if (!vg)
- goto out;
-
/* MSTI 0 (CST) state changes are notified via the regular
- * SWITCHDEV_ATTR_ID_PORT_STP_STATE.
+ * SWITCHDEV_ATTR_ID_PORT_STP_STATE. All other MSTIs are handled via
+ * netlink with RTNL held
*/
if (msti) {
+ ASSERT_RTNL();
+
err = switchdev_port_attr_set(p->dev, &attr, extack);
if (err && err != -EOPNOTSUPP)
goto out;
+ err = 0;
}
- err = 0;
+ rcu_read_lock();
+ vg = nbp_vlan_group_rcu(p);
+ if (!vg)
+ goto out_rcu_unlock;
+
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
if (v->brvlan->msti != msti)
continue;
@@ -129,8 +132,9 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
br_mst_vlan_set_state(vg, v, state);
}
-out:
+out_rcu_unlock:
rcu_read_unlock();
+out:
return err;
}