Commit 7520345190 for openssl.org
commit 7520345190b4f0a9303c6a9352b3e29c71df6c04
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date: Mon Jun 22 14:44:44 2026 +0200
quic: add mfail test for a read with key update
This adds test for #31268 with using SSL_read for QUIC client and
forcing key update.
Assisted-by: Claude:claude-opus-4-8
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
MergeDate: Thu Jul 2 07:21:14 2026
(Merged from https://github.com/openssl/openssl/pull/31272)
diff --git a/test/quicapitest.c b/test/quicapitest.c
index 5b7cdb6a3c..ff858fb170 100644
--- a/test/quicapitest.c
+++ b/test/quicapitest.c
@@ -21,6 +21,7 @@
#include "../ssl/ssl_local.h"
#include "../ssl/quic/quic_channel_local.h"
#include "internal/quic_error.h"
+#include "internal/quic_ssl.h"
static OSSL_LIB_CTX *libctx = NULL;
static char *propq = NULL;
@@ -39,6 +40,9 @@ static BIO_ADDR *create_addr(struct in_addr *ina, short int port);
static int bio_addr_bind(BIO *bio, BIO_ADDR *addr);
static SSL *ql_create(SSL_CTX *ssl_ctx, BIO *bio);
static SSL_CTX *create_server_ctx(void);
+static SSL_CTX *create_client_ctx(void);
+static int create_quic_ssl_objects(SSL_CTX *sctx, SSL_CTX *cctx,
+ SSL **lssl, SSL **cssl);
static int qc_init(SSL *qconn, BIO_ADDR *dst_addr);
/* The ssltrace test assumes some options are switched on/off */
@@ -214,6 +218,92 @@ end:
return ret;
}
+#ifndef OPENSSL_NO_CACHED_FETCH
+static int test_ssl_read_key_update_mfail(void)
+{
+ SSL_CTX *cctx = NULL, *sctx = NULL;
+ SSL *clientssl = NULL, *serverssl = NULL, *qlistener = NULL;
+ QUIC_CHANNEL *sch = NULL;
+ int ret = 0, i;
+ const char *msg = "ping";
+ size_t msglen = strlen(msg);
+ size_t numbytes = 0;
+ unsigned char buf[64];
+
+ if (!TEST_ptr(sctx = create_server_ctx())
+ || !TEST_ptr(cctx = create_client_ctx()))
+ goto err;
+
+ if (!create_quic_ssl_objects(sctx, cctx, &qlistener, &clientssl))
+ goto err;
+
+ if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "localhost")))
+ goto err;
+
+ /* Send ClientHello and server retry. */
+ for (i = 0; i < 2; i++) {
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ))
+ goto err;
+ SSL_handle_events(qlistener);
+ }
+
+ serverssl = SSL_accept_connection(qlistener, 0);
+ if (!TEST_ptr(serverssl)
+ || !TEST_true(create_bare_ssl_connection(serverssl, clientssl,
+ SSL_ERROR_NONE, 0, 0)))
+ goto err;
+
+ if (!TEST_ptr(sch = ossl_quic_conn_get_channel(serverssl)))
+ goto err;
+
+ /* Open the default stream so the server has something to write back on. */
+ if (!TEST_true(SSL_write_ex(clientssl, msg, msglen, &numbytes))
+ || !TEST_size_t_eq(numbytes, msglen))
+ goto err;
+
+ /* Route the datagram to the server connection and let it consume it. */
+ SSL_handle_events(qlistener);
+ SSL_handle_events(serverssl);
+ if (!TEST_true(SSL_read_ex(serverssl, buf, sizeof(buf), &numbytes)))
+ goto err;
+
+ /*
+ * Force the server's TX side to rotate keys. Its next outgoing packet
+ * will carry the flipped Key Phase bit. When the client decrypts that
+ * packet, qrx_key_update_initiated -> rxku_detected -> ch_trigger_txku
+ * fires on the client.
+ */
+ if (!TEST_true(ossl_qtx_trigger_key_update(sch->qtx)))
+ goto err;
+
+ if (!TEST_true(SSL_write_ex(serverssl, msg, msglen, &numbytes))
+ || !TEST_size_t_eq(numbytes, msglen))
+ goto err;
+
+ /*
+ * Process the inbound packet (carrying the new Key Phase) under mfail.
+ * SSL_read_ex ticks the client, reads the datagram off its BIO and
+ * decrypts it, which is where the key update handling runs.
+ */
+ MFAIL_start();
+ ret = SSL_read_ex(clientssl, buf, sizeof(buf), &numbytes);
+ MFAIL_end();
+
+ ret = (ret > 0);
+
+err:
+ SSL_free(serverssl);
+ SSL_free(clientssl);
+ SSL_free(qlistener);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+
+ return ret;
+}
+#endif
+
/*
* Test that sending FIN with no data to a client blocking in SSL_read_ex() will
* wake up the client.
@@ -3655,6 +3745,9 @@ int setup_tests(void)
goto err;
ADD_ALL_TESTS(test_quic_write_read, 3);
+#ifndef OPENSSL_NO_CACHED_FETCH
+ ADD_MFAIL_NO_CHECK_TEST(test_ssl_read_key_update_mfail);
+#endif
ADD_TEST(test_fin_only_blocking);
ADD_TEST(test_ciphersuites);
ADD_TEST(test_cipher_find);