test: add threading test for object creation

In addition, rework the multi tests to use common code.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/15713)
This commit is contained in:
Pauli 2021-06-14 11:11:16 +10:00 committed by Pauli
parent b66b024cf7
commit 0855591e1f

View File

@ -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 <windows.h>
@ -18,15 +23,39 @@
#include <openssl/crypto.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#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(&current_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;
}