Commit 0966e103eb for openssl.org
commit 0966e103ebf36b48c51c384e1954093536df2a63
Author: Matt Caswell <matt@openssl.foundation>
Date: Mon Oct 19 13:19:38 2026 +0100
DTLS listener: free a pending connection that fails after HVR
dtls_listener_conn_needs_retry() reported a DTLS 1.2 connection as
waiting for its second ClientHello whenever its handshake state was
DTLS_ST_SW_HELLO_VERIFY_REQUEST, even if the connection had since
entered the error state, e.g. on receiving a fatal alert from the peer.
The listener then kept it in its pending table, and called SSL_accept()
on it every tick, until the pending timeout expired instead of freeing
it as a failed connection.
Check ossl_statem_in_error() as the DTLS 1.3 HRR case already does.
Assisted-by: Claude:claude-opus-5-5
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
Reviewed-by: Ryan Hooper <ryanh@openssl.foundation>
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Merge-date: Thu Oct 22 14:38:07 2026
Merged-from: https://github.com/openssl/openssl/pull/33265
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index d4240b29f9..09c961742e 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -2125,7 +2125,8 @@ static int dtls_listener_conn_needs_retry(SSL *ssl)
* For DTLS 1.2: Check if we're in a state that indicates HVR was sent.
* The state machine will be waiting for the next ClientHello.
*/
- if (sc->statem.hand_state == DTLS_ST_SW_HELLO_VERIFY_REQUEST)
+ if (sc->statem.hand_state == DTLS_ST_SW_HELLO_VERIFY_REQUEST
+ && !ossl_statem_in_error(sc))
return 1;
return 0;
diff --git a/test/dtlsssllistenertest.c b/test/dtlsssllistenertest.c
index 95ed2e2eda..f6d98bf941 100644
--- a/test/dtlsssllistenertest.c
+++ b/test/dtlsssllistenertest.c
@@ -7509,6 +7509,104 @@ static int test_dtls_listener_flood_then_handshake(void)
#endif
return ok;
}
+
+#ifndef OPENSSL_NO_DTLS1_2
+static void get_pending_conn_cb(SSL *ssl, const BIO_ADDR *peer, void *arg)
+{
+ *(SSL **)arg = ssl;
+}
+
+/* Has the listener's single pending connection sent its HVR? */
+static int pending_conn_sent_hvr(DTLS_LISTENER *dl)
+{
+ SSL *ssl = NULL;
+ SSL_CONNECTION *sc;
+
+ if (ossl_dgram_conn_lookup_num_items(dl->pending_conns) != 1)
+ return 0;
+ ossl_dgram_conn_lookup_foreach(dl->pending_conns, get_pending_conn_cb, &ssl);
+ sc = SSL_CONNECTION_FROM_SSL_ONLY(ssl);
+ return sc != NULL
+ && sc->statem.hand_state == DTLS_ST_SW_HELLO_VERIFY_REQUEST;
+}
+
+/*
+ * Regression test: a DTLS 1.2 pending connection which has sent an HVR and
+ * then fails on a fatal alert from its peer must be freed on that tick, not
+ * kept until the pending timeout.
+ */
+static int test_dtls12_listener_alert_after_hvr_frees_pending(void)
+{
+ SSL_CTX *sctx = NULL, *cctx = NULL;
+ SSL *listener = NULL, *clientssl = NULL;
+ BIO_ADDR *server_addr = NULL;
+ int server_fd = -1, client_fd = -1;
+ DTLS_LISTENER *dl;
+ int testresult = 0, k;
+ static const unsigned char alert[] = {
+ /* Record header */
+ SSL3_RT_ALERT, /* content type */
+ (DTLS1_VERSION >> 8) & 0xff, /* protocol version hi byte */
+ DTLS1_VERSION & 0xff, /* protocol version lo byte */
+ 0, 0, /* epoch */
+ 0, 0, 0, 0, 0, 1, /* record sequence */
+ 0, 2, /* record length */
+
+ /* Alert */
+ SSL3_AL_FATAL, /* level */
+ SSL3_AD_HANDSHAKE_FAILURE /* description */
+ };
+
+ if (!TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(),
+ DTLS_client_method(), DTLS1_2_VERSION, DTLS1_2_VERSION,
+ &sctx, &cctx, cert, privkey)))
+ goto end;
+
+ if (!TEST_true(create_udp_listener(sctx, SSL_LISTENER_FLAG_SINGLE_THREAD,
+ &listener, &server_addr, &server_fd)))
+ goto end;
+ dl = (DTLS_LISTENER *)listener;
+
+ if (!TEST_true(create_dtls_client_for_addr(cctx, server_addr,
+ &clientssl, &client_fd))
+ || !client_step(clientssl))
+ goto end;
+
+ for (k = 0; k < 100 && !pending_conn_sent_hvr(dl); k++) {
+ listener_tick(listener);
+ OSSL_sleep(10);
+ }
+ if (!TEST_true(pending_conn_sent_hvr(dl)))
+ goto end;
+
+ if (!TEST_int_eq(BIO_write(SSL_get_wbio(clientssl), alert, sizeof(alert)),
+ (int)sizeof(alert)))
+ goto end;
+
+ for (k = 0; k < 100
+ && ossl_dgram_conn_lookup_num_items(dl->pending_conns) != 0;
+ k++) {
+ listener_tick(listener);
+ OSSL_sleep(10);
+ }
+ if (!TEST_size_t_eq(ossl_dgram_conn_lookup_num_items(dl->pending_conns),
+ 0))
+ goto end;
+
+ testresult = 1;
+end:
+ SSL_free(clientssl);
+ if (client_fd >= 0)
+ BIO_closesocket(client_fd);
+ SSL_free(listener);
+ BIO_ADDR_free(server_addr);
+ if (server_fd >= 0)
+ BIO_closesocket(server_fd);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+ return testresult;
+}
+#endif /* OPENSSL_NO_DTLS1_2 */
#endif /* !OPENSSL_NO_DTLS1_2 || !OPENSSL_NO_DTLS1_3 */
#ifndef OPENSSL_NO_DTLS1_3
@@ -7775,6 +7873,9 @@ int setup_tests(void)
#ifndef OPENSSL_NO_DTLS1_3
ADD_TEST(test_dtls13_listener_conn_read_timeout);
#endif
+#ifndef OPENSSL_NO_DTLS1_2
+ ADD_TEST(test_dtls12_listener_alert_after_hvr_frees_pending);
+#endif
return 1;
}