From: Nikolay Aleksandrov <razor@blackwall.org>
To: netdev@vger.kernel.org
Cc: tobias@waldekranz.com, idosch@nvidia.com, kuba@kernel.org,
davem@davemloft.net, bridge@lists.linux.dev, pabeni@redhat.com,
edumazet@google.com, horms@kernel.org, petrm@nvidia.com,
Nikolay Aleksandrov <razor@blackwall.org>,
syzbot+dd280197f0f7ab3917be@syzkaller.appspotmail.com
Subject: [PATCH net v2 1/2] net: bridge: fix use-after-free due to MST port state bypass
Date: Wed, 5 Nov 2025 13:19:18 +0200 [thread overview]
Message-ID: <20251105111919.1499702-2-razor@blackwall.org> (raw)
In-Reply-To: <20251105111919.1499702-1-razor@blackwall.org>
syzbot reported[1] a use-after-free when deleting an expired fdb. It is
due to a race condition between learning still happening and a port being
deleted, after all its fdbs have been flushed. The port's state has been
toggled to disabled so no learning should happen at that time, but if we
have MST enabled, it will bypass the port's state, that together with VLAN
filtering disabled can lead to fdb learning at a time when it shouldn't
happen while the port is being deleted. VLAN filtering must be disabled
because we flush the port VLANs when it's being deleted which will stop
learning. This fix adds a check for the port's vlan group which is
initialized to NULL when the port is getting deleted, that avoids the port
state bypass. When MST is enabled there would be a minimal new overhead
in the fast-path because the port's vlan group pointer is cache-hot.
[1] https://syzkaller.appspot.com/bug?extid=dd280197f0f7ab3917be
Fixes: ec7328b59176 ("net: bridge: mst: Multiple Spanning Tree (MST) mode")
Reported-by: syzbot+dd280197f0f7ab3917be@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/69088ffa.050a0220.29fc44.003d.GAE@google.com/
Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org>
---
v2: new fix approach using port's vlan group check when MST is enabled
we rely on the fact that the vlan group gets initialized to NULL
when the port is getting deleted
net/bridge/br_forward.c | 2 +-
net/bridge/br_input.c | 4 ++--
net/bridge/br_private.h | 8 +++++---
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 870bdf2e082c..dea09096ad0f 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -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);
}
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 67b4c905e49a..777fa869c1a1 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -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) {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 16be5d250402..b571d6f61389 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -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,
@@ -1953,7 +1955,7 @@ int br_mst_fill_info(struct sk_buff *skb,
int br_mst_process(struct net_bridge_port *p, const struct nlattr *mst_attr,
struct netlink_ext_ack *extack);
#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;
}
--
2.51.0
next prev parent reply other threads:[~2025-11-05 11:20 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-05 11:19 [PATCH net v2 0/2] net: bridge: fix two MST bugs Nikolay Aleksandrov
2025-11-05 11:19 ` Nikolay Aleksandrov [this message]
2025-11-05 16:59 ` [PATCH net v2 1/2] net: bridge: fix use-after-free due to MST port state bypass Ido Schimmel
2025-11-05 11:19 ` [PATCH net v2 2/2] net: bridge: fix MST static key usage Nikolay Aleksandrov
2025-11-05 17:04 ` Ido Schimmel
2025-11-06 15:40 ` [PATCH net v2 0/2] net: bridge: fix two MST bugs patchwork-bot+netdevbpf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251105111919.1499702-2-razor@blackwall.org \
--to=razor@blackwall.org \
--cc=bridge@lists.linux.dev \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=petrm@nvidia.com \
--cc=syzbot+dd280197f0f7ab3917be@syzkaller.appspotmail.com \
--cc=tobias@waldekranz.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).