Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: bridge: fix soft lockup in br_multicast_query_expired()

When set multicast_query_interval to a large value, the local variable
'time' in br_multicast_send_query() may overflow. If the time is smaller
than jiffies, the timer will expire immediately, and then call mod_timer()
again, which creates a loop and may trigger the following soft lockup
issue.

watchdog: BUG: soft lockup - CPU#1 stuck for 221s! [rb_consumer:66]
CPU: 1 UID: 0 PID: 66 Comm: rb_consumer Not tainted 6.16.0+ #259 PREEMPT(none)
Call Trace:
<IRQ>
__netdev_alloc_skb+0x2e/0x3a0
br_ip6_multicast_alloc_query+0x212/0x1b70
__br_multicast_send_query+0x376/0xac0
br_multicast_send_query+0x299/0x510
br_multicast_query_expired.constprop.0+0x16d/0x1b0
call_timer_fn+0x3b/0x2a0
__run_timers+0x619/0x950
run_timer_softirq+0x11c/0x220
handle_softirqs+0x18e/0x560
__irq_exit_rcu+0x158/0x1a0
sysvec_apic_timer_interrupt+0x76/0x90
</IRQ>

This issue can be reproduced with:
ip link add br0 type bridge
echo 1 > /sys/class/net/br0/bridge/multicast_querier
echo 0xffffffffffffffff >
/sys/class/net/br0/bridge/multicast_query_interval
ip link set dev br0 up

The multicast_startup_query_interval can also cause this issue. Similar to
the commit 99b40610956a ("net: bridge: mcast: add and enforce query
interval minimum"), add check for the query interval maximum to fix this
issue.

Link: https://lore.kernel.org/netdev/20250806094941.1285944-1-wangliang74@huawei.com/
Link: https://lore.kernel.org/netdev/20250812091818.542238-1-wangliang74@huawei.com/
Fixes: d902eee43f19 ("bridge: Add multicast count/interval sysfs entries")
Suggested-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Wang Liang <wangliang74@huawei.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Link: https://patch.msgid.link/20250813021054.1643649-1-wangliang74@huawei.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Wang Liang and committed by
Jakub Kicinski
d1547bf4 fd980bf6

+18
+16
net/bridge/br_multicast.c
··· 4818 4818 intvl_jiffies = BR_MULTICAST_QUERY_INTVL_MIN; 4819 4819 } 4820 4820 4821 + if (intvl_jiffies > BR_MULTICAST_QUERY_INTVL_MAX) { 4822 + br_info(brmctx->br, 4823 + "trying to set multicast query interval above maximum, setting to %lu (%ums)\n", 4824 + jiffies_to_clock_t(BR_MULTICAST_QUERY_INTVL_MAX), 4825 + jiffies_to_msecs(BR_MULTICAST_QUERY_INTVL_MAX)); 4826 + intvl_jiffies = BR_MULTICAST_QUERY_INTVL_MAX; 4827 + } 4828 + 4821 4829 brmctx->multicast_query_interval = intvl_jiffies; 4822 4830 } 4823 4831 ··· 4840 4832 jiffies_to_clock_t(BR_MULTICAST_STARTUP_QUERY_INTVL_MIN), 4841 4833 jiffies_to_msecs(BR_MULTICAST_STARTUP_QUERY_INTVL_MIN)); 4842 4834 intvl_jiffies = BR_MULTICAST_STARTUP_QUERY_INTVL_MIN; 4835 + } 4836 + 4837 + if (intvl_jiffies > BR_MULTICAST_STARTUP_QUERY_INTVL_MAX) { 4838 + br_info(brmctx->br, 4839 + "trying to set multicast startup query interval above maximum, setting to %lu (%ums)\n", 4840 + jiffies_to_clock_t(BR_MULTICAST_STARTUP_QUERY_INTVL_MAX), 4841 + jiffies_to_msecs(BR_MULTICAST_STARTUP_QUERY_INTVL_MAX)); 4842 + intvl_jiffies = BR_MULTICAST_STARTUP_QUERY_INTVL_MAX; 4843 4843 } 4844 4844 4845 4845 brmctx->multicast_startup_query_interval = intvl_jiffies;
+2
net/bridge/br_private.h
··· 31 31 #define BR_MULTICAST_DEFAULT_HASH_MAX 4096 32 32 #define BR_MULTICAST_QUERY_INTVL_MIN msecs_to_jiffies(1000) 33 33 #define BR_MULTICAST_STARTUP_QUERY_INTVL_MIN BR_MULTICAST_QUERY_INTVL_MIN 34 + #define BR_MULTICAST_QUERY_INTVL_MAX msecs_to_jiffies(86400000) /* 24 hours */ 35 + #define BR_MULTICAST_STARTUP_QUERY_INTVL_MAX BR_MULTICAST_QUERY_INTVL_MAX 34 36 35 37 #define BR_HWDOM_MAX BITS_PER_LONG 36 38