Commit 9b182491fc for openssl.org

commit 9b182491fc47042fa3fa5c246b5b24482cd49efd
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date:   Fri Jun 26 15:04:27 2026 +0200

    quic: fix intermittent idle-test failure in tserver test

    The thread-assisted idle test advances fake time in 10ms steps while the
    connection is kept alive solely by the background assist thread sending
    keepalive PINGs. The test stepped fake time without checking that a due
    keepalive had actually been sent, so whether it went out before the
    server's idle deadline lapsed depended on thread scheduling - hence the
    intermittent failure.

    Now, before each step, check the event timeout (next_deadline minus
    fake-now): while a keepalive is still due to be sent it stays at zero, so we
    wake the assist thread and re-check without advancing until it goes positive
    (or the existing real-time watchdog fires). Only then do we step fake time.

    The negotiated 30s idle timeout and 60s idle duration are unchanged, so the
    keepalive is still required and still tested; only the race is removed.

    Assisted-by: Claude:claude-opus-4-8

    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    MergeDate: Fri Jul 10 15:33:45 2026
    (Merged from https://github.com/openssl/openssl/pull/31746)

diff --git a/test/quic_tserver_test.c b/test/quic_tserver_test.c
index 0ef79035d2..b4e81f427f 100644
--- a/test/quic_tserver_test.c
+++ b/test/quic_tserver_test.c
@@ -331,16 +331,38 @@ static int do_test(int use_thread_assist, int use_fake_time, int use_inject)
                 CRYPTO_THREAD_unlock(fake_time_lock);

                 ++idle_units_done;
-                ossl_quic_conn_force_assist_thread_wake(c_ssl);

                 /*
-                 * If the event timeout has expired then give the assistance
-                 * thread a chance to catch up
+                 * The assist thread alone keeps the idle connection alive. It
+                 * waits on real time internally, so advancing fake time can
+                 * outrun it. Rather than race it, wait until it has caught up:
+                 * the event timeout is computed against fake time, so once the
+                 * next deadline is back in the future all events due up to now
+                 * - including any keepalive - have been serviced.
                  */
-                if (!TEST_true(SSL_get_event_timeout(c_ssl, &tv, &isinf)))
-                    goto err;
-                if (!isinf && ossl_time_compare(ossl_time_zero(), ossl_time_from_timeval(tv)) >= 0)
-                    OSSL_sleep(10); /* Ensure CPU scheduling for test purposes */
+                for (;;) {
+                    ossl_quic_conn_force_assist_thread_wake(c_ssl);
+
+                    if (!TEST_true(SSL_get_event_timeout(c_ssl, &tv, &isinf)))
+                        goto err;
+
+                    if (isinf
+                        || ossl_time_compare(ossl_time_from_timeval(tv),
+                               ossl_time_zero())
+                            > 0)
+                        break;
+
+                    if (ossl_time_compare(ossl_time_subtract(real_now(NULL),
+                                              start_time),
+                            ossl_ms2time(limit_ms))
+                        >= 0) {
+                        TEST_error("timeout waiting for assist thread to send "
+                                   "keepalive during idle test");
+                        goto err;
+                    }
+
+                    OSSL_sleep(1); /* Yield so the assist thread can run. */
+                }
             } else {
                 c_done_idle_test = 1;
             }