[SRU][J][PATCH 1/1] net: dsa: Avoid cross-chip syncing of VLAN filtering

Cengiz Can cengiz.can at canonical.com
Tue Sep 8 21:56:33 UTC 2026


From: Tobias Waldekranz <tobias at waldekranz.com>

Changes to VLAN filtering are not applicable to cross-chip
notifications.

On a system like this:

.-----.   .-----.   .-----.
| sw1 +---+ sw2 +---+ sw3 |
'-1-2-'   '-1-2-'   '-1-2-'

Before this change, upon sw1p1 leaving a bridge, a call to
dsa_port_vlan_filtering would also be made to sw2p1 and sw3p1.

In this scenario:

.---------.   .-----.   .-----.
|   sw1   +---+ sw2 +---+ sw3 |
'-1-2-3-4-'   '-1-2-'   '-1-2-'

When sw1p4 would leave a bridge, dsa_port_vlan_filtering would be
called for sw2 and sw3 with a non-existing port - leading to array
out-of-bounds accesses and crashes on mv88e6xxx.

Fixes: d371b7c92d19 ("net: dsa: Unset vlan_filtering when ports leave the bridge")
Signed-off-by: Tobias Waldekranz <tobias at waldekranz.com>
Reviewed-by: Vladimir Oltean <olteanv at gmail.com>
Signed-off-by: David S. Miller <davem at davemloft.net>
(backported from commit 108dc8741c203e9d6ce4e973367f1bac20c7192b)
[bot_kybele: This tree inlines the VLAN-filtering sync logic in
 dsa_switch_bridge_leave instead of the dsa_switch_sync_vlan_filtering() helper
 the upstream patch guards; wrapped that inlined block in the same local-switch
 condition (dst->index==info->tree_index && ds->index==info->sw_index) and
 discarded git-am's fuzzy-matched newer-tree function copy.]
CVE-2022-49234
Assisted-by: kybele:claude-opus-4.8
Signed-off-by: Cengiz Can <cengiz.can at canonical.com>
---
 net/dsa/switch.c | 73 ++++++++++++++++++++++++++----------------------
 1 file changed, 40 insertions(+), 33 deletions(-)

diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index fb69f2f14234..89c08658f8f5 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -132,44 +132,51 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
 						info->sw_index, info->port,
 						info->br);
 
-	if (ds->needs_standalone_vlan_filtering && !br_vlan_enabled(info->br)) {
-		change_vlan_filtering = true;
-		vlan_filtering = true;
-	} else if (!ds->needs_standalone_vlan_filtering &&
-		   br_vlan_enabled(info->br)) {
-		change_vlan_filtering = true;
-		vlan_filtering = false;
-	}
-
-	/* If the bridge was vlan_filtering, the bridge core doesn't trigger an
-	 * event for changing vlan_filtering setting upon slave ports leaving
-	 * it. That is a good thing, because that lets us handle it and also
-	 * handle the case where the switch's vlan_filtering setting is global
-	 * (not per port). When that happens, the correct moment to trigger the
-	 * vlan_filtering callback is only when the last port leaves the last
-	 * VLAN-aware bridge.
+	/* Changes to VLAN filtering are not applicable to cross-chip
+	 * notifications.
 	 */
-	if (change_vlan_filtering && ds->vlan_filtering_is_global) {
-		for (port = 0; port < ds->num_ports; port++) {
-			struct net_device *bridge_dev;
-
-			bridge_dev = dsa_to_port(ds, port)->bridge_dev;
+	if (dst->index == info->tree_index && ds->index == info->sw_index) {
+		if (ds->needs_standalone_vlan_filtering &&
+		    !br_vlan_enabled(info->br)) {
+			change_vlan_filtering = true;
+			vlan_filtering = true;
+		} else if (!ds->needs_standalone_vlan_filtering &&
+			   br_vlan_enabled(info->br)) {
+			change_vlan_filtering = true;
+			vlan_filtering = false;
+		}
 
-			if (bridge_dev && br_vlan_enabled(bridge_dev)) {
-				change_vlan_filtering = false;
-				break;
+		/* If the bridge was vlan_filtering, the bridge core doesn't
+		 * trigger an event for changing vlan_filtering setting upon
+		 * slave ports leaving it. That is a good thing, because that
+		 * lets us handle it and also handle the case where the switch's
+		 * vlan_filtering setting is global (not per port). When that
+		 * happens, the correct moment to trigger the vlan_filtering
+		 * callback is only when the last port leaves the last VLAN-aware
+		 * bridge.
+		 */
+		if (change_vlan_filtering && ds->vlan_filtering_is_global) {
+			for (port = 0; port < ds->num_ports; port++) {
+				struct net_device *bridge_dev;
+
+				bridge_dev = dsa_to_port(ds, port)->bridge_dev;
+
+				if (bridge_dev && br_vlan_enabled(bridge_dev)) {
+					change_vlan_filtering = false;
+					break;
+				}
 			}
 		}
-	}
 
-	if (change_vlan_filtering) {
-		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
-					      vlan_filtering, &extack);
-		if (extack._msg)
-			dev_err(ds->dev, "port %d: %s\n", info->port,
-				extack._msg);
-		if (err && err != -EOPNOTSUPP)
-			return err;
+		if (change_vlan_filtering) {
+			err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
+						      vlan_filtering, &extack);
+			if (extack._msg)
+				dev_err(ds->dev, "port %d: %s\n", info->port,
+					extack._msg);
+			if (err && err != -EOPNOTSUPP)
+				return err;
+		}
 	}
 
 	return dsa_tag_8021q_bridge_leave(ds, info);
-- 
2.53.0




More information about the kernel-team mailing list