Commit d6ca7796bf for openssl.org

commit d6ca7796bf66f65d0c69cb0c0806ed38ce3f3105
Author: Matt Caswell <matt@openssl.foundation>
Date:   Thu Jun 25 17:15:16 2026 +0100

    SSL_poll: fix abort_blocking mishandling in poll_translate()/poll_block()

    When SSL_poll() has to block, poll_translate() registers each item's QUIC
    connection for cross-thread notification one item at a time. If an item
    turns out to already be ready right as it is being registered, translation
    sets abort_blocking and is meant to bail out so the caller retries the
    readout instead of actually blocking.

    Two bugs in that abort path:

    - poll_translate() returned immediately on abort_blocking without calling
      postpoll_translation_cleanup() for any earlier items that had already
      had their blocking section entered. Those items' enter/leave calls were
      left unbalanced, leaking into the QUIC reactor's blocking-waiter count.
      Fixed by routing through the existing "out" cleanup label, mirroring
      what the FAIL_ITEM()/error path already does for items 0..i-1.

    - poll_block() initializes ok = 0 and only ever sets it on the actual
      poll() success path. The abort_blocking branch jumped straight to "out"
      without setting ok, so SSL_poll() reported failure even though nothing
      actually went wrong; the caller's retry loop never got a chance to pick
      up the now-ready item.

    Assisted-by: Claude:claude-sonnet-4-6

    Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
    Reviewed-by: Paul Yang <paulyang.inf@gmail.com>
    MergeDate: Wed Jul 29 07:20:31 2026
    (Merged from https://github.com/openssl/openssl/pull/31743)

diff --git a/ssl/rio/poll_immediate.c b/ssl/rio/poll_immediate.c
index 24b82f3a6a..edb39b2a6c 100644
--- a/ssl/rio/poll_immediate.c
+++ b/ssl/rio/poll_immediate.c
@@ -233,7 +233,7 @@ static int poll_translate(SSL_POLL_ITEM *items,
                     FAIL_ITEM(i);

                 if (*abort_blocking)
-                    return 1;
+                    goto out;

                 if (!SSL_get_event_timeout(ssl, &timeout, &is_infinite))
                     FAIL_ITEM(i++); /* need to clean up this item too */
@@ -271,7 +271,12 @@ static int poll_translate(SSL_POLL_ITEM *items,
     }

 out:
-    if (!ok)
+    /*
+     * On abort_blocking, the item which triggered the abort has already
+     * balanced its own enter/leave of the blocking section (see
+     * poll_translate_ssl_quic()); only items 0..i-1 still need cleanup here.
+     */
+    if (!ok || *abort_blocking)
         postpoll_translation_cleanup(items, i, stride, wctx);

     *p_earliest_wakeup_deadline = earliest_wakeup_deadline;
@@ -320,8 +325,15 @@ static int poll_block(SSL_POLL_ITEM *items,
             p_result_count))
         goto out;

-    if (abort_blocking)
+    if (abort_blocking) {
+        /*
+         * Nothing actually failed; we just shouldn't block because an item
+         * may have become ready while we were setting up. The caller's
+         * retry loop will call poll_readout() again to pick this up.
+         */
+        ok = 1;
         goto out;
+    }

     earliest_wakeup_deadline = ossl_time_min(earliest_wakeup_deadline,
         user_deadline);