* [PATCH net 0/5] net: fix misc signed overflows caused by bad sysctl values
@ 2026-10-23 9:13 Jiayuan Chen
2026-10-23 9:13 ` [PATCH net 1/5] ipv6: restrict gc_elasticity to 31 Jiayuan Chen
` (4 more replies)
0 siblings, 5 replies; 14+ messages in thread
From: Jiayuan Chen @ 2026-10-23 9:13 UTC (permalink / raw)
To: netdev
Cc: Jiayuan Chen, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel,
Kuniyuki Iwashima, Nikolay Aleksandrov, Joel Granados,
Chia-Yu Chang, Yung Chih Su, Wyatt Feng,
Fernando Fernandez Mancera, Flavio Leitner, Hannes Frederic Sowa,
Petr Mladek, Paul E. McKenney, linux-kernel
Recently I have reviewed quite a few fixes for variable overflows caused
by config values, i.e. a value set from sysctl, module params or netlink
that is accepted as valid but later overflows when it is used in a
calculation. I believe there are more of these still out there, so I
used an LLM to dig through the code and it found the issues fixed in
this series.
All of them have been reproduced and verified in QEMU.
Jiayuan Chen (5):
ipv6: restrict gc_elasticity to 31
ipv4: fix shift-out-of-bounds in ip_rt_send_redirect()
ipv6: restrict mld_qrv to 255
ipv4: restrict igmp_qrv to 255
net: restrict message_cost and message_burst to non-negative values
net/core/sysctl_net_core.c | 21 +++++++++++++++++++--
net/ipv4/route.c | 7 ++++---
net/ipv4/sysctl_net_ipv4.c | 4 +++-
net/ipv6/route.c | 6 +++++-
net/ipv6/sysctl_net_ipv6.c | 4 +++-
5 files changed, 34 insertions(+), 8 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH net 1/5] ipv6: restrict gc_elasticity to 31 2026-10-23 9:13 [PATCH net 0/5] net: fix misc signed overflows caused by bad sysctl values Jiayuan Chen @ 2026-10-23 9:13 ` Jiayuan Chen 2026-10-23 13:31 ` Fernando Fernandez Mancera 2026-10-23 9:13 ` [PATCH net 2/5] ipv4: fix shift-out-of-bounds in ip_rt_send_redirect() Jiayuan Chen ` (3 subsequent siblings) 4 siblings, 1 reply; 14+ messages in thread From: Jiayuan Chen @ 2026-10-23 9:13 UTC (permalink / raw) To: netdev Cc: Jiayuan Chen, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Nikolay Aleksandrov, Joel Granados, Chia-Yu Chang, Wyatt Feng, Fernando Fernandez Mancera, Yung Chih Su, Flavio Leitner, Hannes Frederic Sowa, Paul E. McKenney, Petr Mladek, linux-kernel Every counted IPv6 dst allocation runs ip6_dst_gc() once the number of live dst goes over gc_thresh (1024 by default). ip6_dst_gc() decays ip6_rt_gc_expire by gc_elasticity: the smaller gc_elasticity is, the smaller ip6_rt_gc_expire gets, i.e. the more aggressive the gc. ip6_rt_gc_expire is a u32, but gc_elasticity has no upper bound, so writing >= 32 shifts a 32-bit value by >= 32 bits, which is UB: val = atomic_read(&net->ipv6.ip6_rt_gc_expire); atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity)); UBSAN: shift-out-of-bounds in net/ipv6/route.c:3369:53 shift exponent 32 is too large for 32-bit type 'unsigned int' CPU: 2 UID: 0 PID: 615 Comm: ping6 Not tainted 7.3.0-rc7+ #364 PREEMPT Call Trace: <IRQ> dump_stack_lvl+0xc1/0xf0 dump_stack+0x10/0x20 __ubsan_handle_shift_out_of_bounds+0x1d1/0x390 ip6_dst_gc.cold+0x15/0x1a dst_alloc+0x4e0/0x620 ip6_rt_cache_alloc+0x189/0x7e0 __ip6_rt_update_pmtu+0x68e/0x1160 ip6_update_pmtu+0x30c/0x3a0 icmpv6_err+0x1fa/0x330 icmpv6_notify+0x303/0x770 icmpv6_rcv+0xc12/0x1670 ip6_protocol_deliver_rcu+0xb4a/0x1520 ip6_input_finish+0x19c/0x400 ip6_input+0xd4/0x240 ipv6_rcv+0x1fd/0x330 __netif_receive_skb_one_core+0x118/0x1c0 __netif_receive_skb+0x21/0x150 process_backlog+0x37e/0x1200 __napi_poll.constprop.0+0xa5/0x5e0 net_rx_action+0x4c0/0xf70 handle_softirqs+0x204/0x9b0 __do_softirq+0x10/0x18 Cap it at 31. 31 and 32 are equivalent anyway: ip6_rt_gc_expire starts at 30*HZ (~30000 with HZ=1000), and such a value shifted right by 15 or more is already 0, so val - (val >> rt_elasticity) stops decaying well before 31. Values above 31 add no new behaviour, they only hit the UB. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> --- net/ipv6/route.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 94e5ef1c4f83..6f2efc219f39 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -6579,6 +6579,8 @@ static int ipv6_sysctl_rtcache_flush(const struct ctl_table *ctl, int write, return 0; } +static int ip6_rt_gc_elasticity_max = 31; + static const struct ctl_table ipv6_route_table_template[] = { { .procname = "max_size", @@ -6627,7 +6629,9 @@ static const struct ctl_table ipv6_route_table_template[] = { .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = &ip6_rt_gc_elasticity_max, }, { .procname = "mtu_expires", -- 2.43.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 1/5] ipv6: restrict gc_elasticity to 31 2026-10-23 9:13 ` [PATCH net 1/5] ipv6: restrict gc_elasticity to 31 Jiayuan Chen @ 2026-10-23 13:31 ` Fernando Fernandez Mancera 0 siblings, 0 replies; 14+ messages in thread From: Fernando Fernandez Mancera @ 2026-10-23 13:31 UTC (permalink / raw) To: Jiayuan Chen, netdev Cc: stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Nikolay Aleksandrov, Joel Granados, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Flavio Leitner, Hannes Frederic Sowa, Paul E. McKenney, Petr Mladek, linux-kernel On 10/23/26 11:13 AM, Jiayuan Chen wrote: > Every counted IPv6 dst allocation runs ip6_dst_gc() once the number of > live dst goes over gc_thresh (1024 by default). ip6_dst_gc() decays > ip6_rt_gc_expire by gc_elasticity: the smaller gc_elasticity is, the > smaller ip6_rt_gc_expire gets, i.e. the more aggressive the gc. > > ip6_rt_gc_expire is a u32, but gc_elasticity has no upper bound, so > writing >= 32 shifts a 32-bit value by >= 32 bits, which is UB: > > val = atomic_read(&net->ipv6.ip6_rt_gc_expire); > atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity)); > > UBSAN: shift-out-of-bounds in net/ipv6/route.c:3369:53 > shift exponent 32 is too large for 32-bit type 'unsigned int' > CPU: 2 UID: 0 PID: 615 Comm: ping6 Not tainted 7.3.0-rc7+ #364 PREEMPT > Call Trace: > <IRQ> > dump_stack_lvl+0xc1/0xf0 > dump_stack+0x10/0x20 > __ubsan_handle_shift_out_of_bounds+0x1d1/0x390 > ip6_dst_gc.cold+0x15/0x1a > dst_alloc+0x4e0/0x620 > ip6_rt_cache_alloc+0x189/0x7e0 > __ip6_rt_update_pmtu+0x68e/0x1160 > ip6_update_pmtu+0x30c/0x3a0 > icmpv6_err+0x1fa/0x330 > icmpv6_notify+0x303/0x770 > icmpv6_rcv+0xc12/0x1670 > ip6_protocol_deliver_rcu+0xb4a/0x1520 > ip6_input_finish+0x19c/0x400 > ip6_input+0xd4/0x240 > ipv6_rcv+0x1fd/0x330 > __netif_receive_skb_one_core+0x118/0x1c0 > __netif_receive_skb+0x21/0x150 > process_backlog+0x37e/0x1200 > __napi_poll.constprop.0+0xa5/0x5e0 > net_rx_action+0x4c0/0xf70 > handle_softirqs+0x204/0x9b0 > __do_softirq+0x10/0x18 > > Cap it at 31. 31 and 32 are equivalent anyway: ip6_rt_gc_expire starts > at 30*HZ (~30000 with HZ=1000), and such a value shifted right by 15 or > more is already 0, so val - (val >> rt_elasticity) stops decaying well > before 31. Values above 31 add no new behaviour, they only hit the UB. > > Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") > Cc: stable@vger.kernel.org > Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de> Thanks! ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH net 2/5] ipv4: fix shift-out-of-bounds in ip_rt_send_redirect() 2026-10-23 9:13 [PATCH net 0/5] net: fix misc signed overflows caused by bad sysctl values Jiayuan Chen 2026-10-23 9:13 ` [PATCH net 1/5] ipv6: restrict gc_elasticity to 31 Jiayuan Chen @ 2026-10-23 9:13 ` Jiayuan Chen 2026-10-23 14:11 ` Fernando Fernandez Mancera 2026-10-23 9:13 ` [PATCH net 3/5] ipv6: restrict mld_qrv to 255 Jiayuan Chen ` (2 subsequent siblings) 4 siblings, 1 reply; 14+ messages in thread From: Jiayuan Chen @ 2026-10-23 9:13 UTC (permalink / raw) To: netdev Cc: Jiayuan Chen, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Joel Granados, Chia-Yu Chang, Fernando Fernandez Mancera, Wyatt Feng, Yung Chih Su, Hannes Frederic Sowa, Flavio Leitner, Paul E. McKenney, Petr Mladek, linux-kernel ip_rt_redirect_number is the max number of ICMP Redirects we send to one endpoint; once we hit it we stop. Before that, ip_rt_redirect_load gives the redirects an exponential backoff: if (peer->n_redirects == 0 || time_after(jiffies, (peer->rate_last + (ip_rt_redirect_load << peer->n_redirects)))) { n_redirects only stops growing when it reaches ip_rt_redirect_number, and both redirect_number and redirect_load are plain proc_dointvec sysctls with no upper bound. Set redirect_number past 32 and n_redirects climbs past 31, so "ip_rt_redirect_load << peer->n_redirects" shifts a 32-bit int by >= 32 bits, which is UB: UBSAN: shift-out-of-bounds in net/ipv4/route.c:949:6 shift exponent 32 is too large for 32-bit type 'int' CPU: 1 UID: 0 PID: 1845 Comm: ping Not tainted 7.3.0-rc7+ #364 PREEMPT Call Trace: <IRQ> dump_stack_lvl+0xc1/0xf0 dump_stack+0x10/0x20 __ubsan_handle_shift_out_of_bounds+0x1d1/0x390 ip_rt_send_redirect.cold+0x2c/0x146 ip_forward+0x1ddc/0x2430 ip_rcv+0x2b1/0x320 __netif_receive_skb_one_core+0x16b/0x1c0 __netif_receive_skb+0x21/0x150 process_backlog+0x37e/0x1200 __napi_poll.constprop.0+0xa5/0x5e0 net_rx_action+0x4c0/0xf70 Fixing this from the sysctl side would mean bounding both knobs, e.g. redirect_load to [0, 65535] and redirect_number to [0, 16], which gets in the way of users who legitimately do not want to cap ICMP redirects. Instead compute the backoff in u64 with the shift count capped at 31, which cannot overflow since ip_rt_redirect_load is an int, and clamp the result to MAX_JIFFY_OFFSET. The clamp also fixes 32-bit kernels, where the same expression goes wrong long before the shift count reaches 32. With the default ip_rt_redirect_load of 20, from n_redirects == 27 on the result is either truncated or too large for time_after(), which then treats the deadline as already passed, and from 30 on it is 0, so every forwarded packet gets a redirect again until ip_rt_redirect_number is reached. This needs a raised redirect_silence to be reachable, otherwise n_redirects is reset long before. On 64-bit, ip_rt_redirect_load << 31 stays below MAX_JIFFY_OFFSET, so nothing changes there. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> --- net/ipv4/route.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 595a3b7f1783..76807b757046 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -907,6 +907,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) struct inet_peer *peer; int log_martians; struct net *net; + u64 backoff; int vif; rcu_read_lock(); @@ -945,10 +946,10 @@ void ip_rt_send_redirect(struct sk_buff *skb) /* Check for load limit; set rate_last to the latest sent * redirect. */ + backoff = (u64)ip_rt_redirect_load << min(peer->n_redirects, 31U); + backoff = min_t(u64, backoff, MAX_JIFFY_OFFSET); if (peer->n_redirects == 0 || - time_after(jiffies, - (peer->rate_last + - (ip_rt_redirect_load << peer->n_redirects)))) { + time_after(jiffies, peer->rate_last + (unsigned long)backoff)) { __be32 gw = rt_nexthop(rt, ip_hdr(skb)->daddr); icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, gw); -- 2.43.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 2/5] ipv4: fix shift-out-of-bounds in ip_rt_send_redirect() 2026-10-23 9:13 ` [PATCH net 2/5] ipv4: fix shift-out-of-bounds in ip_rt_send_redirect() Jiayuan Chen @ 2026-10-23 14:11 ` Fernando Fernandez Mancera 2026-10-23 14:38 ` Jiayuan Chen 0 siblings, 1 reply; 14+ messages in thread From: Fernando Fernandez Mancera @ 2026-10-23 14:11 UTC (permalink / raw) To: Jiayuan Chen, netdev Cc: stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Joel Granados, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Hannes Frederic Sowa, Flavio Leitner, Paul E. McKenney, Petr Mladek, linux-kernel On 10/23/26 11:13 AM, Jiayuan Chen wrote: > ip_rt_redirect_number is the max number of ICMP Redirects we send to one > endpoint; once we hit it we stop. Before that, ip_rt_redirect_load gives > the redirects an exponential backoff: > > if (peer->n_redirects == 0 || > time_after(jiffies, > (peer->rate_last + > (ip_rt_redirect_load << peer->n_redirects)))) { > > n_redirects only stops growing when it reaches ip_rt_redirect_number, and > both redirect_number and redirect_load are plain proc_dointvec sysctls > with no upper bound. Set redirect_number past 32 and n_redirects climbs > past 31, so "ip_rt_redirect_load << peer->n_redirects" shifts a 32-bit > int by >= 32 bits, which is UB: > > UBSAN: shift-out-of-bounds in net/ipv4/route.c:949:6 > shift exponent 32 is too large for 32-bit type 'int' > CPU: 1 UID: 0 PID: 1845 Comm: ping Not tainted 7.3.0-rc7+ #364 PREEMPT > Call Trace: > <IRQ> > dump_stack_lvl+0xc1/0xf0 > dump_stack+0x10/0x20 > __ubsan_handle_shift_out_of_bounds+0x1d1/0x390 > ip_rt_send_redirect.cold+0x2c/0x146 > ip_forward+0x1ddc/0x2430 > ip_rcv+0x2b1/0x320 > __netif_receive_skb_one_core+0x16b/0x1c0 > __netif_receive_skb+0x21/0x150 > process_backlog+0x37e/0x1200 > __napi_poll.constprop.0+0xa5/0x5e0 > net_rx_action+0x4c0/0xf70 > > Fixing this from the sysctl side would mean bounding both knobs, e.g. > redirect_load to [0, 65535] and redirect_number to [0, 16], which gets > in the way of users who legitimately do not want to cap ICMP redirects. > Instead compute the backoff in u64 with the shift count capped at 31, > which cannot overflow since ip_rt_redirect_load is an int, and clamp the > result to MAX_JIFFY_OFFSET. > I think we should cap redirect_load with a minimum valid value of 0 on the sysctl. If the user want to disable ICMP redirects or do not cap them, they should use 0 or -1 in redirect_number IMHO. What do you think? > The clamp also fixes 32-bit kernels, where the same expression goes > wrong long before the shift count reaches 32. With the default > ip_rt_redirect_load of 20, from n_redirects == 27 on the result is > either truncated or too large for time_after(), which then treats the > deadline as already passed, and from 30 on it is 0, so every forwarded > packet gets a redirect again until ip_rt_redirect_number is reached. > This needs a raised redirect_silence to be reachable, otherwise > n_redirects is reset long before. On 64-bit, ip_rt_redirect_load << 31 > stays below MAX_JIFFY_OFFSET, so nothing changes there. > Isn't the same issue in 64-bit kernels too? In essence, it would be (int << u32) which results in a negative value and that is later sign-extended to a corrupted 64-bit value. Am I missing something here? > Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") > Cc: stable@vger.kernel.org > Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> > --- > net/ipv4/route.c | 7 ++++--- > 1 file changed, 4 insertions(+), 3 deletions(-) > > diff --git a/net/ipv4/route.c b/net/ipv4/route.c > index 595a3b7f1783..76807b757046 100644 > --- a/net/ipv4/route.c > +++ b/net/ipv4/route.c > @@ -907,6 +907,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) > struct inet_peer *peer; > int log_martians; > struct net *net; > + u64 backoff; > int vif; > > rcu_read_lock(); > @@ -945,10 +946,10 @@ void ip_rt_send_redirect(struct sk_buff *skb) > /* Check for load limit; set rate_last to the latest sent > * redirect. > */ > + backoff = (u64)ip_rt_redirect_load << min(peer->n_redirects, 31U); > + backoff = min_t(u64, backoff, MAX_JIFFY_OFFSET); > if (peer->n_redirects == 0 || > - time_after(jiffies, > - (peer->rate_last + > - (ip_rt_redirect_load << peer->n_redirects)))) { > + time_after(jiffies, peer->rate_last + (unsigned long)backoff)) { > __be32 gw = rt_nexthop(rt, ip_hdr(skb)->daddr); > > icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, gw); ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 2/5] ipv4: fix shift-out-of-bounds in ip_rt_send_redirect() 2026-10-23 14:11 ` Fernando Fernandez Mancera @ 2026-10-23 14:38 ` Jiayuan Chen 0 siblings, 0 replies; 14+ messages in thread From: Jiayuan Chen @ 2026-10-23 14:38 UTC (permalink / raw) To: Fernando Fernandez Mancera, netdev Cc: stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Joel Granados, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Hannes Frederic Sowa, Flavio Leitner, Paul E. McKenney, Petr Mladek, linux-kernel On 10/23/26 10:11 PM, Fernando Fernandez Mancera wrote: > On 10/23/26 11:13 AM, Jiayuan Chen wrote: >> ip_rt_redirect_number is the max number of ICMP Redirects we send to one >> endpoint; once we hit it we stop. Before that, ip_rt_redirect_load gives >> the redirects an exponential backoff: >> >> if (peer->n_redirects == 0 || >> time_after(jiffies, >> (peer->rate_last + >> (ip_rt_redirect_load << peer->n_redirects)))) { >> >> n_redirects only stops growing when it reaches ip_rt_redirect_number, >> and >> both redirect_number and redirect_load are plain proc_dointvec sysctls >> with no upper bound. Set redirect_number past 32 and n_redirects climbs >> past 31, so "ip_rt_redirect_load << peer->n_redirects" shifts a 32-bit >> int by >= 32 bits, which is UB: >> >> UBSAN: shift-out-of-bounds in net/ipv4/route.c:949:6 >> shift exponent 32 is too large for 32-bit type 'int' >> CPU: 1 UID: 0 PID: 1845 Comm: ping Not tainted 7.3.0-rc7+ #364 >> PREEMPT >> Call Trace: >> <IRQ> >> dump_stack_lvl+0xc1/0xf0 >> dump_stack+0x10/0x20 >> __ubsan_handle_shift_out_of_bounds+0x1d1/0x390 >> ip_rt_send_redirect.cold+0x2c/0x146 >> ip_forward+0x1ddc/0x2430 >> ip_rcv+0x2b1/0x320 >> __netif_receive_skb_one_core+0x16b/0x1c0 >> __netif_receive_skb+0x21/0x150 >> process_backlog+0x37e/0x1200 >> __napi_poll.constprop.0+0xa5/0x5e0 >> net_rx_action+0x4c0/0xf70 >> >> Fixing this from the sysctl side would mean bounding both knobs, e.g. >> redirect_load to [0, 65535] and redirect_number to [0, 16], which gets >> in the way of users who legitimately do not want to cap ICMP redirects. >> Instead compute the backoff in u64 with the shift count capped at 31, >> which cannot overflow since ip_rt_redirect_load is an int, and clamp the >> result to MAX_JIFFY_OFFSET. >> > > I think we should cap redirect_load with a minimum valid value of 0 on > the sysctl. If the user want to disable ICMP redirects or do not cap > them, they should use 0 or -1 in redirect_number IMHO. > > What do you think? > >> The clamp also fixes 32-bit kernels, where the same expression goes >> wrong long before the shift count reaches 32. With the default >> ip_rt_redirect_load of 20, from n_redirects == 27 on the result is >> either truncated or too large for time_after(), which then treats the >> deadline as already passed, and from 30 on it is 0, so every forwarded >> packet gets a redirect again until ip_rt_redirect_number is reached. >> This needs a raised redirect_silence to be reachable, otherwise >> n_redirects is reset long before. On 64-bit, ip_rt_redirect_load << 31 >> stays below MAX_JIFFY_OFFSET, so nothing changes there. >> > > Isn't the same issue in 64-bit kernels too? In essence, it would be > (int << u32) which results in a negative value and that is later > sign-extended to a corrupted 64-bit value. Am I missing something here? > Thanks for pointing this out, agreed. A negative redirect_load never meant anything: with the old int shift it just made the deadline land in the past, i.e. no backoff at all, which is exactly what 0 gives. And once converted to u64 it would turn into the maximum backoff instead, so it should be rejected rather than silently mapped to something. ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH net 3/5] ipv6: restrict mld_qrv to 255 2026-10-23 9:13 [PATCH net 0/5] net: fix misc signed overflows caused by bad sysctl values Jiayuan Chen 2026-10-23 9:13 ` [PATCH net 1/5] ipv6: restrict gc_elasticity to 31 Jiayuan Chen 2026-10-23 9:13 ` [PATCH net 2/5] ipv4: fix shift-out-of-bounds in ip_rt_send_redirect() Jiayuan Chen @ 2026-10-23 9:13 ` Jiayuan Chen 2026-10-23 14:13 ` Fernando Fernandez Mancera 2026-10-23 9:13 ` [PATCH net 4/5] ipv4: restrict igmp_qrv " Jiayuan Chen 2026-10-23 9:13 ` [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values Jiayuan Chen 4 siblings, 1 reply; 14+ messages in thread From: Jiayuan Chen @ 2026-10-23 9:13 UTC (permalink / raw) To: netdev Cc: Jiayuan Chen, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Neal Cardwell, Joel Granados, Chia-Yu Chang, Fernando Fernandez Mancera, Wyatt Feng, Yung Chih Su, Flavio Leitner, Hannes Frederic Sowa, Paul E. McKenney, Petr Mladek, linux-kernel net.ipv6.mld_qrv only has a lower bound, but ipv6_mc_reset() stores it into the u8 inet6_dev::mc_qrv, and every MLD retransmit counter derived from it (mca_crcount, sf_crcount, mc_ifc_count, mc_dad_count) is u8 as well. Anything above 255 silently wraps: 256 becomes 0, so no state change report is ever retransmitted, and the first MLDv2 Query received on the interface then trips the WARN_ON() in mld_update_qrv(): WARN_ON(idev->mc_qrv == 0); A plain bridge with mcast_querier enabled on the link is enough: WARNING: net/ipv6/mcast.c:1327 at mld_query_work+0x1fd2/0x2cb0, CPU#3: kworker/3:1/2688 Workqueue: mld mld_query_work RIP: 0010:mld_query_work+0x1fd2/0x2cb0 Call Trace: <TASK> process_one_work+0x93a/0x1b20 worker_thread+0x6d0/0x11d0 kthread+0x358/0x470 ret_from_fork+0x5b3/0x820 ret_from_fork_asm+0x1a/0x30 </TASK> Cap the sysctl at U8_MAX, the largest value mc_qrv can actually hold. Fixes: 2f711939d2ea ("ipv6: add sysctl_mld_qrv to configure query robustness variable") Cc: stable@vger.kernel.org Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> --- net/ipv6/sysctl_net_ipv6.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 1a0a36dcdabc..05a384680332 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -29,6 +29,7 @@ static u32 rt6_multipath_hash_fields_all_mask = FIB_MULTIPATH_HASH_FIELD_ALL_MASK; static u32 ioam6_id_max = IOAM6_DEFAULT_ID; static u64 ioam6_id_wide_max = IOAM6_DEFAULT_ID_WIDE; +static int mld_qrv_max = U8_MAX; static int proc_rt6_multipath_hash_policy(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) @@ -229,7 +230,8 @@ static struct ctl_table ipv6_rotable[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = SYSCTL_ONE + .extra1 = SYSCTL_ONE, + .extra2 = &mld_qrv_max, }, #ifdef CONFIG_NETLABEL { -- 2.43.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 3/5] ipv6: restrict mld_qrv to 255 2026-10-23 9:13 ` [PATCH net 3/5] ipv6: restrict mld_qrv to 255 Jiayuan Chen @ 2026-10-23 14:13 ` Fernando Fernandez Mancera 0 siblings, 0 replies; 14+ messages in thread From: Fernando Fernandez Mancera @ 2026-10-23 14:13 UTC (permalink / raw) To: Jiayuan Chen, netdev Cc: stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Neal Cardwell, Joel Granados, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Flavio Leitner, Hannes Frederic Sowa, Paul E. McKenney, Petr Mladek, linux-kernel On 10/23/26 11:13 AM, Jiayuan Chen wrote: > net.ipv6.mld_qrv only has a lower bound, but ipv6_mc_reset() stores it > into the u8 inet6_dev::mc_qrv, and every MLD retransmit counter derived > from it (mca_crcount, sf_crcount, mc_ifc_count, mc_dad_count) is u8 as > well. Anything above 255 silently wraps: 256 becomes 0, so no state > change report is ever retransmitted, and the first MLDv2 Query received > on the interface then trips the WARN_ON() in mld_update_qrv(): > > WARN_ON(idev->mc_qrv == 0); > > A plain bridge with mcast_querier enabled on the link is enough: > > WARNING: net/ipv6/mcast.c:1327 at mld_query_work+0x1fd2/0x2cb0, CPU#3: kworker/3:1/2688 > Workqueue: mld mld_query_work > RIP: 0010:mld_query_work+0x1fd2/0x2cb0 > Call Trace: > <TASK> > process_one_work+0x93a/0x1b20 > worker_thread+0x6d0/0x11d0 > kthread+0x358/0x470 > ret_from_fork+0x5b3/0x820 > ret_from_fork_asm+0x1a/0x30 > </TASK> > > Cap the sysctl at U8_MAX, the largest value mc_qrv can actually hold. > > Fixes: 2f711939d2ea ("ipv6: add sysctl_mld_qrv to configure query robustness variable") > Cc: stable@vger.kernel.org > Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de> Thanks! ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH net 4/5] ipv4: restrict igmp_qrv to 255 2026-10-23 9:13 [PATCH net 0/5] net: fix misc signed overflows caused by bad sysctl values Jiayuan Chen ` (2 preceding siblings ...) 2026-10-23 9:13 ` [PATCH net 3/5] ipv6: restrict mld_qrv to 255 Jiayuan Chen @ 2026-10-23 9:13 ` Jiayuan Chen 2026-10-23 14:14 ` Fernando Fernandez Mancera 2026-10-23 9:13 ` [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values Jiayuan Chen 4 siblings, 1 reply; 14+ messages in thread From: Jiayuan Chen @ 2026-10-23 9:13 UTC (permalink / raw) To: netdev Cc: Jiayuan Chen, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Joel Granados, Chia-Yu Chang, Wyatt Feng, Fernando Fernandez Mancera, Yung Chih Su, Hannes Frederic Sowa, Flavio Leitner, Petr Mladek, Paul E. McKenney, linux-kernel net.ipv4.igmp_qrv only has a lower bound, but ip_mc_reset() stores it into the u8 in_device::mr_qrv, and the IGMPv3 retransmit counters filled from it (crcount, sf_crcount) are u8 as well. Anything above 255 silently wraps. With 256, mr_qrv becomes 0, every "in_dev->mr_qrv ?: sysctl_igmp_qrv" falls back to 256, which truncates to 0 again in crcount, and igmpv3_send_cr() never puts the state change records for the group into a report. The older version querier present timeout (mr_qrv * mr_qi + mr_qri) also collapses to just mr_qri. Unlike MLD there is no WARN_ON() here, the reports just go missing. Cap the sysctl at U8_MAX, the largest value mr_qrv can actually hold. Fixes: a9fe8e29945d ("ipv4: implement igmp_qrv sysctl to tune igmp robustness variable") Cc: stable@vger.kernel.org Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> --- net/ipv4/sysctl_net_ipv4.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index e3760daa3470..ef631a2759b0 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -33,6 +33,7 @@ static int ip_privileged_port_min; static int ip_privileged_port_max = 65535; static int ip_ttl_min = 1; static int ip_ttl_max = 255; +static int igmp_qrv_max __maybe_unused = U8_MAX; static int tcp_syn_retries_min = 1; static int tcp_syn_retries_max = MAX_TCP_SYNCNT; static int tcp_syn_linear_timeouts_max = MAX_TCP_SYNCNT; @@ -978,7 +979,8 @@ static const struct ctl_table ipv4_net_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = SYSCTL_ONE + .extra1 = SYSCTL_ONE, + .extra2 = &igmp_qrv_max, }, #endif { -- 2.43.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 4/5] ipv4: restrict igmp_qrv to 255 2026-10-23 9:13 ` [PATCH net 4/5] ipv4: restrict igmp_qrv " Jiayuan Chen @ 2026-10-23 14:14 ` Fernando Fernandez Mancera 0 siblings, 0 replies; 14+ messages in thread From: Fernando Fernandez Mancera @ 2026-10-23 14:14 UTC (permalink / raw) To: Jiayuan Chen, netdev Cc: stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Joel Granados, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Hannes Frederic Sowa, Flavio Leitner, Petr Mladek, Paul E. McKenney, linux-kernel On 10/23/26 11:13 AM, Jiayuan Chen wrote: > net.ipv4.igmp_qrv only has a lower bound, but ip_mc_reset() stores it > into the u8 in_device::mr_qrv, and the IGMPv3 retransmit counters filled > from it (crcount, sf_crcount) are u8 as well. Anything above 255 > silently wraps. With 256, mr_qrv becomes 0, every > "in_dev->mr_qrv ?: sysctl_igmp_qrv" falls back to 256, which truncates > to 0 again in crcount, and igmpv3_send_cr() never puts the state change > records for the group into a report. The older version querier present > timeout (mr_qrv * mr_qi + mr_qri) also collapses to just mr_qri. > > Unlike MLD there is no WARN_ON() here, the reports just go missing. > > Cap the sysctl at U8_MAX, the largest value mr_qrv can actually hold. > > Fixes: a9fe8e29945d ("ipv4: implement igmp_qrv sysctl to tune igmp robustness variable") > Cc: stable@vger.kernel.org > Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de> Thanks! ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values 2026-10-23 9:13 [PATCH net 0/5] net: fix misc signed overflows caused by bad sysctl values Jiayuan Chen ` (3 preceding siblings ...) 2026-10-23 9:13 ` [PATCH net 4/5] ipv4: restrict igmp_qrv " Jiayuan Chen @ 2026-10-23 9:13 ` Jiayuan Chen 2026-10-23 12:19 ` Joel Granados 4 siblings, 1 reply; 14+ messages in thread From: Jiayuan Chen @ 2026-10-23 9:13 UTC (permalink / raw) To: netdev Cc: Jiayuan Chen, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Joel Granados, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Fernando Fernandez Mancera, Hannes Frederic Sowa, Flavio Leitner, Petr Mladek, Paul E. McKenney, linux-kernel net.core.message_cost and net.core.message_burst feed the interval and burst of net_ratelimit_state, but neither has a lower bound: message_burst uses plain proc_dointvec, and message_cost uses proc_dointvec_jiffies, which happily stores -1 second as -HZ jiffies. Since commit a940d145cc38 ("ratelimit: Warn if ->interval or ->burst are negative"), ___ratelimit() splats on such values, so the next net_ratelimit() caller warns, e.g. a martian source log: Negative interval (-250) or burst (10): Uninitialized ratelimit_state structure? WARNING: lib/ratelimit.c:43 at ___ratelimit+0x56e/0x960, CPU#1: ping/3218 RIP: 0010:___ratelimit+0x578/0x960 Call Trace: <IRQ> net_ratelimit+0x1c/0x30 ip_handle_martian_source+0x1bb/0x340 ip_route_input_slow+0x1057/0x3650 ip_route_input_noref+0x104/0x260 arp_process+0x109b/0x2510 arp_rcv+0x381/0x460 __netif_receive_skb_one_core+0x173/0x1c0 __netif_receive_skb+0x21/0x150 process_backlog+0x37e/0x1200 __napi_poll.constprop.0+0xa5/0x5e0 net_rx_action+0x4c0/0xf70 handle_softirqs+0x204/0x9b0 __do_softirq+0x10/0x18 do_softirq.part.0+0x49/0xd0 </IRQ> Negative values never meant anything here: ___ratelimit() treats them like 0, and 0 already expresses both "no limit" (interval) and "suppress everything" (burst). Reject them at write time. message_burst can simply use proc_dointvec_minmax with extra1 = 0. proc_dointvec_jiffies ignores extra1/extra2, so give message_cost a small handler that parses into a temporary and only stores the value if it is not negative. Fixes: a940d145cc38 ("ratelimit: Warn if ->interval or ->burst are negative") Cc: stable@vger.kernel.org Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> --- net/core/sysctl_net_core.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 473c162736d6..8146ed819695 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -378,6 +378,22 @@ static int proc_do_skb_defer_max(const struct ctl_table *table, int write, return ret; } +static int proc_do_message_cost(const struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + int ret, interval = READ_ONCE(*(int *)table->data); + struct ctl_table tmp = *table; + + tmp.data = &interval; + ret = proc_dointvec_jiffies(&tmp, write, buffer, lenp, ppos); + if (write && !ret) { + if (interval < 0) + return -EINVAL; + WRITE_ONCE(*(int *)table->data, interval); + } + return ret; +} + #ifdef CONFIG_BPF_JIT static int proc_dointvec_minmax_bpf_enable(const struct ctl_table *table, int write, void *buffer, size_t *lenp, @@ -544,14 +560,15 @@ static struct ctl_table net_core_table[] = { .data = &net_ratelimit_state.interval, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_jiffies, + .proc_handler = proc_do_message_cost, }, { .procname = "message_burst", .data = &net_ratelimit_state.burst, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, }, #ifdef CONFIG_RPS { -- 2.43.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values 2026-10-23 9:13 ` [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values Jiayuan Chen @ 2026-10-23 12:19 ` Joel Granados 2026-10-23 12:55 ` Fernando Fernandez Mancera 0 siblings, 1 reply; 14+ messages in thread From: Joel Granados @ 2026-10-23 12:19 UTC (permalink / raw) To: Jiayuan Chen Cc: netdev, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Fernando Fernandez Mancera, Hannes Frederic Sowa, Flavio Leitner, Petr Mladek, Paul E. McKenney, linux-kernel [-- Attachment #1: Type: text/plain, Size: 6798 bytes --] On Fri, Oct 23, 2026 at 05:13:31PM +0800, Jiayuan Chen wrote: > net.core.message_cost and net.core.message_burst feed the interval and > burst of net_ratelimit_state, but neither has a lower bound: > message_burst uses plain proc_dointvec, and message_cost uses > proc_dointvec_jiffies, which happily stores -1 second as -HZ jiffies. > > Since commit a940d145cc38 ("ratelimit: Warn if ->interval or ->burst are > negative"), ___ratelimit() splats on such values, so the next > net_ratelimit() caller warns, e.g. a martian source log: > > Negative interval (-250) or burst (10): Uninitialized ratelimit_state structure? > WARNING: lib/ratelimit.c:43 at ___ratelimit+0x56e/0x960, CPU#1: ping/3218 > RIP: 0010:___ratelimit+0x578/0x960 > Call Trace: > <IRQ> > net_ratelimit+0x1c/0x30 > ip_handle_martian_source+0x1bb/0x340 > ip_route_input_slow+0x1057/0x3650 > ip_route_input_noref+0x104/0x260 > arp_process+0x109b/0x2510 > arp_rcv+0x381/0x460 > __netif_receive_skb_one_core+0x173/0x1c0 > __netif_receive_skb+0x21/0x150 > process_backlog+0x37e/0x1200 > __napi_poll.constprop.0+0xa5/0x5e0 > net_rx_action+0x4c0/0xf70 > handle_softirqs+0x204/0x9b0 > __do_softirq+0x10/0x18 > do_softirq.part.0+0x49/0xd0 > </IRQ> > > Negative values never meant anything here: ___ratelimit() treats them > like 0, and 0 already expresses both "no limit" (interval) and > "suppress everything" (burst). > > Reject them at write time. message_burst can simply use > proc_dointvec_minmax with extra1 = 0. proc_dointvec_jiffies ignores > extra1/extra2, so give message_cost a small handler that parses into a > temporary and only stores the value if it is not negative. > > Fixes: a940d145cc38 ("ratelimit: Warn if ->interval or ->burst are negative") > Cc: stable@vger.kernel.org > Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> > --- > net/core/sysctl_net_core.c | 21 +++++++++++++++++++-- > 1 file changed, 19 insertions(+), 2 deletions(-) > > diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c > index 473c162736d6..8146ed819695 100644 > --- a/net/core/sysctl_net_core.c > +++ b/net/core/sysctl_net_core.c > @@ -378,6 +378,22 @@ static int proc_do_skb_defer_max(const struct ctl_table *table, int write, > return ret; > } > > +static int proc_do_message_cost(const struct ctl_table *table, int write, > + void *buffer, size_t *lenp, loff_t *ppos) > +{ > + int ret, interval = READ_ONCE(*(int *)table->data); > + struct ctl_table tmp = *table; > + > + tmp.data = &interval; > + ret = proc_dointvec_jiffies(&tmp, write, buffer, lenp, ppos); > + if (write && !ret) { > + if (interval < 0) > + return -EINVAL; > + WRITE_ONCE(*(int *)table->data, interval); > + } > + return ret; > +} > + > #ifdef CONFIG_BPF_JIT > static int proc_dointvec_minmax_bpf_enable(const struct ctl_table *table, int write, > void *buffer, size_t *lenp, > @@ -544,14 +560,15 @@ static struct ctl_table net_core_table[] = { > .data = &net_ratelimit_state.interval, > .maxlen = sizeof(int), > .mode = 0644, > - .proc_handler = proc_dointvec_jiffies, > + .proc_handler = proc_do_message_cost, > }, > { > .procname = "message_burst", > .data = &net_ratelimit_state.burst, > .maxlen = sizeof(int), > .mode = 0644, > - .proc_handler = proc_dointvec, > + .proc_handler = proc_dointvec_minmax, > + .extra1 = SYSCTL_ZERO, > }, > #ifdef CONFIG_RPS > { > -- > 2.43.0 > I would prefer something like this [1] (not tested and not compiled). This is more in line with the patterns inside the jiffies.c file. I put SYSCTL_ZERO there, but I have a doubt if you actually need SYSCTL_ONE. If the interval variable can be zero, then leave it as SYSCTL_ZERO, but if it cant be zero and can be one the use SYSCTL_ONE best [1] diff --git i/include/linux/jiffies.h w/include/linux/jiffies.h index bbd57061802c..288ba81ed53e 100644 --- i/include/linux/jiffies.h +++ w/include/linux/jiffies.h @@ -646,6 +646,8 @@ extern unsigned long nsecs_to_jiffies(u64 n); struct ctl_table; int proc_dointvec_jiffies(const struct ctl_table *table, int dir, void *buffer, size_t *lenp, loff_t *ppos); +int proc_dointvec_jiffies_minmax(const struct ctl_table *table, int dir, void *buffer, + size_t *lenp, loff_t *ppos); int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir, void *buffer, size_t *lenp, loff_t *ppos); int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir, diff --git i/kernel/time/jiffies.c w/kernel/time/jiffies.c index 9b3487d40cd6..cf91996fe4e3 100644 --- i/kernel/time/jiffies.c +++ w/kernel/time/jiffies.c @@ -165,6 +165,14 @@ static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz); } +static int do_proc_int_conv_jiffies_minmax(bool *negp, ulong *u_ptr, int *k_ptr, + int dir, const struct ctl_table *tbl) +{ + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true, + sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz); +} + + static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, int dir, const struct ctl_table *tbl) @@ -263,6 +271,15 @@ int proc_dointvec_jiffies(const struct ctl_table *table, int dir, } EXPORT_SYMBOL(proc_dointvec_jiffies); +int proc_dointvec_jiffies_minmax(const struct ctl_table *table, int dir, + void *buffer, size_t *lenp, loff_t *ppos) +{ + return proc_dointvec_conv(table, dir, buffer, lenp, ppos, + do_proc_int_conv_jiffies_minmax); +} +EXPORT_SYMBOL(proc_dointvec_jiffies); + + /** * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds * @table: the sysctl table diff --git i/net/core/sysctl_net_core.c w/net/core/sysctl_net_core.c index eb35da3556f4..7226697bf7c1 100644 --- i/net/core/sysctl_net_core.c +++ w/net/core/sysctl_net_core.c @@ -538,7 +538,8 @@ static struct ctl_table net_core_table[] = { .data = &net_ratelimit_state.interval, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_jiffies, + .proc_handler = proc_dointvec_jiffies_minmax, + .extra1 = SYSCTL_ZERO, }, { .procname = "message_burst", [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 659 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values 2026-10-23 12:19 ` Joel Granados @ 2026-10-23 12:55 ` Fernando Fernandez Mancera 2026-10-23 14:30 ` Jiayuan Chen 0 siblings, 1 reply; 14+ messages in thread From: Fernando Fernandez Mancera @ 2026-10-23 12:55 UTC (permalink / raw) To: Joel Granados, Jiayuan Chen Cc: netdev, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Hannes Frederic Sowa, Flavio Leitner, Petr Mladek, Paul E. McKenney, linux-kernel On 10/23/26 2:19 PM, Joel Granados wrote: > On Fri, Oct 23, 2026 at 05:13:31PM +0800, Jiayuan Chen wrote: >> net.core.message_cost and net.core.message_burst feed the interval and >> burst of net_ratelimit_state, but neither has a lower bound: >> message_burst uses plain proc_dointvec, and message_cost uses >> proc_dointvec_jiffies, which happily stores -1 second as -HZ jiffies. >> >> Since commit a940d145cc38 ("ratelimit: Warn if ->interval or ->burst are >> negative"), ___ratelimit() splats on such values, so the next >> net_ratelimit() caller warns, e.g. a martian source log: >> >> Negative interval (-250) or burst (10): Uninitialized ratelimit_state structure? >> WARNING: lib/ratelimit.c:43 at ___ratelimit+0x56e/0x960, CPU#1: ping/3218 >> RIP: 0010:___ratelimit+0x578/0x960 >> Call Trace: >> <IRQ> >> net_ratelimit+0x1c/0x30 >> ip_handle_martian_source+0x1bb/0x340 >> ip_route_input_slow+0x1057/0x3650 >> ip_route_input_noref+0x104/0x260 >> arp_process+0x109b/0x2510 >> arp_rcv+0x381/0x460 >> __netif_receive_skb_one_core+0x173/0x1c0 >> __netif_receive_skb+0x21/0x150 >> process_backlog+0x37e/0x1200 >> __napi_poll.constprop.0+0xa5/0x5e0 >> net_rx_action+0x4c0/0xf70 >> handle_softirqs+0x204/0x9b0 >> __do_softirq+0x10/0x18 >> do_softirq.part.0+0x49/0xd0 >> </IRQ> >> >> Negative values never meant anything here: ___ratelimit() treats them >> like 0, and 0 already expresses both "no limit" (interval) and >> "suppress everything" (burst). >> >> Reject them at write time. message_burst can simply use >> proc_dointvec_minmax with extra1 = 0. proc_dointvec_jiffies ignores >> extra1/extra2, so give message_cost a small handler that parses into a >> temporary and only stores the value if it is not negative. >> >> Fixes: a940d145cc38 ("ratelimit: Warn if ->interval or ->burst are negative") >> Cc: stable@vger.kernel.org >> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> >> --- >> net/core/sysctl_net_core.c | 21 +++++++++++++++++++-- >> 1 file changed, 19 insertions(+), 2 deletions(-) >> >> diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c >> index 473c162736d6..8146ed819695 100644 >> --- a/net/core/sysctl_net_core.c >> +++ b/net/core/sysctl_net_core.c >> @@ -378,6 +378,22 @@ static int proc_do_skb_defer_max(const struct ctl_table *table, int write, >> return ret; >> } >> >> +static int proc_do_message_cost(const struct ctl_table *table, int write, >> + void *buffer, size_t *lenp, loff_t *ppos) >> +{ >> + int ret, interval = READ_ONCE(*(int *)table->data); >> + struct ctl_table tmp = *table; >> + >> + tmp.data = &interval; >> + ret = proc_dointvec_jiffies(&tmp, write, buffer, lenp, ppos); >> + if (write && !ret) { >> + if (interval < 0) >> + return -EINVAL; >> + WRITE_ONCE(*(int *)table->data, interval); >> + } >> + return ret; >> +} >> + >> #ifdef CONFIG_BPF_JIT >> static int proc_dointvec_minmax_bpf_enable(const struct ctl_table *table, int write, >> void *buffer, size_t *lenp, >> @@ -544,14 +560,15 @@ static struct ctl_table net_core_table[] = { >> .data = &net_ratelimit_state.interval, >> .maxlen = sizeof(int), >> .mode = 0644, >> - .proc_handler = proc_dointvec_jiffies, >> + .proc_handler = proc_do_message_cost, >> }, >> { >> .procname = "message_burst", >> .data = &net_ratelimit_state.burst, >> .maxlen = sizeof(int), >> .mode = 0644, >> - .proc_handler = proc_dointvec, >> + .proc_handler = proc_dointvec_minmax, >> + .extra1 = SYSCTL_ZERO, >> }, >> #ifdef CONFIG_RPS >> { >> -- >> 2.43.0 >> > > I would prefer something like this [1] (not tested and not compiled). > This is more in line with the patterns inside the jiffies.c file. I put > SYSCTL_ZERO there, but I have a doubt if you actually need SYSCTL_ONE. > > If the interval variable can be zero, then leave it as SYSCTL_ZERO, but > if it cant be zero and can be one the use SYSCTL_ONE > FWIW, I also like this solution and indeed we can convert other users in the future. About SYSCTL_ZERO, I believe it is the right choice. AFAIU, it just disables limiting. Thanks, Fernando. > best > > > [1] > > diff --git i/include/linux/jiffies.h w/include/linux/jiffies.h > index bbd57061802c..288ba81ed53e 100644 > --- i/include/linux/jiffies.h > +++ w/include/linux/jiffies.h > @@ -646,6 +646,8 @@ extern unsigned long nsecs_to_jiffies(u64 n); > struct ctl_table; > int proc_dointvec_jiffies(const struct ctl_table *table, int dir, void *buffer, > size_t *lenp, loff_t *ppos); > +int proc_dointvec_jiffies_minmax(const struct ctl_table *table, int dir, void *buffer, > + size_t *lenp, loff_t *ppos); > int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir, > void *buffer, size_t *lenp, loff_t *ppos); > int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir, > diff --git i/kernel/time/jiffies.c w/kernel/time/jiffies.c > index 9b3487d40cd6..cf91996fe4e3 100644 > --- i/kernel/time/jiffies.c > +++ w/kernel/time/jiffies.c > @@ -165,6 +165,14 @@ static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, > sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz); > } > > +static int do_proc_int_conv_jiffies_minmax(bool *negp, ulong *u_ptr, int *k_ptr, > + int dir, const struct ctl_table *tbl) > +{ > + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true, > + sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz); > +} > + > + > static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr, > int *k_ptr, int dir, > const struct ctl_table *tbl) > @@ -263,6 +271,15 @@ int proc_dointvec_jiffies(const struct ctl_table *table, int dir, > } > EXPORT_SYMBOL(proc_dointvec_jiffies); > > +int proc_dointvec_jiffies_minmax(const struct ctl_table *table, int dir, > + void *buffer, size_t *lenp, loff_t *ppos) > +{ > + return proc_dointvec_conv(table, dir, buffer, lenp, ppos, > + do_proc_int_conv_jiffies_minmax); > +} > +EXPORT_SYMBOL(proc_dointvec_jiffies); > + > + > /** > * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds > * @table: the sysctl table > diff --git i/net/core/sysctl_net_core.c w/net/core/sysctl_net_core.c > index eb35da3556f4..7226697bf7c1 100644 > --- i/net/core/sysctl_net_core.c > +++ w/net/core/sysctl_net_core.c > @@ -538,7 +538,8 @@ static struct ctl_table net_core_table[] = { > .data = &net_ratelimit_state.interval, > .maxlen = sizeof(int), > .mode = 0644, > - .proc_handler = proc_dointvec_jiffies, > + .proc_handler = proc_dointvec_jiffies_minmax, > + .extra1 = SYSCTL_ZERO, > }, > { > .procname = "message_burst", ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values 2026-10-23 12:55 ` Fernando Fernandez Mancera @ 2026-10-23 14:30 ` Jiayuan Chen 0 siblings, 0 replies; 14+ messages in thread From: Jiayuan Chen @ 2026-10-23 14:30 UTC (permalink / raw) To: Fernando Fernandez Mancera, Joel Granados Cc: netdev, stable, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, David Ahern, Ido Schimmel, Kuniyuki Iwashima, Chia-Yu Chang, Wyatt Feng, Yung Chih Su, Hannes Frederic Sowa, Flavio Leitner, Petr Mladek, Paul E. McKenney, linux-kernel On 10/23/26 8:55 PM, Fernando Fernandez Mancera wrote: [...] >>> #ifdef CONFIG_BPF_JIT >>> static int proc_dointvec_minmax_bpf_enable(const struct ctl_table >>> *table, int write, >>> void *buffer, size_t *lenp, >>> @@ -544,14 +560,15 @@ static struct ctl_table net_core_table[] = { >>> .data = &net_ratelimit_state.interval, >>> .maxlen = sizeof(int), >>> .mode = 0644, >>> - .proc_handler = proc_dointvec_jiffies, >>> + .proc_handler = proc_do_message_cost, >>> }, >>> { >>> .procname = "message_burst", >>> .data = &net_ratelimit_state.burst, >>> .maxlen = sizeof(int), >>> .mode = 0644, >>> - .proc_handler = proc_dointvec, >>> + .proc_handler = proc_dointvec_minmax, >>> + .extra1 = SYSCTL_ZERO, >>> }, >>> #ifdef CONFIG_RPS >>> { >>> -- >>> 2.43.0 >>> >> >> I would prefer something like this [1] (not tested and not compiled). >> This is more in line with the patterns inside the jiffies.c file. I put >> SYSCTL_ZERO there, but I have a doubt if you actually need SYSCTL_ONE. >> Thanks Joel and Fernando, good to hear this. I actually had almost the same proc_dointvec_jiffies_minmax() locally at first, but then thought "this touches sysctl core, better not" and went with the local handler instead. Glad I was wrong about that :) >> If the interval variable can be zero, then leave it as SYSCTL_ZERO, but >> if it cant be zero and can be one the use SYSCTL_ONE >> > > FWIW, I also like this solution and indeed we can convert other users > in the future. > > About SYSCTL_ZERO, I believe it is the right choice. AFAIU, it just > disables limiting. > Agreed on SYSCTL_ZERO. interval == 0 is the documented "never limit" case in ___ratelimit(), so it has to stay allowed. > Thanks, > Fernando. > ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-10-23 14:38 UTC | newest] Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-23 9:13 [PATCH net 0/5] net: fix misc signed overflows caused by bad sysctl values Jiayuan Chen 2026-10-23 9:13 ` [PATCH net 1/5] ipv6: restrict gc_elasticity to 31 Jiayuan Chen 2026-10-23 13:31 ` Fernando Fernandez Mancera 2026-10-23 9:13 ` [PATCH net 2/5] ipv4: fix shift-out-of-bounds in ip_rt_send_redirect() Jiayuan Chen 2026-10-23 14:11 ` Fernando Fernandez Mancera 2026-10-23 14:38 ` Jiayuan Chen 2026-10-23 9:13 ` [PATCH net 3/5] ipv6: restrict mld_qrv to 255 Jiayuan Chen 2026-10-23 14:13 ` Fernando Fernandez Mancera 2026-10-23 9:13 ` [PATCH net 4/5] ipv4: restrict igmp_qrv " Jiayuan Chen 2026-10-23 14:14 ` Fernando Fernandez Mancera 2026-10-23 9:13 ` [PATCH net 5/5] net: restrict message_cost and message_burst to non-negative values Jiayuan Chen 2026-10-23 12:19 ` Joel Granados 2026-10-23 12:55 ` Fernando Fernandez Mancera 2026-10-23 14:30 ` Jiayuan Chen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®