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)
This commit is contained in:
Čestmír Kalina 2021-09-27 22:42:11 +02:00 committed by Matt Caswell
parent b137219749
commit 4574a7fd8d
20 changed files with 1572 additions and 21 deletions

View File

@ -655,6 +655,9 @@ my @disable_cascades = (
"fips" => [ "fips-securitychecks", "acvp-tests" ],
"threads" => [ "thread-pool" ],
"thread-pool" => [ "default-thread-pool" ],
"deprecated-3.0" => [ "engine", "srp" ]
);
@ -812,8 +815,6 @@ while (@argvcopy)
s /^-?-?shared$/enable-shared/;
s /^sctp$/enable-sctp/;
s /^threads$/enable-threads/;
s /^thread-pool$/enable-thread-pool/;
s /^default-thread-pool$/enable-default-thread-pool/;
s /^zlib$/enable-zlib/;
s /^zlib-dynamic$/enable-zlib-dynamic/;
s /^fips$/enable-fips/;
@ -1400,14 +1401,6 @@ if (grep { $_ =~ /(?:^|\s)-static(?:\s|$)/ } @{$config{LDFLAGS}}) {
disable('static', 'pic', 'threads');
}
if ($disabled{threads}) {
disable('unavailable', 'thread-pool');
}
if ($disabled{"thread-pool"}) {
disable('unavailable', 'default-thread-pool');
}
# Allow overriding the build file name
$config{build_file} = env('BUILDFILE') || $target{build_file} || "Makefile";
@ -1506,12 +1499,6 @@ foreach (grep /^-fsanitize=/, @{$config{CFLAGS} || []}) {
unless($disabled{threads}) {
push @{$config{openssl_feature_defines}}, "OPENSSL_THREADS";
}
unless($disabled{"thread-pool"}) {
push @{$config{openssl_feature_defines}}, "OPENSSL_THREAD_POOL";
}
unless($disabled{"default-thread-pool"}) {
push @{$config{openssl_feature_defines}}, "OPENSSL_DEFAULT_THREAD_POOL";
}
my $no_shared_warn=0;
if (($target{shared_target} // '') eq "")

View File

@ -899,6 +899,27 @@ will usually require additional system-dependent options!
See [Notes on multi-threading](#notes-on-multi-threading) below.
### no-thread-pool
Don't build with support for thread pool functionality.
### thread-pool
Build with thread pool functionality. If enabled, OpenSSL algorithms may
use the thread pool to perform parallel computation. This option in itself
does not enable OpenSSL to spawn new threads. Currently the only supported
thread pool mechanism is the default thread pool.
### no-default-thread-pool
Don't build with support for default thread pool functionality.
### default-thread-pool
Build with default thread pool functionality. If enabled, OpenSSL may create
and manage threads up to a maximum number of threads authorized by the
application. Supported on POSIX compliant platforms and Windows.
### enable-trace
Build with support for the integrated tracing api.

View File

@ -6,7 +6,7 @@ SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 conf \
siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \
seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \
err comp http ocsp cms ts srp cmac ct async ess crmf cmp encode_decode \
ffc hpke
ffc hpke thread
LIBS=../libcrypto

View File

@ -36,6 +36,9 @@ struct ossl_lib_ctx_st {
OSSL_METHOD_STORE *encoder_store;
OSSL_METHOD_STORE *store_loader_store;
void *self_test_cb;
#endif
#if defined(OPENSSL_THREADS)
void *threads;
#endif
void *rand_crngt;
#ifdef FIPS_MODULE
@ -171,6 +174,12 @@ static int context_init(OSSL_LIB_CTX *ctx)
goto err;
#endif
#if defined(OPENSSL_THREADS)
ctx->threads = ossl_threads_ctx_new(ctx);
if (ctx->threads == NULL)
goto err;
#endif
/* Low priority. */
#ifndef FIPS_MODULE
ctx->child_provider = ossl_child_prov_ctx_new(ctx);
@ -299,6 +308,13 @@ static void context_deinit_objs(OSSL_LIB_CTX *ctx)
}
#endif
#if defined(OPENSSL_THREADS)
if (ctx->threads != NULL) {
ossl_threads_ctx_free(ctx->threads);
ctx->threads = NULL;
}
#endif
/* Low priority. */
#ifndef FIPS_MODULE
if (ctx->child_provider != NULL) {
@ -526,6 +542,10 @@ void *ossl_lib_ctx_get_data(OSSL_LIB_CTX *ctx, int index)
case OSSL_LIB_CTX_SELF_TEST_CB_INDEX:
return ctx->self_test_cb;
#endif
#if defined(OPENSSL_THREADS)
case OSSL_LIB_CTX_THREAD_INDEX:
return ctx->threads;
#endif
case OSSL_LIB_CTX_RAND_CRNGT_INDEX: {

73
crypto/thread/api.c Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/configuration.h>
#include <openssl/thread.h>
#include <internal/thread.h>
uint32_t OSSL_get_thread_support_flags(void)
{
int support = 0;
#if !defined(OPENSSL_NO_THREAD_POOL)
support |= OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL;
#endif
#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
support |= OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN;
#endif
return support;
}
#if defined(OPENSSL_NO_THREAD_POOL) || defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads)
{
return 0;
}
uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx)
{
return 0;
}
#else
uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx)
{
uint64_t ret = 0;
OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
if (tdata == NULL)
goto fail;
ossl_crypto_mutex_lock(tdata->lock);
ret = tdata->max_threads;
ossl_crypto_mutex_unlock(tdata->lock);
fail:
return ret;
}
int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads)
{
OSSL_LIB_CTX_THREADS *tdata;
tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
if (tdata == NULL)
return 0;
ossl_crypto_mutex_lock(tdata->lock);
tdata->max_threads = max_threads;
ossl_crypto_mutex_unlock(tdata->lock);
return 1;
}
#endif

91
crypto/thread/arch.c Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/configuration.h>
#include <internal/thread_arch.h>
#if defined(OPENSSL_THREADS)
CRYPTO_THREAD *ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine,
void *data, int joinable)
{
CRYPTO_THREAD *handle;
if (routine == NULL)
return NULL;
handle = OPENSSL_zalloc(sizeof(*handle));
if (handle == NULL)
return NULL;
if ((handle->lock = ossl_crypto_mutex_new()) == NULL)
goto fail;
if ((handle->statelock = ossl_crypto_mutex_new()) == NULL)
goto fail;
if ((handle->condvar = ossl_crypto_condvar_new()) == NULL)
goto fail;
handle->data = data;
handle->routine = routine;
handle->joinable = joinable;
if (ossl_crypto_thread_native_spawn(handle) == 1)
return handle;
fail:
ossl_crypto_condvar_free(&handle->condvar);
ossl_crypto_mutex_free(&handle->statelock);
ossl_crypto_mutex_free(&handle->lock);
OPENSSL_free(handle);
return NULL;
}
int ossl_crypto_thread_native_clean(CRYPTO_THREAD *handle)
{
uint64_t req_state_mask;
if (handle == NULL)
return 0;
req_state_mask = 0;
req_state_mask |= CRYPTO_THREAD_FINISHED;
req_state_mask |= CRYPTO_THREAD_TERMINATED;
req_state_mask |= CRYPTO_THREAD_JOINED;
ossl_crypto_mutex_lock(handle->statelock);
if (CRYPTO_THREAD_GET_STATE(handle, req_state_mask) == 0) {
ossl_crypto_mutex_unlock(handle->statelock);
return 0;
}
ossl_crypto_mutex_unlock(handle->statelock);
ossl_crypto_mutex_free(&handle->lock);
ossl_crypto_mutex_free(&handle->statelock);
ossl_crypto_condvar_free(&handle->condvar);
OPENSSL_free(handle->handle);
OPENSSL_free(handle);
return 1;
}
#else
CRYPTO_THREAD *ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine,
void *data, int joinable)
{
return NULL;
}
int ossl_crypto_thread_native_clean(CRYPTO_THREAD *handle)
{
return 0;
}
#endif

View File

@ -0,0 +1,82 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <internal/thread_arch.h>
#if defined(OPENSSL_THREADS_NONE)
int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread)
{
return 0;
}
int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
{
return 0;
}
int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread)
{
return 0;
}
int ossl_crypto_thread_native_exit(void)
{
return 0;
}
int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
{
return 0;
}
CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
{
return NULL;
}
void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
{
}
int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
{
return 0;
}
void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
{
}
void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
{
}
CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
{
return NULL;
}
void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
{
}
void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
{
}
void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
{
}
void ossl_crypto_mem_barrier(void)
{
}
#endif

View File

@ -0,0 +1,305 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <internal/thread_arch.h>
#if defined(OPENSSL_THREADS_POSIX)
# define _GNU_SOURCE
# include <errno.h>
# include <sys/types.h>
# include <unistd.h>
static void *thread_start_thunk(void *vthread)
{
CRYPTO_THREAD *thread;
CRYPTO_THREAD_RETVAL ret;
thread = (CRYPTO_THREAD *)vthread;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
ret = thread->routine(thread->data);
ossl_crypto_mutex_lock(thread->statelock);
CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED);
thread->retval = ret;
ossl_crypto_condvar_broadcast(thread->condvar);
ossl_crypto_mutex_unlock(thread->statelock);
return NULL;
}
int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread)
{
int ret;
pthread_attr_t attr;
pthread_t *handle;
handle = OPENSSL_zalloc(sizeof(*handle));
if (handle == NULL)
goto fail;
pthread_attr_init(&attr);
if (!thread->joinable)
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
ret = pthread_create(handle, &attr, thread_start_thunk, thread);
pthread_attr_destroy(&attr);
if (ret != 0)
goto fail;
thread->handle = handle;
return 1;
fail:
thread->handle = NULL;
OPENSSL_free(handle);
return 0;
}
int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
{
void *thread_retval;
pthread_t *handle;
uint64_t req_state_mask;
if (thread == NULL)
return 0;
req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_JOINED;
ossl_crypto_mutex_lock(thread->statelock);
if (CRYPTO_THREAD_GET_STATE(thread, req_state_mask)) {
ossl_crypto_mutex_unlock(thread->statelock);
goto pass;
}
while (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_FINISHED))
ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
ossl_crypto_mutex_unlock(thread->statelock);
handle = (pthread_t *) thread->handle;
if (handle == NULL)
goto fail;
if (pthread_join(*handle, &thread_retval) != 0)
goto fail;
/*
* Join return value may be non-NULL when the thread has been cancelled,
* as indicated by thread_retval set to PTHREAD_CANCELLED.
*/
if (thread_retval != NULL)
goto fail;
pass:
if (retval != NULL)
*retval = thread->retval;
ossl_crypto_mutex_lock(thread->statelock);
CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED);
CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED);
ossl_crypto_mutex_unlock(thread->statelock);
return 1;
fail:
ossl_crypto_mutex_lock(thread->statelock);
CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED);
ossl_crypto_mutex_unlock(thread->statelock);
return 0;
}
int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread)
{
void *res;
uint64_t mask;
pthread_t *handle;
mask = CRYPTO_THREAD_FINISHED;
mask |= CRYPTO_THREAD_TERMINATED;
mask |= CRYPTO_THREAD_JOINED;
if (thread == NULL)
return 0;
ossl_crypto_mutex_lock(thread->statelock);
if (thread->handle == NULL || CRYPTO_THREAD_GET_STATE(thread, mask))
goto terminated;
ossl_crypto_mutex_unlock(thread->statelock);
handle = thread->handle;
if (pthread_cancel(*handle) != 0) {
ossl_crypto_mutex_lock(thread->statelock);
CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
ossl_crypto_mutex_unlock(thread->statelock);
return 0;
}
if (pthread_join(*handle, &res) != 0)
return 0;
if (res != PTHREAD_CANCELED)
return 0;
thread->handle = NULL;
OPENSSL_free(handle);
ossl_crypto_mutex_lock(thread->statelock);
terminated:
CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_TERMINATED);
ossl_crypto_mutex_unlock(thread->statelock);
return 1;
}
int ossl_crypto_thread_native_exit(void)
{
pthread_exit(NULL);
return 1;
}
int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
{
return pthread_equal(*(pthread_t *)thread->handle, pthread_self());
}
CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
{
pthread_mutex_t *mutex;
if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL)
return NULL;
if (pthread_mutex_init(mutex, NULL) != 0) {
OPENSSL_free(mutex);
return NULL;
}
return (CRYPTO_MUTEX *)mutex;
}
int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
{
pthread_mutex_t *mutex_p;
mutex_p = (pthread_mutex_t *)mutex;
if (pthread_mutex_trylock(mutex_p) == EBUSY)
return 0;
return 1;
}
void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
{
pthread_mutex_t *mutex_p;
mutex_p = (pthread_mutex_t *)mutex;
pthread_mutex_lock(mutex_p);
}
void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
{
pthread_mutex_t *mutex_p;
mutex_p = (pthread_mutex_t *)mutex;
pthread_mutex_unlock(mutex_p);
}
void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
{
pthread_mutex_t **mutex_p;
if (mutex == NULL)
return;
mutex_p = (pthread_mutex_t **)mutex;
if (*mutex_p != NULL)
pthread_mutex_destroy(*mutex_p);
OPENSSL_free(*mutex_p);
*mutex = NULL;
}
CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
{
pthread_cond_t *cv_p;
if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL)
return NULL;
if (pthread_cond_init(cv_p, NULL) != 0) {
OPENSSL_free(cv_p);
return NULL;
}
return (CRYPTO_CONDVAR *) cv_p;
}
void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
{
pthread_cond_t *cv_p;
pthread_mutex_t *mutex_p;
cv_p = (pthread_cond_t *)cv;
mutex_p = (pthread_mutex_t *)mutex;
pthread_cond_wait(cv_p, mutex_p);
}
void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
{
pthread_cond_t *cv_p;
cv_p = (pthread_cond_t *)cv;
pthread_cond_broadcast(cv_p);
}
void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
{
pthread_cond_t **cv_p;
if (cv == NULL)
return;
cv_p = (pthread_cond_t **)cv;
if (*cv_p != NULL)
pthread_cond_destroy(*cv_p);
OPENSSL_free(*cv_p);
*cv_p = NULL;
}
void ossl_crypto_mem_barrier(void)
{
# if defined(__clang__) || defined(__GNUC__)
__sync_synchronize();
# elif !defined(OPENSSL_NO_ASM)
# if defined(__alpha__) /* Alpha */
__asm__ volatile("mb" : : : "memory");
# elif defined(__amd64__) || defined(__i386__) || defined(__i486__) \
|| defined(__i586__) || defined(__i686__) || defined(__i386) /* x86 */
__asm__ volatile("mfence" : : : "memory");
# elif defined(__arm__) || defined(__aarch64__) /* ARMv7, ARMv8 */
__asm__ volatile("dmb ish" : : : "memory");
# elif defined(__hppa__) /* PARISC */
__asm__ volatile("" : : : "memory");
# elif defined(__mips__) /* MIPS */
__asm__ volatile("sync" : : : "memory");
# elif defined(__powerpc__) || defined(__powerpc64__) /* power, ppc64, ppc64le */
__asm__ volatile("sync" : : : "memory");
# elif defined(__sparc__)
__asm__ volatile("ba,pt %%xcc, 1f\n\t" \
" membar #Sync\n" \
"1:\n" \
: : : "memory");
# elif defined(__s390__) || defined(__s390x__) /* z */
__asm__ volatile("bcr 15,0" : : : "memory");
# elif defined(__riscv) || defined(__riscv__) /* riscv */
__asm__ volatile("fence iorw,iorw" : : : "memory");
# else /* others, compiler only */
__asm__ volatile("" : : : "memory");
# endif
# else
/* compiler only barrier */
__asm__ volatile("" : : : "memory");
# endif
}
#endif

View File

@ -0,0 +1,254 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <internal/thread_arch.h>
#if defined(OPENSSL_THREADS_WINNT)
# include <process.h>
# include <windows.h>
static DWORD __stdcall thread_start_thunk(LPVOID vthread)
{
CRYPTO_THREAD *thread;
CRYPTO_THREAD_RETVAL ret;
thread = (CRYPTO_THREAD *)vthread;
thread->thread_id = GetCurrentThreadId();
ret = thread->routine(thread->data);
ossl_crypto_mutex_lock(thread->statelock);
CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED);
thread->retval = ret;
ossl_crypto_condvar_broadcast(thread->condvar);
ossl_crypto_mutex_unlock(thread->statelock);
return 0;
}
int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread)
{
HANDLE *handle;
handle = OPENSSL_zalloc(sizeof(*handle));
if (handle == NULL)
goto fail;
*handle = (HANDLE)_beginthreadex(NULL, 0, &thread_start_thunk, thread, 0, NULL);
if (*handle == NULL)
goto fail;
thread->handle = handle;
return 1;
fail:
thread->handle = NULL;
OPENSSL_free(handle);
return 0;
}
int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
{
int req_state_mask;
DWORD thread_retval;
HANDLE *handle;
if (thread == NULL)
return 0;
req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_JOINED;
ossl_crypto_mutex_lock(thread->statelock);
if (CRYPTO_THREAD_GET_STATE(thread, req_state_mask))
goto pass;
while (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_FINISHED))
ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
handle = (HANDLE *) thread->handle;
if (handle == NULL)
goto fail;
if (WaitForSingleObject(*handle, INFINITE) != WAIT_OBJECT_0)
goto fail;
if (GetExitCodeThread(*handle, &thread_retval) == 0)
goto fail;
/*
* GetExitCodeThread call followed by this check is to make sure that
* the thread exitted properly. In particular, thread_retval may be
* non-zero when exitted via explicit ExitThread/TerminateThread or
* if the thread is still active (returns STILL_ACTIVE (259)).
*/
if (thread_retval != 0)
goto fail;
if (CloseHandle(*handle) == 0)
goto fail;
pass:
if (retval != NULL)
*retval = thread->retval;
CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED);
CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED);
ossl_crypto_mutex_unlock(thread->statelock);
return 1;
fail:
CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED);
ossl_crypto_mutex_unlock(thread->statelock);
return 0;
}
int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread)
{
uint64_t mask;
HANDLE *handle;
mask = CRYPTO_THREAD_FINISHED;
mask |= CRYPTO_THREAD_TERMINATED;
mask |= CRYPTO_THREAD_JOINED;
if (thread == NULL)
return 1;
ossl_crypto_mutex_lock(thread->statelock);
if (thread->handle == NULL || CRYPTO_THREAD_GET_STATE(thread, mask))
goto terminated;
ossl_crypto_mutex_unlock(thread->statelock);
handle = thread->handle;
if (WaitForSingleObject(*handle, 0) != WAIT_OBJECT_0) {
if (TerminateThread(*handle, STILL_ACTIVE) == 0) {
ossl_crypto_mutex_lock(thread->statelock);
CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
ossl_crypto_mutex_unlock(thread->statelock);
return 0;
}
}
if (CloseHandle(*handle) == 0) {
CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
return 0;
}
thread->handle = NULL;
OPENSSL_free(handle);
ossl_crypto_mutex_lock(thread->statelock);
terminated:
CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_TERMINATED);
ossl_crypto_mutex_unlock(thread->statelock);
return 1;
}
int ossl_crypto_thread_native_exit(void)
{
_endthreadex(0);
return 1;
}
int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
{
return thread->thread_id == GetCurrentThreadId();
}
CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
{
CRITICAL_SECTION *mutex;
if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL)
return NULL;
InitializeCriticalSection(mutex);
return (CRYPTO_MUTEX *)mutex;
}
void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
{
CRITICAL_SECTION *mutex_p;
mutex_p = (CRITICAL_SECTION *)mutex;
EnterCriticalSection(mutex_p);
}
int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
{
CRITICAL_SECTION *mutex_p;
mutex_p = (CRITICAL_SECTION *)mutex;
if (TryEnterCriticalSection(mutex_p))
return 1;
return 0;
}
void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
{
CRITICAL_SECTION *mutex_p;
mutex_p = (CRITICAL_SECTION *)mutex;
LeaveCriticalSection(mutex_p);
}
void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
{
CRITICAL_SECTION **mutex_p;
mutex_p = (CRITICAL_SECTION **)mutex;
if (*mutex_p != NULL)
DeleteCriticalSection(*mutex_p);
OPENSSL_free(*mutex_p);
*mutex = NULL;
}
CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
{
CONDITION_VARIABLE *cv_p;
if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL)
return NULL;
InitializeConditionVariable(cv_p);
return (CRYPTO_CONDVAR *)cv_p;
}
void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
{
CONDITION_VARIABLE *cv_p;
CRITICAL_SECTION *mutex_p;
cv_p = (CONDITION_VARIABLE *)cv;
mutex_p = (CRITICAL_SECTION *)mutex;
SleepConditionVariableCS(cv_p, mutex_p, INFINITE);
}
void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
{
CONDITION_VARIABLE *cv_p;
cv_p = (CONDITION_VARIABLE *)cv;
WakeAllConditionVariable(cv_p);
}
void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
{
CONDITION_VARIABLE **cv_p;
cv_p = (CONDITION_VARIABLE **)cv;
OPENSSL_free(*cv_p);
*cv_p = NULL;
}
void ossl_crypto_mem_barrier(void)
{
MemoryBarrier();
}
#endif

8
crypto/thread/build.info Normal file
View File

@ -0,0 +1,8 @@
LIBS=../../libcrypto
$THREADS=\
api.c internal.c arch.c \
arch/thread_win.c arch/thread_posix.c arch/thread_none.c
SOURCE[../../libcrypto]=$THREADS
SOURCE[../../providers/libfips.a]=$THREADS

161
crypto/thread/internal.c Normal file
View File

@ -0,0 +1,161 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/configuration.h>
#include <openssl/e_os2.h>
#include <openssl/types.h>
#include <openssl/crypto.h>
#include <internal/thread.h>
#include <internal/thread_arch.h>
#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
static ossl_inline uint64_t _ossl_get_avail_threads(OSSL_LIB_CTX_THREADS *tdata)
{
/* assumes that tdata->lock is taken */
return tdata->max_threads - tdata->active_threads;
}
uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx)
{
uint64_t retval = 0;
OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
if (tdata == NULL)
return retval;
ossl_crypto_mutex_lock(tdata->lock);
retval = _ossl_get_avail_threads(tdata);
ossl_crypto_mutex_unlock(tdata->lock);
return retval;
}
void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
void *data)
{
CRYPTO_THREAD *thread;
OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
if (tdata == NULL)
return NULL;
ossl_crypto_mutex_lock(tdata->lock);
if (tdata == NULL || tdata->max_threads == 0) {
ossl_crypto_mutex_unlock(tdata->lock);
return NULL;
}
while (_ossl_get_avail_threads(tdata) == 0)
ossl_crypto_condvar_wait(tdata->cond_finished, tdata->lock);
tdata->active_threads++;
ossl_crypto_mutex_unlock(tdata->lock);
thread = ossl_crypto_thread_native_start(start, data, 1);
if (thread == NULL) {
ossl_crypto_mutex_lock(tdata->lock);
tdata->active_threads--;
ossl_crypto_mutex_unlock(tdata->lock);
goto fail;
}
thread->ctx = ctx;
fail:
return (void *) thread;
}
int ossl_crypto_thread_join(void *vhandle, CRYPTO_THREAD_RETVAL *retval)
{
CRYPTO_THREAD *handle = vhandle;
OSSL_LIB_CTX_THREADS *tdata;
if (vhandle == NULL)
return 0;
tdata = OSSL_LIB_CTX_GET_THREADS(handle->ctx);
if (tdata == NULL)
return 0;
if (ossl_crypto_thread_native_join(handle, retval) == 0)
return 0;
ossl_crypto_mutex_lock(tdata->lock);
tdata->active_threads--;
ossl_crypto_condvar_broadcast(tdata->cond_finished);
ossl_crypto_mutex_unlock(tdata->lock);
return 1;
}
int ossl_crypto_thread_clean(void *vhandle)
{
CRYPTO_THREAD *handle = vhandle;
return ossl_crypto_thread_native_clean(handle);
}
#else
ossl_inline uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx)
{
return 0;
}
void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
void *data)
{
return NULL;
}
int ossl_crypto_thread_join(void *vhandle, CRYPTO_THREAD_RETVAL *retval)
{
return 0;
}
int ossl_crypto_thread_clean(void *vhandle)
{
return 0;
}
#endif
#if defined(OPENSSL_THREADS)
void *ossl_threads_ctx_new(OSSL_LIB_CTX *ctx)
{
struct openssl_threads_st *t = OPENSSL_zalloc(sizeof(*t));
if (t == NULL)
return NULL;
t->lock = ossl_crypto_mutex_new();
t->cond_finished = ossl_crypto_condvar_new();
if (t->lock == NULL || t->cond_finished == NULL)
goto fail;
return t;
fail:
ossl_threads_ctx_free((void *)t);
return NULL;
}
void ossl_threads_ctx_free(void *vdata)
{
OSSL_LIB_CTX_THREADS *t = (OSSL_LIB_CTX_THREADS *) vdata;
if (t == NULL)
return;
ossl_crypto_mutex_free(&t->lock);
ossl_crypto_condvar_free(&t->cond_finished);
OPENSSL_free(t);
}
#endif

View File

@ -5,7 +5,9 @@
CRYPTO_THREAD_run_once,
CRYPTO_THREAD_lock_new, CRYPTO_THREAD_read_lock, CRYPTO_THREAD_write_lock,
CRYPTO_THREAD_unlock, CRYPTO_THREAD_lock_free,
CRYPTO_atomic_add, CRYPTO_atomic_or, CRYPTO_atomic_load - OpenSSL thread support
CRYPTO_atomic_add, CRYPTO_atomic_or, CRYPTO_atomic_load,
OSSL_set_max_threads, OSSL_get_max_threads,
OSSL_get_thread_support_flags - OpenSSL thread support
=head1 SYNOPSIS
@ -25,6 +27,10 @@ CRYPTO_atomic_add, CRYPTO_atomic_or, CRYPTO_atomic_load - OpenSSL thread support
CRYPTO_RWLOCK *lock);
int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock);
int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads);
uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx);
uint32_t OSSL_get_thread_support_flags(void);
=head1 DESCRIPTION
OpenSSL can be safely used in multi-threaded applications provided that
@ -98,6 +104,16 @@ read by CRYPTO_atomic_load() then CRYPTO_atomic_load() must be the only way that
the variable is read. If atomic operations are not supported and I<lock> is
NULL, then the function will fail.
=item *
OSSL_set_max_threads() sets the maximum number of threads to be used by the
thread pool. If the argument is 0, thread pooling is disabled. OpenSSL will
not create any threads and existing threads in the thread pool will be torn
down. The maximum thread count is a limit, not a target. Threads will not be
spawned unless (and until) there is demand. Thread polling is disabled by
default. To enable threading you must call OSSL_set_max_threads() explicitly.
Under no circumstances is this done for you.
=back
=head1 RETURN VALUES
@ -108,6 +124,15 @@ CRYPTO_THREAD_lock_new() returns the allocated lock, or NULL on error.
CRYPTO_THREAD_lock_free() returns no value.
OSSL_set_max_threads() returns 1 on success and 0 on failure. Returns failure
if OpenSSL-managed thread pooling is not supported (for example, if it is not
supported on the current platform, or because OpenSSL is not built with the
necessary support).
OSSL_get_max_threads() returns the maximum number of threads currently allowed
to be used by the thread pool. If thread pooling is disabled or not available,
returns 0.
The other functions return 1 on success, or 0 on error.
=head1 NOTES

View File

@ -23,6 +23,9 @@ void *ossl_self_test_set_callback_new(OSSL_LIB_CTX *);
void *ossl_rand_crng_ctx_new(OSSL_LIB_CTX *);
void *ossl_thread_event_ctx_new(OSSL_LIB_CTX *);
void *ossl_fips_prov_ossl_ctx_new(OSSL_LIB_CTX *);
#if defined(OPENSSL_THREADS)
void *ossl_threads_ctx_new(OSSL_LIB_CTX *);
#endif
void ossl_provider_store_free(void *);
void ossl_property_string_data_free(void *);
@ -38,3 +41,6 @@ void ossl_self_test_set_callback_free(void *);
void ossl_rand_crng_ctx_free(void *);
void ossl_thread_event_ctx_free(void *);
void ossl_fips_prov_ossl_ctx_free(void *);
#if defined(OPENSSL_THREADS)
void ossl_threads_ctx_free(void *);
#endif

View File

@ -116,7 +116,8 @@ typedef struct ossl_ex_data_global_st {
# define OSSL_LIB_CTX_PROVIDER_CONF_INDEX 16
# define OSSL_LIB_CTX_BIO_CORE_INDEX 17
# define OSSL_LIB_CTX_CHILD_PROVIDER_INDEX 18
# define OSSL_LIB_CTX_MAX_INDEXES 19
# define OSSL_LIB_CTX_THREAD_INDEX 19
# define OSSL_LIB_CTX_MAX_INDEXES 20
OSSL_LIB_CTX *ossl_lib_ctx_get_concrete(OSSL_LIB_CTX *ctx);
int ossl_lib_ctx_is_default(OSSL_LIB_CTX *ctx);

39
include/internal/thread.h Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OPENSSL_INTERNAL_THREAD_H
# define OPENSSL_INTERNAL_THREAD_H
# include <openssl/configuration.h>
# include <internal/thread_arch.h>
# include <openssl/e_os2.h>
# include <openssl/types.h>
# include <internal/cryptlib.h>
# include "crypto/context.h"
void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
void *data);
int ossl_crypto_thread_join(void *task, CRYPTO_THREAD_RETVAL *retval);
int ossl_crypto_thread_clean(void *vhandle);
uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx);
# if defined(OPENSSL_THREADS)
# define OSSL_LIB_CTX_GET_THREADS(CTX) \
ossl_lib_ctx_get_data(CTX, OSSL_LIB_CTX_THREAD_INDEX);
typedef struct openssl_threads_st {
uint64_t max_threads;
uint64_t active_threads;
CRYPTO_MUTEX *lock;
CRYPTO_CONDVAR *cond_finished;
} OSSL_LIB_CTX_THREADS;
# endif /* defined(OPENSSL_THREADS) */
#endif /* OPENSSL_INTERNAL_THREAD_H */

View File

@ -0,0 +1,119 @@
/*
* Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_INTERNAL_THREAD_ARCH_H
# define OSSL_INTERNAL_THREAD_ARCH_H
# include <openssl/configuration.h>
# include <openssl/e_os2.h>
# if defined(_WIN32)
# include <windows.h>
# endif
# if defined(OPENSSL_THREADS) && defined(OPENSSL_SYS_UNIX)
# define OPENSSL_THREADS_POSIX
# elif defined(OPENSSL_THREADS) && defined(OPENSSL_SYS_WINDOWS) && \
defined(_WIN32_WINNT)
# if _WIN32_WINNT >= 0x0600
# define OPENSSL_THREADS_WINNT
# else
# define OPENSSL_THREADS_NONE
# endif
# else
# define OPENSSL_THREADS_NONE
# endif
# include <openssl/crypto.h>
typedef void CRYPTO_MUTEX;
typedef void CRYPTO_CONDVAR;
CRYPTO_MUTEX *ossl_crypto_mutex_new(void);
void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex);
int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex);
void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex);
void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex);
CRYPTO_CONDVAR *ossl_crypto_condvar_new(void);
void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex);
void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv);
void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv);
typedef uint32_t CRYPTO_THREAD_RETVAL;
typedef CRYPTO_THREAD_RETVAL (*CRYPTO_THREAD_ROUTINE)(void *);
typedef CRYPTO_THREAD_RETVAL (*CRYPTO_THREAD_ROUTINE_CB)(void *,
void (**)(void *),
void **);
# define CRYPTO_THREAD_NO_STATE 0UL
# define CRYPTO_THREAD_FINISHED (1UL << 1)
# define CRYPTO_THREAD_JOINED (1UL << 2)
# define CRYPTO_THREAD_TERMINATED (1UL << 3)
# define CRYPTO_THREAD_GET_STATE(THREAD, FLAG) ((THREAD)->state & (FLAG))
# define CRYPTO_THREAD_GET_ERROR(THREAD, FLAG) (((THREAD)->state >> 16) & (FLAG))
typedef struct crypto_thread_st {
uint32_t state;
void *data;
CRYPTO_THREAD_ROUTINE routine;
CRYPTO_THREAD_RETVAL retval;
void *handle;
CRYPTO_MUTEX *lock;
CRYPTO_MUTEX *statelock;
CRYPTO_CONDVAR *condvar;
unsigned long thread_id;
int joinable;
OSSL_LIB_CTX *ctx;
} CRYPTO_THREAD;
# if defined(OPENSSL_THREADS)
# define CRYPTO_THREAD_UNSET_STATE(THREAD, FLAG) \
do { \
(THREAD)->state &= ~(FLAG); \
} while ((void)0, 0)
# define CRYPTO_THREAD_SET_STATE(THREAD, FLAG) \
do { \
(THREAD)->state |= (FLAG); \
} while ((void)0, 0)
# define CRYPTO_THREAD_SET_ERROR(THREAD, FLAG) \
do { \
(THREAD)->state |= ((FLAG) << 16); \
} while ((void)0, 0)
# define CRYPTO_THREAD_UNSET_ERROR(THREAD, FLAG) \
do { \
(THREAD)->state &= ~((FLAG) << 16); \
} while ((void)0, 0)
# else
# define CRYPTO_THREAD_UNSET_STATE(THREAD, FLAG)
# define CRYPTO_THREAD_SET_STATE(THREAD, FLAG)
# define CRYPTO_THREAD_SET_ERROR(THREAD, FLAG)
# define CRYPTO_THREAD_UNSET_ERROR(THREAD, FLAG)
# endif /* defined(OPENSSL_THREADS) */
CRYPTO_THREAD * ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine,
void *data, int joinable);
int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread);
int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread,
CRYPTO_THREAD_RETVAL *retval);
int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread);
int ossl_crypto_thread_native_exit(void);
int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread);
int ossl_crypto_thread_native_clean(CRYPTO_THREAD *thread);
void ossl_crypto_mem_barrier(void);
#endif /* OSSL_INTERNAL_THREAD_ARCH_H */

23
include/openssl/thread.h Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OPENSSL_THREAD_H
# define OPENSSL_THREAD_H
# define OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL (1U<<0)
# define OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN (1U<<1)
# include <openssl/types.h>
uint32_t OSSL_get_thread_support_flags(void);
int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads);
uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx);
#endif /* OPENSSL_THREAD_H */

View File

@ -340,8 +340,8 @@ IF[{- !$disabled{tests} -}]
DEPEND[ct_test]=../libcrypto libtestutil.a
SOURCE[threadstest]=threadstest.c
INCLUDE[threadstest]=../include ../apps/include
DEPEND[threadstest]=../libcrypto libtestutil.a
INCLUDE[threadstest]=.. ../include ../apps/include
DEPEND[threadstest]=../libcrypto.a libtestutil.a
SOURCE[threadstest_fips]=threadstest_fips.c
INCLUDE[threadstest_fips]=../include ../apps/include

View File

@ -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;
}

View File

@ -5466,3 +5466,6 @@ EVP_PKEY_auth_decapsulate_init ? 3_2_0 EXIST::FUNCTION:
PKCS12_SAFEBAG_set0_attrs ? 3_2_0 EXIST::FUNCTION:
PKCS12_create_ex2 ? 3_2_0 EXIST::FUNCTION:
OSSL_sleep ? 3_2_0 EXIST::FUNCTION:
OSSL_get_thread_support_flags ? 3_2_0 EXIST::FUNCTION:
OSSL_set_max_threads ? 3_2_0 EXIST::FUNCTION:
OSSL_get_max_threads ? 3_2_0 EXIST::FUNCTION: