ACK: [SRU][J/N][PATCH 1/1] bridge: mcast: Fix use-after-free during router port configuration
Aaron Ma
aaron.ma at canonical.com
Thu Jan 8 08:16:51 UTC 2026
Acked-by: Aaron Ma <aaron.ma at canonical.com>
On Wed, Jan 7, 2026 at 6:45 AM Tim Whisonant
<tim.whisonant at canonical.com> wrote:
>
> From: Ido Schimmel <idosch at nvidia.com>
>
> The bridge maintains a global list of ports behind which a multicast
> router resides. The list is consulted during forwarding to ensure
> multicast packets are forwarded to these ports even if the ports are not
> member in the matching MDB entry.
>
> When per-VLAN multicast snooping is enabled, the per-port multicast
> context is disabled on each port and the port is removed from the global
> router port list:
>
> # ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1
> # ip link add name dummy1 up master br1 type dummy
> # ip link set dev dummy1 type bridge_slave mcast_router 2
> $ bridge -d mdb show | grep router
> router ports on br1: dummy1
> # ip link set dev br1 type bridge mcast_vlan_snooping 1
> $ bridge -d mdb show | grep router
>
> However, the port can be re-added to the global list even when per-VLAN
> multicast snooping is enabled:
>
> # ip link set dev dummy1 type bridge_slave mcast_router 0
> # ip link set dev dummy1 type bridge_slave mcast_router 2
> $ bridge -d mdb show | grep router
> router ports on br1: dummy1
>
> Since commit 4b30ae9adb04 ("net: bridge: mcast: re-implement
> br_multicast_{enable, disable}_port functions"), when per-VLAN multicast
> snooping is enabled, multicast disablement on a port will disable the
> per-{port, VLAN} multicast contexts and not the per-port one. As a
> result, a port will remain in the global router port list even after it
> is deleted. This will lead to a use-after-free [1] when the list is
> traversed (when adding a new port to the list, for example):
>
> # ip link del dev dummy1
> # ip link add name dummy2 up master br1 type dummy
> # ip link set dev dummy2 type bridge_slave mcast_router 2
>
> Similarly, stale entries can also be found in the per-VLAN router port
> list. When per-VLAN multicast snooping is disabled, the per-{port, VLAN}
> contexts are disabled on each port and the port is removed from the
> per-VLAN router port list:
>
> # ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 mcast_vlan_snooping 1
> # ip link add name dummy1 up master br1 type dummy
> # bridge vlan add vid 2 dev dummy1
> # bridge vlan global set vid 2 dev br1 mcast_snooping 1
> # bridge vlan set vid 2 dev dummy1 mcast_router 2
> $ bridge vlan global show dev br1 vid 2 | grep router
> router ports: dummy1
> # ip link set dev br1 type bridge mcast_vlan_snooping 0
> $ bridge vlan global show dev br1 vid 2 | grep router
>
> However, the port can be re-added to the per-VLAN list even when
> per-VLAN multicast snooping is disabled:
>
> # bridge vlan set vid 2 dev dummy1 mcast_router 0
> # bridge vlan set vid 2 dev dummy1 mcast_router 2
> $ bridge vlan global show dev br1 vid 2 | grep router
> router ports: dummy1
>
> When the VLAN is deleted from the port, the per-{port, VLAN} multicast
> context will not be disabled since multicast snooping is not enabled
> on the VLAN. As a result, the port will remain in the per-VLAN router
> port list even after it is no longer member in the VLAN. This will lead
> to a use-after-free [2] when the list is traversed (when adding a new
> port to the list, for example):
>
> # ip link add name dummy2 up master br1 type dummy
> # bridge vlan add vid 2 dev dummy2
> # bridge vlan del vid 2 dev dummy1
> # bridge vlan set vid 2 dev dummy2 mcast_router 2
>
> Fix these issues by removing the port from the relevant (global or
> per-VLAN) router port list in br_multicast_port_ctx_deinit(). The
> function is invoked during port deletion with the per-port multicast
> context and during VLAN deletion with the per-{port, VLAN} multicast
> context.
>
> Note that deleting the multicast router timer is not enough as it only
> takes care of the temporary multicast router states (1 or 3) and not the
> permanent one (2).
>
> [1]
> BUG: KASAN: slab-out-of-bounds in br_multicast_add_router.part.0+0x3f1/0x560
> Write of size 8 at addr ffff888004a67328 by task ip/384
> [...]
> Call Trace:
> <TASK>
> dump_stack_lvl+0x6f/0xa0
> print_address_description.constprop.0+0x6f/0x350
> print_report+0x108/0x205
> kasan_report+0xdf/0x110
> br_multicast_add_router.part.0+0x3f1/0x560
> br_multicast_set_port_router+0x74e/0xac0
> br_setport+0xa55/0x1870
> br_port_slave_changelink+0x95/0x120
> __rtnl_newlink+0x5e8/0xa40
> rtnl_newlink+0x627/0xb00
> rtnetlink_rcv_msg+0x6fb/0xb70
> netlink_rcv_skb+0x11f/0x350
> netlink_unicast+0x426/0x710
> netlink_sendmsg+0x75a/0xc20
> __sock_sendmsg+0xc1/0x150
> ____sys_sendmsg+0x5aa/0x7b0
> ___sys_sendmsg+0xfc/0x180
> __sys_sendmsg+0x124/0x1c0
> do_syscall_64+0xbb/0x360
> entry_SYSCALL_64_after_hwframe+0x4b/0x53
>
> [2]
> BUG: KASAN: slab-use-after-free in br_multicast_add_router.part.0+0x378/0x560
> Read of size 8 at addr ffff888009f00840 by task bridge/391
> [...]
> Call Trace:
> <TASK>
> dump_stack_lvl+0x6f/0xa0
> print_address_description.constprop.0+0x6f/0x350
> print_report+0x108/0x205
> kasan_report+0xdf/0x110
> br_multicast_add_router.part.0+0x378/0x560
> br_multicast_set_port_router+0x6f9/0xac0
> br_vlan_process_options+0x8b6/0x1430
> br_vlan_rtm_process_one+0x605/0xa30
> br_vlan_rtm_process+0x396/0x4c0
> rtnetlink_rcv_msg+0x2f7/0xb70
> netlink_rcv_skb+0x11f/0x350
> netlink_unicast+0x426/0x710
> netlink_sendmsg+0x75a/0xc20
> __sock_sendmsg+0xc1/0x150
> ____sys_sendmsg+0x5aa/0x7b0
> ___sys_sendmsg+0xfc/0x180
> __sys_sendmsg+0x124/0x1c0
> do_syscall_64+0xbb/0x360
> entry_SYSCALL_64_after_hwframe+0x4b/0x53
>
> Fixes: 2796d846d74a ("net: bridge: vlan: convert mcast router global option to per-vlan entry")
> Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions")
> Reported-by: syzbot+7bfa4b72c6a5da128d32 at syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/all/684c18bd.a00a0220.279073.000b.GAE@google.com/T/
> Signed-off-by: Ido Schimmel <idosch at nvidia.com>
> Link: https://patch.msgid.link/20250619182228.1656906-1-idosch@nvidia.com
> Signed-off-by: Jakub Kicinski <kuba at kernel.org>
> (backported from commit 7544f3f5b0b58c396f374d060898b5939da31709)
> [tswhison: context adjusted due to missing commit
> 8fa7292fee5 ("treewide: Switch/rename to timer_delete[_sync]()")]
> CVE-2025-38248
> Signed-off-by: Tim Whisonant <tim.whisonant at canonical.com>
> ---
> net/bridge/br_multicast.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index 6f59e50585956..8eff44042b5da 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -2014,10 +2014,19 @@ void br_multicast_port_ctx_init(struct net_bridge_port *port,
>
> void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx)
> {
> + struct net_bridge *br = pmctx->port->br;
> + bool del = false;
> +
> #if IS_ENABLED(CONFIG_IPV6)
> del_timer_sync(&pmctx->ip6_mc_router_timer);
> #endif
> del_timer_sync(&pmctx->ip4_mc_router_timer);
> +
> + spin_lock_bh(&br->multicast_lock);
> + del |= br_ip6_multicast_rport_del(pmctx);
> + del |= br_ip4_multicast_rport_del(pmctx);
> + br_multicast_rport_del_notify(pmctx, del);
> + spin_unlock_bh(&br->multicast_lock);
> }
>
> int br_multicast_add_port(struct net_bridge_port *port)
> --
> 2.43.0
>
>
> --
> kernel-team mailing list
> kernel-team at lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
More information about the kernel-team
mailing list