Re: [PATCH net] tcp: make probe0 timer handle expired user timeout
From: Eric Dumazet
Date: Wed Apr 22 2026 - 03:47:06 EST
On Tue, Apr 21, 2026 at 8:31 PM Altan Hacigumus <ahacigu.linux@xxxxxxxxx> wrote:
>
> On Mon, Apr 20, 2026 at 9:58 PM Eric Dumazet <edumazet@xxxxxxxxxx> wrote:
> >
> > On Mon, Apr 13, 2026 at 6:36 PM Altan Hacigumus <ahacigu.linux@xxxxxxxxx> wrote:
> > >
> > > tcp_clamp_probe0_to_user_timeout() computes remaining time in jiffies
> > > using subtraction with an unsigned lvalue. If elapsed probing time
> > > already exceeds the configured TCP_USER_TIMEOUT, the subtraction
> > > underflows and yields a large value.
> > >
> > > Handle this expiration case similarly to tcp_clamp_rto_to_user_timeout().
> > >
> > > Fixes: 344db93ae3ee ("tcp: make TCP_USER_TIMEOUT accurate for zero window probes")
> > > Signed-off-by: Altan Hacigumus <ahacigu.linux@xxxxxxxxx>
> > > ---
> > > net/ipv4/tcp_timer.c | 5 ++++-
> > > 1 file changed, 4 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
> > > index 5a14a53a3c9e..4a43356a4e06 100644
> > > --- a/net/ipv4/tcp_timer.c
> > > +++ b/net/ipv4/tcp_timer.c
> > > @@ -50,7 +50,8 @@ static u32 tcp_clamp_rto_to_user_timeout(const struct sock *sk)
> > > u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when)
> > > {
> > > const struct inet_connection_sock *icsk = inet_csk(sk);
> > > - u32 remaining, user_timeout;
> > > + u32 user_timeout;
> > > + s32 remaining;
> > > s32 elapsed;
> > >
> > > user_timeout = READ_ONCE(icsk->icsk_user_timeout);
> > > @@ -61,6 +62,8 @@ u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when)
> > > if (unlikely(elapsed < 0))
> > > elapsed = 0;
> > > remaining = msecs_to_jiffies(user_timeout) - elapsed;
> > > + if (remaining <= 0)
> > > + return 1;
> >
> > I do not think this chunk is needed ?
> > If @remaining is signed, then perhaps change the following line to:
> >
> > remaining = max_t(int, remaining, TCP_TIMEOUT_MIN);
> >
>
> The if (remaining <= 0) return 1 handles the already-expired case and
> mirrors the logic in tcp_clamp_rto_to_user_timeout().
tcp_clamp_rto_to_user_timeout() has two conditionals.
if (remaining <= 0)
return 1;
return min_t(u32, icsk->icsk_rto, msecs_to_jiffies(remaining));
First one was to avoid to call msecs_to_jiffies() with a negative number.
tcp_clamp_probe0_to_user_timeout() has 3 tests already, you want to
add a fourth one...
I would prefer something less obscure.
We do not care if we return 1 or 2 (TCP_TIMEOUT_MIN) jiffies
>
> max_t(s32, remaining, TCP_TIMEOUT_MIN) with remaining changed to s32
> also fixes the underflow; I used the explicit early return for symmetry
> with the RTO helper, but I can switch that in v2 if you prefer.
>
> > Also, it would be great to have a new packetdrill test.
> >
>
> With packetdrill, AFAICS it would require a late ACK to be processed
> after TCP_USER_TIMEOUT has elapsed but before tcp_probe_timer() runs the
> abort check - i.e. a race between the RX softirq path and the timer.
> This is not purely an event/packet ordering problem, so it is not clear
> to me how a deterministic behavior can be simulated with packetdrill
> without tying it to exact probe timing.
I was trying to sense if the bug was serious or not, considering last
tcp_clamp_probe0_to_user_timeout()
statement is:
return min_t(u32, remaining, when);
Perhaps you should give more details in the changelog. What were the
symptoms of this bug, for TCP_USER_TIMEOUT users.