]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
SSL_poll: fix abort_blocking mishandling in poll_translate()/poll_block()
authorMatt Caswell <matt@openssl.foundation>
Thu, 25 Jun 2026 16:15:16 +0000 (17:15 +0100)
committerMatt Caswell <matt@openssl.foundation>
Wed, 29 Jul 2026 07:19:47 +0000 (08:19 +0100)
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)

ssl/rio/poll_immediate.c

index 24b82f3a6a43843eab38e9547c00ce8073416e13..edb39b2a6c16e620c41bc996a8864aafd27c365f 100644 (file)
@@ -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);