]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tcp: fix icsk_ack.ato bitfield overflow
authorJiayuan Chen <jiayuan.chen@linux.dev>
Fri, 7 Aug 2026 01:44:36 +0000 (09:44 +0800)
committerJakub Kicinski <kuba@kernel.org>
Mon, 10 Aug 2026 23:02:30 +0000 (16:02 -0700)
On cross-region connections we observed delayed ACKs suddenly turning
into immediate ACKs plus a TCP_MAX_QUICKACKS burst, as if the
connection had just received its first data segment.

Commit 95b9a87c6a6b ("tcp: record last received ipv6 flowlabel")
squeezed icsk_ack.ato into 8 bits, sized for TCP_DELACK_MAX. But both
writers still bound ato by icsk_rto, which can be well above 255
jiffies, so the bitfield assignment silently wraps mod 256: repeated
delack timer misses double ato up to icsk_rto, storing 320 as 64 and
256 as 0, and ato == 0 is the "first data packet" sentinel in
tcp_event_data_recv().

Clamp both writers to TCP_DELACK_MAX, which the static_assert already
guarantees to fit and tcp_send_delayed_ack() effectively caps ato at
anyway.

Fixes: 95b9a87c6a6b ("tcp: record last received ipv6 flowlabel")
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Reviewed-by: Neal Cardwell <ncardwell@google.com>
Link: https://patch.msgid.link/20260807014437.36687-1-jiayuan.chen@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/tcp_input.c
net/ipv4/tcp_timer.c

index 5b6378b94701ef8f5ff7c5303931f7e5046cad4e..0f60a1dbf927468741f6f163e8d1b07687452c39 100644 (file)
@@ -1039,9 +1039,9 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
                        /* The fastest case is the first. */
                        icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2;
                } else if (m < icsk->icsk_ack.ato) {
-                       icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m;
-                       if (icsk->icsk_ack.ato > icsk->icsk_rto)
-                               icsk->icsk_ack.ato = icsk->icsk_rto;
+                       icsk->icsk_ack.ato = min3((icsk->icsk_ack.ato >> 1) + (u32)m,
+                                                 icsk->icsk_rto,
+                                                 (u32)TCP_DELACK_MAX);
                } else if (m > icsk->icsk_rto) {
                        /* Too long gap. Apparently sender failed to
                         * restart window, so that we send ACKs quickly.
index bf171b5e1eb3064e9320b0c9d0b069e419553604..4df1c9745c5d5741822d4902fe4e53d82fd71c9c 100644 (file)
@@ -334,7 +334,9 @@ void tcp_delack_timer_handler(struct sock *sk)
        if (inet_csk_ack_scheduled(sk)) {
                if (!inet_csk_in_pingpong_mode(sk)) {
                        /* Delayed ACK missed: inflate ATO. */
-                       icsk->icsk_ack.ato = min_t(u32, icsk->icsk_ack.ato << 1, icsk->icsk_rto);
+                       icsk->icsk_ack.ato = min3((u32)icsk->icsk_ack.ato << 1,
+                                                 icsk->icsk_rto,
+                                                 (u32)TCP_DELACK_MAX);
                } else {
                        /* Delayed ACK missed: leave pingpong mode and
                         * deflate ATO.