From: Matt Caswell Date: Thu, 31 Aug 2023 15:18:28 +0000 (+0100) Subject: Add a test for QUIC non IO retry errors X-Git-Tag: openssl-3.2.0-alpha1~35 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=48724e8a205c732705c3f54a3bd43d7049e77774;p=thirdparty%2Fopenssl.git Add a test for QUIC non IO retry errors Test that errors such as SSL_ERROR_WANT_RETRY_VERIFY are properly handled by QUIC connections. Reviewed-by: Tomas Mraz Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/21922) --- diff --git a/doc/designs/quic-design/quic-fault-injector.md b/doc/designs/quic-design/quic-fault-injector.md index a60763518f0..30db905ee84 100644 --- a/doc/designs/quic-design/quic-fault-injector.md +++ b/doc/designs/quic-design/quic-fault-injector.md @@ -228,6 +228,13 @@ void ossl_quic_fault_free(OSSL_QUIC_FAULT *fault); */ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl); +/* + * Same as qtest_create_quic_connection but will stop (successfully) if the + * clientssl indicates SSL_ERROR_WANT_XXX as specified by |wanterr| + */ +int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl, + int wanterr); + /* * Confirm that the server has received the given transport error code. */ diff --git a/test/helpers/quictestlib.c b/test/helpers/quictestlib.c index 2dbbb435bba..2fcb4bdb6f8 100644 --- a/test/helpers/quictestlib.c +++ b/test/helpers/quictestlib.c @@ -239,6 +239,7 @@ int qtest_supports_blocking(void) #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) static int globserverret = 0; +static TSAN_QUALIFIER int abortserverthread = 0; static QUIC_TSERVER *globtserv; static const thread_t thread_zero; @@ -253,7 +254,8 @@ static void run_server_thread(void) } #endif -int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl) +int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl, + int wanterr) { int retc = -1, rets = 0, abortctr = 0, ret = 0; int clienterr = 0, servererr = 0; @@ -263,6 +265,9 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl) * t uninitialised */ thread_t t = thread_zero; + + if (clientssl != NULL) + abortserverthread = 0; #endif if (!TEST_ptr(qtserv)) { @@ -295,10 +300,21 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl) if (retc <= 0) { err = SSL_get_error(clientssl, retc); - if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { - TEST_info("SSL_connect() failed %d, %d", retc, err); - TEST_openssl_errors(); - clienterr = 1; + if (err == wanterr) { + retc = 1; +#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) + if (qtserv == NULL && rets > 0) + tsan_store(&abortserverthread, 1); + else +#endif + rets = 1; + } else { + if (err != SSL_ERROR_WANT_READ + && err != SSL_ERROR_WANT_WRITE) { + TEST_info("SSL_connect() failed %d, %d", retc, err); + TEST_openssl_errors(); + clienterr = 1; + } } } } @@ -312,6 +328,7 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl) */ if (!clienterr && retc <= 0) SSL_handle_events(clientssl); + if (!servererr && rets <= 0) { qtest_add_time(1); ossl_quic_tserver_tick(qtserv); @@ -327,7 +344,12 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl) TEST_info("No progress made"); goto err; } - } while ((retc <= 0 && !clienterr) || (rets <= 0 && !servererr)); + } while ((retc <= 0 && !clienterr) + || (rets <= 0 && !servererr +#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) + && !tsan_load(&abortserverthread) +#endif + )); if (qtserv == NULL && rets > 0) { #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) @@ -345,6 +367,11 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl) return ret; } +int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl) +{ + return qtest_create_quic_connection_ex(qtserv, clientssl, SSL_ERROR_NONE); +} + #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) static TSAN_QUALIFIER int shutdowndone; diff --git a/test/helpers/quictestlib.h b/test/helpers/quictestlib.h index cfda1b29b5f..fb1c5d88b53 100644 --- a/test/helpers/quictestlib.h +++ b/test/helpers/quictestlib.h @@ -62,6 +62,13 @@ int qtest_supports_blocking(void); */ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl); +/* + * Same as qtest_create_quic_connection but will stop (successfully) if the + * clientssl indicates SSL_ERROR_WANT_XXX as specified by |wanterr| + */ +int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl, + int wanterr); + /* * Shutdown the client SSL object gracefully */ diff --git a/test/quicapitest.c b/test/quicapitest.c index 83a048bc744..5eff924527e 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -1003,6 +1003,64 @@ static int test_multiple_dgrams(void) return testresult; } +static int non_io_retry_cert_verify_cb(X509_STORE_CTX *ctx, void *arg) +{ + int idx = SSL_get_ex_data_X509_STORE_CTX_idx(); + SSL *ssl; + int *ctr = (int *)arg; + + /* this should not happen but check anyway */ + if (idx < 0 + || (ssl = X509_STORE_CTX_get_ex_data(ctx, idx)) == NULL) + return 0; + + /* If this is the first time we've been called then retry */ + if (((*ctr)++) == 0) + return SSL_set_retry_verify(ssl); + + /* Otherwise do nothing - verification succeeds. Continue as normal */ + return 1; +} + +/* Test that we can handle a non-io related retry error + * Test 0: Non-blocking + * Test 1: Blocking + */ +static int test_non_io_retry(int idx) +{ + SSL_CTX *cctx; + SSL *clientquic = NULL; + QUIC_TSERVER *qtserv = NULL; + int testresult = 0; + int flags = 0, ctr = 0; + + if (idx >= 1 && !qtest_supports_blocking()) + return TEST_skip("Blocking tests not supported in this build"); + + cctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method()); + if (!TEST_ptr(cctx)) + goto err; + + SSL_CTX_set_cert_verify_callback(cctx, non_io_retry_cert_verify_cb, &ctr); + + flags = (idx >= 1) ? QTEST_FLAG_BLOCK : 0; + if (!TEST_true(qtest_create_quic_objects(libctx, cctx, NULL, cert, privkey, + flags, &qtserv, &clientquic, NULL)) + || !TEST_true(qtest_create_quic_connection_ex(qtserv, clientquic, + SSL_ERROR_WANT_RETRY_VERIFY)) + || !TEST_int_eq(SSL_want(clientquic), SSL_RETRY_VERIFY) + || !TEST_true(qtest_create_quic_connection(qtserv, clientquic))) + goto err; + + testresult = 1; + err: + SSL_free(clientquic); + ossl_quic_tserver_free(qtserv); + SSL_CTX_free(cctx); + + return testresult; +} + OPT_TEST_DECLARE_USAGE("provider config certsdir datadir\n") int setup_tests(void) @@ -1072,6 +1130,7 @@ int setup_tests(void) ADD_TEST(test_bio_ssl); ADD_TEST(test_back_pressure); ADD_TEST(test_multiple_dgrams); + ADD_ALL_TESTS(test_non_io_retry, 2); return 1; err: cleanup_tests();