Re: [PATCH net] tcp: make probe0 timer handle expired user timeout
From: Altan Hacigumus
Date: Wed Apr 22 2026 - 23:02:27 EST
On Wed, Apr 22, 2026 at 12:39 AM Eric Dumazet <edumazet@xxxxxxxxxx> wrote:
>
> 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
>
okay
> >
> > 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.
Yes, it at least clamps to @when, but still caused connection teardowns
to be intermittently delayed beyond the user's explicit timeout sockopt.
Will send a v2 accordingly.
Thanks.