From ae95a40e8d453aa9d4f6499568f658ffc88a7d6e Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Thu, 10 Dec 2020 15:39:58 +0000 Subject: [PATCH] Add a test for performing work in multiple concurrent threads We test both the default provider and the fips provider Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/13660) --- test/recipes/90-test_threads.t | 32 ++++++- test/threadstest.c | 158 ++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 2 deletions(-) diff --git a/test/recipes/90-test_threads.t b/test/recipes/90-test_threads.t index e629f24d1c5..fa4d2b8de97 100644 --- a/test/recipes/90-test_threads.t +++ b/test/recipes/90-test_threads.t @@ -8,5 +8,35 @@ use OpenSSL::Test::Simple; +use OpenSSL::Test qw/:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file/; +use OpenSSL::Test::Utils; +use Cwd qw(abs_path); -simple_test("test_threads", "threadstest"); +BEGIN { +setup("test_threads"); +} + +use lib srctop_dir('Configurations'); +use lib bldtop_dir('.'); +use platform; + +my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); + + +plan tests => 1 + ($no_fips ? 0 : 1); + +if (!$no_fips) { + my $infile = bldtop_file('providers', platform->dso('fips')); + ok(run(app(['openssl', 'fipsinstall', + '-out', bldtop_file('providers', 'fipsmodule.cnf'), + '-module', $infile])), + "fipsinstall"); +} + +if ($no_fips) { + $ENV{OPENSSL_CONF} = abs_path(srctop_file("test", "default.cnf")); + ok(run(test(["threadstest"])), "running test_threads"); +} else { + $ENV{OPENSSL_CONF} = abs_path(srctop_file("test", "default-and-fips.cnf")); + ok(run(test(["threadstest", "-fips"])), "running test_threads"); +} diff --git a/test/threadstest.c b/test/threadstest.c index d7ed59781d9..81379a3a04a 100644 --- a/test/threadstest.c +++ b/test/threadstest.c @@ -11,9 +11,15 @@ # include #endif +#include #include +#include +#include +#include #include "testutil.h" +static int do_fips = 0; + #if !defined(OPENSSL_THREADS) || defined(CRYPTO_TDEBUG) typedef unsigned int thread_t; @@ -254,16 +260,166 @@ static int test_atomic(void) testresult = 1; err: - CRYPTO_THREAD_lock_free(lock); return testresult; } +static OSSL_LIB_CTX *multi_libctx = NULL; +static int multi_success; + +static void thread_multi_worker(void) +{ + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + EVP_MD *md = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL); + EVP_CIPHER_CTX *cipherctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER *ciph = EVP_CIPHER_fetch(multi_libctx, "AES-128-CBC", NULL); + const char *message = "Hello World"; + size_t messlen = strlen(message); + /* Should be big enough for encryption output too */ + unsigned char out[EVP_MAX_MD_SIZE]; + const unsigned char key[AES_BLOCK_SIZE] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f + }; + const unsigned char iv[AES_BLOCK_SIZE] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f + }; + unsigned int mdoutl; + int ciphoutl; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + int testresult = 0; + int i, isfips; + + isfips = OSSL_PROVIDER_available(multi_libctx, "fips"); + + if (!TEST_ptr(mdctx) + || !TEST_ptr(md) + || !TEST_ptr(cipherctx) + || !TEST_ptr(ciph)) + goto err; + + /* Do some work */ + for (i = 0; i < 5; i++) { + if (!TEST_true(EVP_DigestInit_ex(mdctx, md, NULL)) + || !TEST_true(EVP_DigestUpdate(mdctx, message, messlen)) + || !TEST_true(EVP_DigestFinal(mdctx, out, &mdoutl))) + goto err; + } + for (i = 0; i < 5; i++) { + if (!TEST_true(EVP_EncryptInit_ex(cipherctx, ciph, NULL, key, iv)) + || !TEST_true(EVP_EncryptUpdate(cipherctx, out, &ciphoutl, + (unsigned char *)message, + messlen)) + || !TEST_true(EVP_EncryptFinal(cipherctx, out, &ciphoutl))) + goto err; + } + + pctx = EVP_PKEY_CTX_new_from_name(multi_libctx, "RSA", NULL); + if (!TEST_ptr(pctx) + || !TEST_int_gt(EVP_PKEY_keygen_init(pctx), 0) + /* + * We want the test to run quickly - not securely. Therefore we + * use an insecure bit length where we can (512). In the FIPS + * module though we must use a longer length. + */ + || !TEST_int_gt(EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, + isfips ? 2048 : 512), + 0) + || !TEST_int_gt(EVP_PKEY_keygen(pctx, &pkey), 0)) + goto err; + + testresult = 1; + err: + EVP_MD_CTX_free(mdctx); + EVP_MD_free(md); + EVP_CIPHER_CTX_free(cipherctx); + EVP_CIPHER_free(ciph); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + if (!testresult) + multi_success = 0; +} + +/* + * Do work in multiple worker threads at the same time. + * Test 0: Use the default provider + * Test 1: Use the fips provider + */ +static int test_multi(int idx) +{ + thread_t thread1, thread2; + int testresult = 0; + OSSL_PROVIDER *prov = NULL; + + if (idx == 1 && !do_fips) + return TEST_skip("FIPS not supported"); + + multi_success = 1; + multi_libctx = OSSL_LIB_CTX_new(); + if (!TEST_ptr(multi_libctx)) + goto err; + prov = OSSL_PROVIDER_load(multi_libctx, (idx == 0) ? "default" : "fips"); + if (!TEST_ptr(prov)) + goto err; + + if (!TEST_true(run_thread(&thread1, thread_multi_worker)) + || !TEST_true(run_thread(&thread2, thread_multi_worker))) + goto err; + + thread_multi_worker(); + + 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); + OSSL_LIB_CTX_free(multi_libctx); + return testresult; +} + +typedef enum OPTION_choice { + OPT_ERR = -1, + OPT_EOF = 0, + OPT_FIPS, + OPT_TEST_ENUM +} OPTION_CHOICE; + +const OPTIONS *test_get_options(void) +{ + static const OPTIONS options[] = { + OPT_TEST_OPTIONS_DEFAULT_USAGE, + { "fips", OPT_FIPS, '-', "Test the FIPS provider" }, + { NULL } + }; + return options; +} + int setup_tests(void) { + OPTION_CHOICE o; + + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_FIPS: + do_fips = 1; + break; + case OPT_TEST_CASES: + break; + default: + return 0; + } + } + ADD_TEST(test_lock); ADD_TEST(test_once); ADD_TEST(test_thread_local); ADD_TEST(test_atomic); + ADD_ALL_TESTS(test_multi, 2); return 1; } -- 2.47.2