Commit 0670df4cbb for openssl.org

commit 0670df4cbb543f5b23a4ecb6ff05fbbb5adb8e5f
Author: Matt Caswell <matt@openssl.foundation>
Date:   Mon Aug 10 08:59:06 2026 +0100

    Wait consistently across platforms when there is nothing to poll

    poll_translate_ssl_dtls_conn() adds no file descriptor to the poll set when
    the connection's BIO cannot supply one, which is the case for the memory and
    datagram-pair BIOs the test suite is built on. Unlike the QUIC and DTLS
    listener paths it does not report that as an error, and rightly so: a DTLS
    connection with no pollable BIO has no readiness to wait for, but it still
    has a retransmission deadline to wake for, so a wait with nothing to watch is
    meaningful.

    The resulting empty descriptor set was then handed to the operating system,
    where the behaviour differs. poll() treats it as a plain sleep, so this
    worked on Unix. Windows' select() rejects a call with no descriptors, so
    ossl_rio_poll_builder_poll() returned failure and SSL_poll() failed with it.

    Sleep out the deadline explicitly rather than relying on the platform. With
    no descriptors and no deadline nothing could ever provide a wakeup, so that
    combination is reported as a failure instead of sleeping for ever.

    This has no test of its own: on any platform whose poll() already sleeps, the
    behaviour before and after is identical, so only the Windows CI builds
    distinguish them.

    Assisted-by: Claude Code:claude-opus-5
    Reviewed-by: Ryan Hooper <ryanh@openssl.foundation>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Merge-date: Fri Aug 14 15:45:41 2026
    Merged-from: https://github.com/openssl/openssl/pull/32239

diff --git a/ssl/rio/poll_builder.c b/ssl/rio/poll_builder.c
index 8088b17ad7..61ded3f931 100644
--- a/ssl/rio/poll_builder.c
+++ b/ssl/rio/poll_builder.c
@@ -137,10 +137,40 @@ int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd,
 #endif
 }

+/* Returns 1 if no file descriptors have been added to the poll builder. */
+static int rio_poll_builder_is_empty(const RIO_POLL_BUILDER *rpb)
+{
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+    return rpb->hwm_fd < 0;
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+    return rpb->pfd_num == 0;
+#else
+    return 1;
+#endif
+}
+
 int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline)
 {
     int rc;

+    /*
+     * Waiting with no file descriptors is legitimate: a DTLS connection whose
+     * BIO cannot provide a poll descriptor has no readiness to wait for, but
+     * still has a retransmission deadline to wake for. Handle that here rather
+     * than leaving it to the OS, because poll() treats an empty descriptor set
+     * as a plain sleep whereas Windows' select() rejects it outright.
+     *
+     * With no descriptors and no deadline nothing could ever wake us, so that
+     * is a caller error rather than an indefinite sleep.
+     */
+    if (rio_poll_builder_is_empty(rpb)) {
+        if (ossl_time_is_infinite(deadline))
+            return 0;
+
+        OSSL_sleep(ossl_time2ms(ossl_time_subtract(deadline, ossl_time_now())));
+        return 1;
+    }
+
 #if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
     do {
         struct timeval timeout, *p_timeout = &timeout;