From: Jiayuan Chen <jiayuan.chen@linux.dev>
To: netdev@vger.kernel.org
Cc: Jiayuan Chen <jiayuan.chen@linux.dev>,
stable@vger.kernel.org, "David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>, Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
Neal Cardwell <ncardwell@google.com>,
Kuniyuki Iwashima <kuniyu@google.com>,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH net v2 1/2] tcp: fix shift-out-of-bounds in SYN-ACK retransmit timers
Date: Mon, 26 Oct 2026 20:32:38 +0800 [thread overview]
Message-ID: <20261026123244.239766-1-jiayuan.chen@linux.dev> (raw)
req->num_timeout counts the SYN-ACK timeouts of a request and is used as
a shift amount:
/* tcp_reqsk_timeout() */
u64 timeout = (u64)req->timeout << req->num_timeout;
/* tcp_fastopen_synack_timer() */
req->timeout << req->num_timeout
Once it climbs past the width of the shifted type the shift is UB. Seen
with tcp_synack_retries=255 and a peer that never completes the
handshake:
UBSAN: shift-out-of-bounds in ./include/net/tcp.h:917:34
shift exponent 64 is too large for 64-bit type 'long long unsigned int'
Call Trace:
<TASK>
dump_stack_lvl+0xc1/0xf0
dump_stack+0x10/0x20
__ubsan_handle_shift_out_of_bounds+0x1d1/0x390
reqsk_timer_handler.cold+0x1c/0x5d
call_timer_fn+0x17d/0x510
__run_timers+0x662/0xab0
run_timer_softirq+0xdf/0x260
handle_softirqs+0x204/0x9b0
__irq_exit_rcu+0x13c/0x220
irq_exit_rcu+0xe/0x20
sysvec_apic_timer_interrupt+0x40/0xd0
asm_sysvec_apic_timer_interrupt+0x1b/0x20
num_timeout is a 7-bit field. Once a retransmit limit is above 127 the
"num_timeout >= limit" check can never be true, the request is never
expired by that limit, and after the field wraps to 0
reqsk_timer_handler() decrements queue->young again on the next expiry.
- split tcp_reqsk_timeout() into tcp_reqsk_timeout_sk() and bound the
shift there; a num_timeout well under 30 already saturates the
tcp_rto_max() clamp, so nothing reachable changes for the u64 path.
- cap max_retries / max_syn_ack_retries at 127, the largest value the
field can hold, instead of narrowing the sysctl; 128..255 are still
accepted and act as 127.
- cap the TCP_DEFER_ACCEPT conversion in secs_to_retrans() at 127 too;
larger values kept an acked request from ever expiring. getsockopt()
now reports at most 14527 instead of 29887 seconds.
- route the TFO path through the same helper; its u32 shift could also
wrap to 0 before reaching UB.
- document that tcp_synack_retries values above 127 act as 127.
Fixes: e6c022a4fa2d ("tcp: better retrans tracking for defer-accept")
Cc: stable@vger.kernel.org
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
v1 -> v2: reworded the commit message, no code change.
v1: https://lore.kernel.org/netdev/20261023064915.401112-1-jiayuan.chen@linux.dev/
---
Documentation/networking/ip-sysctl.rst | 4 +++-
include/net/tcp.h | 16 ++++++++++++----
net/ipv4/inet_connection_sock.c | 2 ++
net/ipv4/tcp.c | 2 +-
net/ipv4/tcp_timer.c | 3 ++-
5 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index 4b19c8d3efda..eac33072be2d 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -956,7 +956,9 @@ tcp_stdurg - BOOLEAN
tcp_synack_retries - INTEGER
Number of times SYNACKs for a passive TCP connection attempt will
- be retransmitted. Should not be higher than 255. Default value
+ be retransmitted. The sysctl accepts 0-255, but values above 127 are
+ silently capped by the SYN-ACK timers to match the 7-bit
+ request_sock::num_timeout field. Default value
is 5, which corresponds to 31seconds till the last retransmission
with the current initial RTO of 1second. With this the final timeout
for a passive TCP connection will happen after 63seconds.
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 0265c27a5fd7..986b7ea05e4a 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -183,6 +183,8 @@ static_assert((1 << ATO_BITS) > TCP_DELACK_MAX);
#define MAX_TCP_KEEPINTVL 32767
#define MAX_TCP_KEEPCNT 127
#define MAX_TCP_SYNCNT 127
+/* request_sock::num_timeout is a 7-bit field. */
+#define MAX_TCP_SYNACK_RETRIES 127
/* Ensure that TCP PAWS checks are relaxed after ~2147 seconds
* to avoid overflows. This assumes a clock smaller than 1 Mhz.
@@ -912,12 +914,18 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
}
-static inline unsigned long tcp_reqsk_timeout(struct request_sock *req)
+static inline unsigned long tcp_reqsk_timeout_sk(const struct sock *sk,
+ const struct request_sock *req)
{
- u64 timeout = (u64)req->timeout << req->num_timeout;
+ u64 timeout = (u64)READ_ONCE(req->timeout) <<
+ min_t(u8, req->num_timeout, 30);
+
+ return (unsigned long)min_t(u64, timeout, tcp_rto_max(sk));
+}
- return (unsigned long)min_t(u64, timeout,
- tcp_rto_max(req->rsk_listener));
+static inline unsigned long tcp_reqsk_timeout(struct request_sock *req)
+{
+ return tcp_reqsk_timeout_sk(req->rsk_listener, req);
}
u32 tcp_delack_max(const struct sock *sk);
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 8151c638a24c..ee7b64f3141c 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -1072,6 +1072,8 @@ static void reqsk_timer_handler(struct timer_list *t)
net = sock_net(sk_listener);
max_syn_ack_retries = READ_ONCE(icsk->icsk_syn_retries) ? :
READ_ONCE(net->ipv4.sysctl_tcp_synack_retries);
+ max_syn_ack_retries = min_t(int, max_syn_ack_retries,
+ MAX_TCP_SYNACK_RETRIES);
/* Normally all the openreqs are young and become mature
* (i.e. converted to established socket) for first timeout.
* If synack was not acknowledged for 1 second, it means
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 99d7237a623d..a309fd45ad42 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -358,7 +358,7 @@ static u8 secs_to_retrans(int seconds, int timeout, int rto_max)
int period = timeout;
res = 1;
- while (seconds > period && res < 255) {
+ while (seconds > period && res < MAX_TCP_SYNACK_RETRIES) {
res++;
timeout <<= 1;
if (timeout > rto_max)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index e56eae4bc341..f7b100334a89 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -469,6 +469,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
*/
max_retries = READ_ONCE(icsk->icsk_syn_retries) ? :
READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_synack_retries) + 1;
+ max_retries = min_t(int, max_retries, MAX_TCP_SYNACK_RETRIES);
if (req->num_timeout >= max_retries) {
tcp_write_err(sk);
@@ -490,7 +491,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
if (!tp->retrans_stamp)
tp->retrans_stamp = tcp_time_stamp_ts(tp);
tcp_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
- req->timeout << req->num_timeout, false);
+ tcp_reqsk_timeout_sk(sk, req), false);
}
static bool tcp_rtx_probe0_timed_out(const struct sock *sk,
--
2.43.0
next reply other threads:[~2026-10-26 12:32 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 12:32 Jiayuan Chen [this message]
2026-10-26 12:32 ` [PATCH net v2 2/2] tcp: fix shift-out-of-bounds in inet_csk_rto_backoff() Jiayuan Chen
2026-10-26 13:36 ` Neal Cardwell
2026-10-26 13:27 ` [PATCH net v2 1/2] tcp: fix shift-out-of-bounds in SYN-ACK retransmit timers Neal Cardwell
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=20261026123244.239766-1-jiayuan.chen@linux.dev \
--to=jiayuan.chen@linux.dev \
--cc=corbet@lwn.net \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
--cc=stable@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.