diff --git a/test/threadstest.c b/test/threadstest.c index 3160d9e334..f689676c54 100644 --- a/test/threadstest.c +++ b/test/threadstest.c @@ -7,8 +7,13 @@ * https://www.openssl.org/source/license.html */ -/* test_multi below tests the thread safety of a deprecated function */ -#define OPENSSL_SUPPRESS_DEPRECATED +/* + * The test_multi_downgrade_shared_pkey function tests the thread safety of a + * deprecated function. + */ +#ifndef OPENSSL_NO_DEPRECATED_3_0 +# define OPENSSL_SUPPRESS_DEPRECATED +#endif #if defined(_WIN32) # include @@ -18,15 +23,39 @@ #include #include #include -#include +#include +#include "internal/tsan_assist.h" +#include "internal/nelem.h" #include "testutil.h" #include "threadstest.h" +/* Limit the maximum number of threads */ +#define MAXIMUM_THREADS 3 + +/* Limit the maximum number of providers loaded into a library context */ +#define MAXIMUM_PROVIDERS 4 + static int do_fips = 0; static char *privkey; static char *config_file = NULL; static int multidefault_run = 0; +static const char *default_provider[] = { "default", NULL }; +static const char *fips_provider[] = { "fips", NULL }; +static const char *fips_and_default_providers[] = { "default", "fips", NULL }; + +/* Grab a globally unique integer value */ +static int get_new_uid(void) +{ + /* + * Start with a nice large number to avoid potential conflicts when + * we generate a new OID. + */ + static TSAN_QUALIFIER int current_uid = 1 << (sizeof(int) * 8 - 2); + + return tsan_counter(¤t_uid); +} + static int test_lock(void) { CRYPTO_RWLOCK *lock = CRYPTO_THREAD_lock_new(); @@ -207,6 +236,94 @@ static int test_atomic(void) static OSSL_LIB_CTX *multi_libctx = NULL; static int multi_success; +static OSSL_PROVIDER *multi_provider[MAXIMUM_PROVIDERS + 1]; +static size_t multi_num_threads; +static thread_t multi_threads[MAXIMUM_THREADS]; + +static void multi_intialise(void) +{ + multi_success = 1; + multi_libctx = NULL; + multi_num_threads = 0; + memset(multi_threads, 0, sizeof(multi_threads)); + memset(multi_provider, 0, sizeof(multi_provider)); +} + +static void thead_teardown_libctx(void) +{ + OSSL_PROVIDER **p; + + for (p = multi_provider; *p != NULL; p++) + OSSL_PROVIDER_unload(*p); + OSSL_LIB_CTX_free(multi_libctx); + multi_intialise(); +} + +static int thread_setup_libctx(int libctx, const char *providers[]) +{ + size_t n; + + if (libctx && !TEST_true(test_get_libctx(&multi_libctx, NULL, config_file, + NULL, NULL))) + return 0; + + if (providers != NULL) + for (n = 0; providers[n] != NULL; n++) + if (!TEST_size_t_lt(n, MAXIMUM_PROVIDERS) + || !TEST_ptr(multi_provider[n] = OSSL_PROVIDER_load(multi_libctx, + providers[n]))) { + thead_teardown_libctx(); + return 0; + } + return 1; +} + +static int teardown_threads(void) +{ + size_t i; + + for (i = 0; i < multi_num_threads; i++) + if (!TEST_true(wait_for_thread(multi_threads[i]))) + return 0; + return 1; +} + +static int start_threads(size_t n, void (*thread_func)(void)) +{ + size_t i; + + if (!TEST_size_t_le(multi_num_threads + n, MAXIMUM_THREADS)) + return 0; + + for (i = 0 ; i < n; i++) + if (!TEST_true(run_thread(multi_threads + multi_num_threads++, thread_func))) + return 0; + return 1; +} + +/* Template multi-threaded test function */ +static int thread_run_test(void (*main_func)(void), + size_t num_threads, void (*thread_func)(void), + int libctx, const char *providers[]) +{ + int testresult = 0; + + multi_intialise(); + if (!thread_setup_libctx(libctx, providers) + || !start_threads(num_threads, thread_func)) + goto err; + + if (main_func != NULL) + main_func(); + + if (!teardown_threads() + || !TEST_true(multi_success)) + goto err; + testresult = 1; + err: + thead_teardown_libctx(); + return testresult; +} static void thread_general_worker(void) { @@ -334,21 +451,6 @@ static void thread_shared_evp_pkey(void) multi_success = 0; } -static void thread_downgrade_shared_evp_pkey(void) -{ -#ifndef OPENSSL_NO_DEPRECATED_3_0 - /* - * This test is only relevant for deprecated functions that perform - * downgrading - */ - if (EVP_PKEY_get0_RSA(shared_evp_pkey) == NULL) - multi_success = 0; -#else - /* Shouldn't ever get here */ - multi_success = 0; -#endif -} - static void thread_provider_load_unload(void) { OSSL_PROVIDER *deflt = OSSL_PROVIDER_load(multi_libctx, "default"); @@ -360,107 +462,99 @@ static void thread_provider_load_unload(void) OSSL_PROVIDER_unload(deflt); } -/* - * Do work in multiple worker threads at the same time. - * Test 0: General worker, using the default provider - * Test 1: General worker, using the fips provider - * Test 2: Simple fetch worker - * Test 3: Worker downgrading a shared EVP_PKEY - * Test 4: Worker using a shared EVP_PKEY - * Test 5: Worker loading and unloading a provider - */ -static int test_multi(int idx) +static int test_multi_general_worker_default_provider(void) { - thread_t thread1, thread2; - int testresult = 0; - OSSL_PROVIDER *prov = NULL, *prov2 = NULL; - void (*worker)(void) = NULL; - void (*worker2)(void) = NULL; - EVP_MD *sha256 = NULL; + return thread_run_test(&thread_general_worker, 2, &thread_general_worker, + 1, default_provider); +} - if (idx == 1 && !do_fips) +static int test_multi_general_worker_fips_provider(void) +{ + if (!do_fips) return TEST_skip("FIPS not supported"); + return thread_run_test(&thread_general_worker, 2, &thread_general_worker, + 1, fips_provider); +} -#ifdef OPENSSL_NO_DEPRECATED_3_0 - if (idx == 3) - return TEST_skip("Skipping tests for deprected functions"); +static int test_multi_fetch_worker(void) +{ + return thread_run_test(&thread_multi_simple_fetch, + 2, &thread_multi_simple_fetch, 1, default_provider); +} + +static int test_multi_shared_pkey_common(void (*worker)(void)) +{ + int testresult = 0; + + multi_intialise(); + if (!thread_setup_libctx(1, do_fips ? fips_and_default_providers + : default_provider) + || !TEST_ptr(shared_evp_pkey = load_pkey_pem(privkey, multi_libctx)) + || !start_threads(1, &thread_shared_evp_pkey) + || !start_threads(1, worker)) + goto err; + + thread_shared_evp_pkey(); + + if (!teardown_threads() + || !TEST_true(multi_success)) + goto err; + testresult = 1; + err: + EVP_PKEY_free(shared_evp_pkey); + thead_teardown_libctx(); + return testresult; +} + +#ifndef OPENSSL_NO_DEPRECATED_3_0 +static void thread_downgrade_shared_evp_pkey(void) +{ + /* + * This test is only relevant for deprecated functions that perform + * downgrading + */ + if (EVP_PKEY_get0_RSA(shared_evp_pkey) == NULL) + multi_success = 0; +} + +static int test_multi_downgrade_shared_pkey(void) +{ + return test_multi_shared_pkey_common(&thread_downgrade_shared_evp_pkey); +} #endif - multi_success = 1; - if (!TEST_true(test_get_libctx(&multi_libctx, NULL, config_file, - NULL, NULL))) - return 0; +static int test_multi_shared_pkey(void) +{ + return test_multi_shared_pkey_common(&thread_shared_evp_pkey); +} - prov = OSSL_PROVIDER_load(multi_libctx, (idx == 1) ? "fips" : "default"); - if (!TEST_ptr(prov)) +static int test_multi_load_unload_provider(void) +{ + EVP_MD *sha256 = NULL; + OSSL_PROVIDER *prov = NULL; + int testresult = 0; + + multi_intialise(); + if (!thread_setup_libctx(1, NULL) + || !TEST_ptr(prov = OSSL_PROVIDER_load(multi_libctx, "default")) + || !TEST_ptr(sha256 = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL)) + || !TEST_true(OSSL_PROVIDER_unload(prov))) + goto err; + prov = NULL; + + if (!start_threads(2, &thread_provider_load_unload)) goto err; - switch (idx) { - case 0: - case 1: - worker = thread_general_worker; - break; - case 2: - worker = thread_multi_simple_fetch; - break; - case 3: - worker2 = thread_downgrade_shared_evp_pkey; - /* fall through */ - case 4: - /* - * If available we have both the default and fips providers for this - * test - */ - if (do_fips - && !TEST_ptr(prov2 = OSSL_PROVIDER_load(multi_libctx, "fips"))) - goto err; - if (!TEST_ptr(shared_evp_pkey = load_pkey_pem(privkey, multi_libctx))) - goto err; - worker = thread_shared_evp_pkey; - break; - case 5: - /* - * We ensure we get an md from the default provider, and then unload the - * provider. This ensures the provider remains around but in a - * deactivated state. - */ - sha256 = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL); - OSSL_PROVIDER_unload(prov); - prov = NULL; - worker = thread_provider_load_unload; - break; - default: - TEST_error("Invalid test index"); + thread_provider_load_unload(); + + if (!teardown_threads() + || !TEST_true(multi_success)) goto err; - } - if (worker2 == NULL) - worker2 = worker; - - if (!TEST_true(run_thread(&thread1, worker)) - || !TEST_true(run_thread(&thread2, worker2))) - goto err; - - worker(); - testresult = 1; - /* - * Don't combine these into one if statement; must wait for both threads. - */ - if (!TEST_true(wait_for_thread(thread1))) - testresult = 0; - if (!TEST_true(wait_for_thread(thread2))) - testresult = 0; - if (!TEST_true(multi_success)) - testresult = 0; - err: - EVP_MD_free(sha256); OSSL_PROVIDER_unload(prov); - OSSL_PROVIDER_unload(prov2); - OSSL_LIB_CTX_free(multi_libctx); - EVP_PKEY_free(shared_evp_pkey); - shared_evp_pkey = NULL; - multi_libctx = NULL; + EVP_MD_free(sha256); + thead_teardown_libctx(); return testresult; } @@ -469,21 +563,17 @@ static int test_multi(int idx) * run with a thread sanitizer, should crash if the core provider code * doesn't synchronize well enough. */ -#define MULTI_LOAD_THREADS 3 static void test_multi_load_worker(void) { OSSL_PROVIDER *prov; - (void)TEST_ptr(prov = OSSL_PROVIDER_load(NULL, "default")); - (void)TEST_true(OSSL_PROVIDER_unload(prov)); + if (!TEST_ptr(prov = OSSL_PROVIDER_load(multi_libctx, "default")) + || !TEST_true(OSSL_PROVIDER_unload(prov))) + multi_success = 0; } static int test_multi_default(void) { - thread_t thread1, thread2; - int testresult = 0; - OSSL_PROVIDER *prov = NULL; - /* Avoid running this test twice */ if (multidefault_run) { TEST_skip("multi default test already run"); @@ -491,34 +581,13 @@ static int test_multi_default(void) } multidefault_run = 1; - multi_success = 1; - multi_libctx = NULL; - prov = OSSL_PROVIDER_load(multi_libctx, "default"); - if (!TEST_ptr(prov)) - goto err; - - if (!TEST_true(run_thread(&thread1, thread_multi_simple_fetch)) - || !TEST_true(run_thread(&thread2, thread_multi_simple_fetch))) - goto err; - - thread_multi_simple_fetch(); - - if (!TEST_true(wait_for_thread(thread1)) - || !TEST_true(wait_for_thread(thread2)) - || !TEST_true(multi_success)) - goto err; - - testresult = 1; - - err: - OSSL_PROVIDER_unload(prov); - return testresult; + return thread_run_test(&thread_multi_simple_fetch, + 2, &thread_multi_simple_fetch, 0, default_provider); } static int test_multi_load(void) { - thread_t threads[MULTI_LOAD_THREADS]; - int i, res = 1; + int res = 1; /* The multidefault test must run prior to this test */ if (!multidefault_run) { @@ -526,13 +595,28 @@ static int test_multi_load(void) res = test_multi_default(); } - for (i = 0; i < MULTI_LOAD_THREADS; i++) - (void)TEST_true(run_thread(&threads[i], test_multi_load_worker)); + return thread_run_test(NULL, 3, &test_multi_load_worker, 0, NULL) && res; +} - for (i = 0; i < MULTI_LOAD_THREADS; i++) - (void)TEST_true(wait_for_thread(threads[i])); +static void test_obj_create_one(void) +{ + char tids[12], oid[40], sn[30], ln[30]; + int id = get_new_uid(); - return res; + BIO_snprintf(tids, sizeof(tids), "%d", id); + BIO_snprintf(oid, sizeof(oid), "1.3.6.1.4.1.16604.%s", tids); + BIO_snprintf(sn, sizeof(sn), "short-name-%s", tids); + BIO_snprintf(ln, sizeof(ln), "long-name-%s", tids); + if (!TEST_true(id = OBJ_create(oid, sn, ln)) + || !TEST_true(OBJ_add_sigid(id, NID_sha3_256, NID_rsa))) + multi_success = 0; +} + +static int test_obj_add(void) +{ + return thread_run_test(&test_obj_create_one, + MAXIMUM_THREADS, &test_obj_create_one, + 1, default_provider); } typedef enum OPTION_choice { @@ -589,7 +673,15 @@ int setup_tests(void) ADD_TEST(test_thread_local); ADD_TEST(test_atomic); ADD_TEST(test_multi_load); - ADD_ALL_TESTS(test_multi, 6); + ADD_TEST(test_multi_general_worker_default_provider); + ADD_TEST(test_multi_general_worker_fips_provider); + ADD_TEST(test_multi_fetch_worker); + ADD_TEST(test_multi_shared_pkey); +#ifndef OPENSSL_NO_DEPRECATED_3_0 + ADD_TEST(test_multi_downgrade_shared_pkey); +#endif + ADD_TEST(test_multi_load_unload_provider); + ADD_TEST(test_obj_add); return 1; }