Skip to content

Commit

Permalink
Add a test for QUIC non IO retry errors
Browse files Browse the repository at this point in the history
Test that errors such as SSL_ERROR_WANT_RETRY_VERIFY are properly
handled by QUIC connections.
  • Loading branch information
mattcaswell committed Sep 1, 2023
1 parent d6f64dd commit 100d519
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 6 deletions.
7 changes: 7 additions & 0 deletions doc/designs/quic-design/quic-fault-injector.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
33 changes: 27 additions & 6 deletions test/helpers/quictestlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -265,6 +267,9 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
thread_t t = thread_zero;
#endif

if (clientssl != NULL)
abortserverthread = 0;

if (!TEST_ptr(qtserv)) {
goto err;
} else if (clientssl == NULL) {
Expand Down Expand Up @@ -295,10 +300,19 @@ 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 (qtserv == NULL && rets > 0)
tsan_store(&abortserverthread, 1);
else
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;
}
}
}
}
Expand All @@ -312,6 +326,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);
Expand All @@ -327,7 +342,8 @@ 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 && !tsan_load(&abortserverthread)));

if (qtserv == NULL && rets > 0) {
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
Expand All @@ -345,6 +361,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;

Expand Down
7 changes: 7 additions & 0 deletions test/helpers/quictestlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
59 changes: 59 additions & 0 deletions test/quicapitest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 100d519

Please sign in to comment.