summaryrefslogtreecommitdiffstats
path: root/test/threadstest.c
diff options
context:
space:
mode:
authorČestmír Kalina <ckalina@redhat.com>2021-09-27 22:42:11 +0200
committerMatt Caswell <matt@openssl.org>2022-10-17 09:45:39 +0100
commit4574a7fd8dda070b129d76defca07703cab53842 (patch)
treea6e03446b0f784e550010a2c51efe5b7861e9473 /test/threadstest.c
parentb1372197496650c3cb318cade911a3bd6af14adc (diff)
crypto: add preemptive threading support
Some primitives are designed to be used in a multi-threaded environment, if supported, e.g., Argon2. This patch adds support for preemptive threading and basic synchronization primitives for platforms compliant with POSIX threads or Windows CRT. Native functions are wrapped to provide a common (internal) API. Threading support can be disabled at compile time. If enabled, threading is disabled by default and needs to be explicitly enabled by the user. Thread enablement requires an explicit limit on the number of threads that OpenSSL may spawn (non-negative integer/infinity). The limit may be changed. Signed-off-by: Čestmír Kalina <ckalina@redhat.com> Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/12255)
Diffstat (limited to 'test/threadstest.c')
-rw-r--r--test/threadstest.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/test/threadstest.c b/test/threadstest.c
index 505b2cf07e..af1d78a3b7 100644
--- a/test/threadstest.c
+++ b/test/threadstest.c
@@ -20,11 +20,15 @@
#endif
#include <string.h>
+#include <internal/cryptlib.h>
+#include <internal/thread_arch.h>
+#include <internal/thread.h>
#include <openssl/crypto.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/rand.h>
+#include <openssl/thread.h>
#include "internal/tsan_assist.h"
#include "internal/nelem.h"
#include "testutil.h"
@@ -741,6 +745,326 @@ err:
}
#endif
+static int test_thread_reported_flags(void)
+{
+ uint32_t flags = OSSL_get_thread_support_flags();
+
+#if !defined(OPENSSL_THREADS)
+ if (!TEST_int_eq(flags, 0))
+ return 0;
+#endif
+
+#if defined(OPENSSL_NO_THREAD_POOL)
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL, 0))
+ return 0;
+#else
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL,
+ OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL))
+ return 0;
+#endif
+
+#if defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN, 0))
+ return 0;
+#else
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN,
+ OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN))
+ return 0;
+#endif
+
+ return 1;
+}
+
+#if defined(OPENSSL_THREADS)
+
+# define TEST_THREAD_NATIVE_FN_SET_VALUE 1
+static uint32_t test_thread_native_fn(void *data)
+{
+ uint32_t *ldata = (uint32_t*) data;
+ *ldata = *ldata + 1;
+ return *ldata - 1;
+}
+
+static uint32_t test_thread_noreturn(void *data)
+{
+ CRYPTO_MUTEX *lock = (uint32_t*) data;
+
+ /* lock is assumed to be locked */
+ ossl_crypto_mutex_lock(lock);
+
+ /* unreachable */
+ OPENSSL_die("test_thread_noreturn", __FILE__, __LINE__);
+ return 0;
+}
+
+/* Tests of native threads */
+
+static int test_thread_native(void)
+{
+ int testval = 0;
+ uint32_t retval;
+ uint32_t local;
+ CRYPTO_THREAD *t;
+ CRYPTO_MUTEX *lock;
+
+ /* thread spawn, join and termination */
+
+ local = 1;
+ t = ossl_crypto_thread_native_start(test_thread_native_fn, &local, 1);
+ if (!TEST_ptr(t))
+ return 0;
+
+ /*
+ * pthread_join results in undefined behaviour if called on a joined
+ * thread. We do not impose such restrictions, so it's up to us to
+ * ensure that this does not happen (thread sanitizer will warn us
+ * if we do).
+ */
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
+ return 0;
+
+ if (!TEST_int_eq(retval, 1) || !TEST_int_eq(local, 2))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_terminate(t), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_native_terminate(t), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
+ return 0;
+ t = NULL;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 0))
+ return 0;
+
+ /* termination of a long running thread */
+
+ lock = ossl_crypto_mutex_new();
+ if (!TEST_ptr(lock))
+ return 0;
+ ossl_crypto_mutex_lock(lock);
+
+ t = ossl_crypto_thread_native_start(test_thread_noreturn, lock, 1);
+ if (!TEST_ptr(t))
+ goto fail;
+ if (!TEST_int_eq(ossl_crypto_thread_native_terminate(t), 1))
+ goto fail;
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
+ goto fail;
+
+ testval = 1;
+
+fail:
+ ossl_crypto_mutex_unlock(lock);
+ ossl_crypto_mutex_free(&lock);
+ if (!TEST_ptr_null(lock))
+ return 0;
+
+ return testval;
+}
+
+#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+static int test_thread_internal(void)
+{
+ uint32_t retval[3];
+ uint32_t local[3] = { 0 };
+ uint32_t threads_supported;
+ size_t i;
+ void *t[3];
+ OSSL_LIB_CTX *cust_ctx = OSSL_LIB_CTX_new();
+
+ threads_supported = OSSL_get_thread_support_flags();
+ threads_supported &= OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN;
+
+ if (threads_supported == 0) {
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, 1), 0))
+ return 0;
+ if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 1), 0))
+ return 0;
+
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+
+ t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr_null(t[0]))
+ return 0;
+
+ return 1;
+ }
+
+ /* fail when not allowed to use threads */
+
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr_null(t[0]))
+ return 0;
+
+ /* fail when enabled on a different context */
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+ if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 1), 1))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 1))
+ return 0;
+ t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr_null(t[0]))
+ return 0;
+ if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 0), 1))
+ return 0;
+
+ /* sequential startup */
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, 1), 1))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 1))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ local[0] = i + 1;
+
+ t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr(t[i]))
+ return 0;
+
+ /*
+ * pthread_join results in undefined behaviour if called on a joined
+ * thread. We do not impose such restrictions, so it's up to us to
+ * ensure that this does not happen (thread sanitizer will warn us
+ * if we do).
+ */
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[0]), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[0]), 1))
+ return 0;
+
+ if (!TEST_int_eq(retval[0], i + 1) || !TEST_int_eq(local[0], i + 2))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
+ return 0;
+ t[i] = NULL;
+
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 0))
+ return 0;
+ }
+
+ /* parallel startup */
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, OSSL_NELEM(t)), 1))
+ return 0;
+
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ local[i] = i + 1;
+ t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[i]);
+ if (!TEST_ptr(t[i]))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[i]), 1))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(retval[i], i + 1) || !TEST_int_eq(local[i], i + 2))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
+ return 0;
+ }
+
+ /* parallel startup, bottleneck */
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, OSSL_NELEM(t) - 1), 1))
+ return 0;
+
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ local[i] = i + 1;
+ t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[i]);
+ if (!TEST_ptr(t[i]))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[i]), 1))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(retval[i], i + 1) || !TEST_int_eq(local[i], i + 2))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
+ return 0;
+ }
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, 0), 1))
+ return 0;
+
+ OSSL_LIB_CTX_free(cust_ctx);
+ return 1;
+}
+#endif
+
+static uint32_t test_thread_native_multiple_joins_fn1(void *data)
+{
+ return 0;
+}
+
+static uint32_t test_thread_native_multiple_joins_fn2(void *data)
+{
+ ossl_crypto_thread_native_join((CRYPTO_THREAD *)data, NULL);
+ return 0;
+}
+
+static uint32_t test_thread_native_multiple_joins_fn3(void *data)
+{
+ ossl_crypto_thread_native_join((CRYPTO_THREAD *)data, NULL);
+ return 0;
+}
+
+static int test_thread_native_multiple_joins(void)
+{
+ CRYPTO_THREAD *t, *t1, *t2;
+
+ t = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn1, NULL, 1);
+ t1 = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn2, t, 1);
+ t2 = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn3, t, 1);
+
+ if (!TEST_ptr(t) || !TEST_ptr(t1) || !TEST_ptr(t2))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t2, NULL), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t1, NULL), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t2), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t1), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
+ return 0;
+
+ return 1;
+}
+
+#endif
+
typedef enum OPTION_choice {
OPT_ERR = -1,
OPT_EOF = 0,
@@ -816,6 +1140,15 @@ int setup_tests(void)
#if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK)
ADD_TEST(test_bio_dgram_pair);
#endif
+ ADD_TEST(test_thread_reported_flags);
+#if defined(OPENSSL_THREADS)
+ ADD_TEST(test_thread_native);
+ ADD_TEST(test_thread_native_multiple_joins);
+#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+ ADD_TEST(test_thread_internal);
+#endif
+#endif
+
return 1;
}