-/* crypto/async/async.c */
/*
* Written by Matt Caswell (matt@openssl.org) for the OpenSSL project.
*/
* ====================================================================
*/
-#include <openssl/crypto.h>
-#include <openssl/async.h>
-#include <string.h>
+/*
+ * Without this we start getting longjmp crashes because it thinks we're jumping
+ * up the stack when in fact we are jumping to an entirely different stack. The
+ * cost of this is not having certain buffer overrun/underrun checks etc for
+ * this source file :-(
+ */
+#undef _FORTIFY_SOURCE
+
+/* This must be the first #include file */
#include "async_locl.h"
+#include <internal/threads.h>
+#include <openssl/err.h>
+#include <internal/cryptlib_int.h>
+#include <string.h>
+
#define ASYNC_JOB_RUNNING 0
#define ASYNC_JOB_PAUSING 1
#define ASYNC_JOB_PAUSED 2
#define ASYNC_JOB_STOPPING 3
+static CRYPTO_THREAD_LOCAL ctxkey;
+static CRYPTO_THREAD_LOCAL poolkey;
+
+static void async_free_pool_internal(async_pool *pool);
-static ASYNC_CTX *ASYNC_CTX_new(void)
+static async_ctx *async_ctx_new(void)
{
- ASYNC_CTX *nctx = NULL;
+ async_ctx *nctx = NULL;
- if(!(nctx = OPENSSL_malloc(sizeof (ASYNC_CTX)))) {
- /* Error here */
+ nctx = OPENSSL_malloc(sizeof (async_ctx));
+ if (nctx == NULL) {
+ ASYNCerr(ASYNC_F_ASYNC_CTX_NEW, ERR_R_MALLOC_FAILURE);
goto err;
}
- ASYNC_FIBRE_init_dispatcher(&nctx->dispatcher);
+ async_fibre_init_dispatcher(&nctx->dispatcher);
nctx->currjob = NULL;
- if(!ASYNC_set_ctx(nctx))
+ nctx->blocked = 0;
+ if (!CRYPTO_THREAD_set_local(&ctxkey, nctx))
goto err;
return nctx;
err:
- if(nctx) {
- OPENSSL_free(nctx);
- }
+ OPENSSL_free(nctx);
return NULL;
}
-static int ASYNC_CTX_free(void)
+async_ctx *async_get_ctx(void)
{
- if(ASYNC_get_ctx()) {
- OPENSSL_free(ASYNC_get_ctx());
- }
+ if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL))
+ return NULL;
+
+ return (async_ctx *)CRYPTO_THREAD_get_local(&ctxkey);
+}
+
+static int async_ctx_free(void)
+{
+ async_ctx *ctx;
- if(!ASYNC_set_ctx(NULL))
+ ctx = async_get_ctx();
+
+ if (!CRYPTO_THREAD_set_local(&ctxkey, NULL))
return 0;
+ OPENSSL_free(ctx);
+
return 1;
}
-static ASYNC_JOB *ASYNC_JOB_new(void)
+static ASYNC_JOB *async_job_new(void)
{
ASYNC_JOB *job = NULL;
- if(!(job = OPENSSL_malloc(sizeof (ASYNC_JOB)))) {
+ job = OPENSSL_zalloc(sizeof (ASYNC_JOB));
+ if (job == NULL) {
+ ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ERR_R_MALLOC_FAILURE);
return NULL;
}
job->status = ASYNC_JOB_RUNNING;
- job->funcargs = NULL;
return job;
}
-static void ASYNC_JOB_free(ASYNC_JOB *job)
+static void async_job_free(ASYNC_JOB *job)
{
- if(job) {
- if(job->funcargs)
- OPENSSL_free(job->funcargs);
- ASYNC_FIBRE_free(&job->fibrectx);
+ if (job != NULL) {
+ OPENSSL_free(job->funcargs);
+ async_fibre_free(&job->fibrectx);
OPENSSL_free(job);
}
}
-void ASYNC_start_func(void)
-{
+static ASYNC_JOB *async_get_pool_job(void) {
ASYNC_JOB *job;
+ async_pool *pool;
- /* Run the job */
- job = ASYNC_get_ctx()->currjob;
- job->ret = job->func(job->funcargs);
-
- /* Stop the job */
- job->status = ASYNC_JOB_STOPPING;
- if(!ASYNC_FIBRE_swapcontext(&job->fibrectx,
- &ASYNC_get_ctx()->dispatcher, 0)) {
+ pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
+ if (pool == NULL) {
/*
- * Should not happen. Getting here will close the thread...can't do much
- * about it
+ * Pool has not been initialised, so init with the defaults, i.e.
+ * no max size and no pre-created jobs
*/
+ if (ASYNC_init_thread(0, 0) == 0)
+ return NULL;
+ pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
}
+
+ job = sk_ASYNC_JOB_pop(pool->jobs);
+ if (job == NULL) {
+ /* Pool is empty */
+ if ((pool->max_size != 0) && (pool->curr_size >= pool->max_size))
+ return NULL;
+
+ job = async_job_new();
+ if (job != NULL) {
+ if (! async_fibre_makecontext(&job->fibrectx)) {
+ async_job_free(job);
+ return NULL;
+ }
+ pool->curr_size++;
+ }
+ }
+ return job;
}
-int ASYNC_start_job(ASYNC_JOB **job, int *ret, int (*func)(void *),
- void *args, size_t size)
+static void async_release_job(ASYNC_JOB *job) {
+ async_pool *pool;
+
+ pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
+ OPENSSL_free(job->funcargs);
+ job->funcargs = NULL;
+ sk_ASYNC_JOB_push(pool->jobs, job);
+}
+
+void async_start_func(void)
+{
+ ASYNC_JOB *job;
+ async_ctx *ctx = async_get_ctx();
+
+ while (1) {
+ /* Run the job */
+ job = ctx->currjob;
+ job->ret = job->func(job->funcargs);
+
+ /* Stop the job */
+ job->status = ASYNC_JOB_STOPPING;
+ if (!async_fibre_swapcontext(&job->fibrectx,
+ &ctx->dispatcher, 1)) {
+ /*
+ * Should not happen. Getting here will close the thread...can't do
+ * much about it
+ */
+ ASYNCerr(ASYNC_F_ASYNC_START_FUNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
+ }
+ }
+}
+
+int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret,
+ int (*func)(void *), void *args, size_t size)
{
- if(ASYNC_get_ctx() || !ASYNC_CTX_new()) {
+ async_ctx *ctx = async_get_ctx();
+ if (ctx == NULL)
+ ctx = async_ctx_new();
+ if (ctx == NULL) {
return ASYNC_ERR;
}
- if(*job) {
- ASYNC_get_ctx()->currjob = *job;
+ if (*job) {
+ ctx->currjob = *job;
}
for (;;) {
- if(ASYNC_get_ctx()->currjob) {
- if(ASYNC_get_ctx()->currjob->status == ASYNC_JOB_STOPPING) {
- *ret = ASYNC_get_ctx()->currjob->ret;
- ASYNC_JOB_free(ASYNC_get_ctx()->currjob);
- ASYNC_get_ctx()->currjob = NULL;
- ASYNC_CTX_free();
+ if (ctx->currjob != NULL) {
+ if (ctx->currjob->status == ASYNC_JOB_STOPPING) {
+ *ret = ctx->currjob->ret;
+ ctx->currjob->waitctx = NULL;
+ async_release_job(ctx->currjob);
+ ctx->currjob = NULL;
+ *job = NULL;
return ASYNC_FINISH;
}
- if(ASYNC_get_ctx()->currjob->status == ASYNC_JOB_PAUSING) {
- *job = ASYNC_get_ctx()->currjob;
- ASYNC_get_ctx()->currjob->status = ASYNC_JOB_PAUSED;
- ASYNC_CTX_free();
+ if (ctx->currjob->status == ASYNC_JOB_PAUSING) {
+ *job = ctx->currjob;
+ ctx->currjob->status = ASYNC_JOB_PAUSED;
+ ctx->currjob = NULL;
return ASYNC_PAUSE;
}
- if(ASYNC_get_ctx()->currjob->status == ASYNC_JOB_PAUSED) {
- ASYNC_get_ctx()->currjob = *job;
+ if (ctx->currjob->status == ASYNC_JOB_PAUSED) {
+ ctx->currjob = *job;
/* Resume previous job */
- if(!ASYNC_FIBRE_swapcontext(&ASYNC_get_ctx()->dispatcher,
- &ASYNC_get_ctx()->currjob->fibrectx, 1))
+ if (!async_fibre_swapcontext(&ctx->dispatcher,
+ &ctx->currjob->fibrectx, 1)) {
+ ASYNCerr(ASYNC_F_ASYNC_START_JOB,
+ ASYNC_R_FAILED_TO_SWAP_CONTEXT);
goto err;
+ }
continue;
}
/* Should not happen */
- ASYNC_JOB_free(ASYNC_get_ctx()->currjob);
- ASYNC_get_ctx()->currjob = NULL;
- ASYNC_CTX_free();
+ ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_INTERNAL_ERROR);
+ async_release_job(ctx->currjob);
+ ctx->currjob = NULL;
+ *job = NULL;
return ASYNC_ERR;
}
/* Start a new job */
- if(!(ASYNC_get_ctx()->currjob = ASYNC_JOB_new())) {
- ASYNC_CTX_free();
- return ASYNC_ERR;
+ if ((ctx->currjob = async_get_pool_job()) == NULL) {
+ return ASYNC_NO_JOBS;
}
- if(args != NULL) {
- ASYNC_get_ctx()->currjob->funcargs = OPENSSL_malloc(size);
- if(!ASYNC_get_ctx()->currjob->funcargs) {
- ASYNC_JOB_free(ASYNC_get_ctx()->currjob);
- ASYNC_get_ctx()->currjob = NULL;
- ASYNC_CTX_free();
+ if (args != NULL) {
+ ctx->currjob->funcargs = OPENSSL_malloc(size);
+ if (ctx->currjob->funcargs == NULL) {
+ ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_MALLOC_FAILURE);
+ async_release_job(ctx->currjob);
+ ctx->currjob = NULL;
return ASYNC_ERR;
}
- memcpy(ASYNC_get_ctx()->currjob->funcargs, args, size);
+ memcpy(ctx->currjob->funcargs, args, size);
} else {
- ASYNC_get_ctx()->currjob->funcargs = NULL;
+ ctx->currjob->funcargs = NULL;
}
- ASYNC_get_ctx()->currjob->func = func;
- ASYNC_FIBRE_makecontext(&ASYNC_get_ctx()->currjob->fibrectx);
- if(!ASYNC_FIBRE_swapcontext(&ASYNC_get_ctx()->dispatcher,
- &ASYNC_get_ctx()->currjob->fibrectx, 1))
+ ctx->currjob->func = func;
+ ctx->currjob->waitctx = wctx;
+ if (!async_fibre_swapcontext(&ctx->dispatcher,
+ &ctx->currjob->fibrectx, 1)) {
+ ASYNCerr(ASYNC_F_ASYNC_START_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
goto err;
+ }
}
err:
- ASYNC_JOB_free(ASYNC_get_ctx()->currjob);
- ASYNC_get_ctx()->currjob = NULL;
- ASYNC_CTX_free();
+ async_release_job(ctx->currjob);
+ ctx->currjob = NULL;
+ *job = NULL;
return ASYNC_ERR;
}
-
int ASYNC_pause_job(void)
{
ASYNC_JOB *job;
+ async_ctx *ctx = async_get_ctx();
- if(!ASYNC_get_ctx() || !ASYNC_get_ctx()->currjob)
- return 0;
+ if (ctx == NULL
+ || ctx->currjob == NULL
+ || ctx->blocked) {
+ /*
+ * Could be we've deliberately not been started within a job so this is
+ * counted as success.
+ */
+ return 1;
+ }
- job = ASYNC_get_ctx()->currjob;
+ job = ctx->currjob;
job->status = ASYNC_JOB_PAUSING;
- if(!ASYNC_FIBRE_swapcontext(&job->fibrectx,
- &ASYNC_get_ctx()->dispatcher, 1)) {
- /* Error */
+ if (!async_fibre_swapcontext(&job->fibrectx,
+ &ctx->dispatcher, 1)) {
+ ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
return 0;
}
+ /* Reset counts of added and deleted fds */
+ async_wait_ctx_reset_counts(job->waitctx);
return 1;
}
-int ASYNC_in_job(void)
+static void async_empty_pool(async_pool *pool)
{
- if(ASYNC_get_ctx())
- return 1;
+ ASYNC_JOB *job;
+
+ if (!pool || !pool->jobs)
+ return;
+
+ do {
+ job = sk_ASYNC_JOB_pop(pool->jobs);
+ async_job_free(job);
+ } while (job);
+}
+
+int async_init(void)
+{
+ if (!CRYPTO_THREAD_init_local(&ctxkey, NULL))
+ return 0;
+
+ if (!CRYPTO_THREAD_init_local(&poolkey, NULL)) {
+ CRYPTO_THREAD_cleanup_local(&ctxkey);
+ return 0;
+ }
+ return 1;
+}
+
+void async_deinit(void)
+{
+ CRYPTO_THREAD_cleanup_local(&ctxkey);
+ CRYPTO_THREAD_cleanup_local(&poolkey);
+}
+
+int ASYNC_init_thread(size_t max_size, size_t init_size)
+{
+ async_pool *pool;
+ size_t curr_size = 0;
+
+ if (init_size > max_size) {
+ ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_INVALID_POOL_SIZE);
+ return 0;
+ }
+
+ if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) {
+ return 0;
+ }
+ if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC)) {
+ return 0;
+ }
+
+ pool = OPENSSL_zalloc(sizeof *pool);
+ if (pool == NULL) {
+ ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ pool->jobs = sk_ASYNC_JOB_new_null();
+ if (pool->jobs == NULL) {
+ ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(pool);
+ return 0;
+ }
+
+ pool->max_size = max_size;
+
+ /* Pre-create jobs as required */
+ while (init_size--) {
+ ASYNC_JOB *job;
+ job = async_job_new();
+ if (job == NULL || !async_fibre_makecontext(&job->fibrectx)) {
+ /*
+ * Not actually fatal because we already created the pool, just
+ * skip creation of any more jobs
+ */
+ async_job_free(job);
+ break;
+ }
+ job->funcargs = NULL;
+ sk_ASYNC_JOB_push(pool->jobs, job);
+ curr_size++;
+ }
+ pool->curr_size = curr_size;
+ if (!CRYPTO_THREAD_set_local(&poolkey, pool)) {
+ ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_FAILED_TO_SET_POOL);
+ goto err;
+ }
+
+ return 1;
+err:
+ async_free_pool_internal(pool);
return 0;
}
-int ASYNC_job_is_waiting(ASYNC_JOB *job)
+static void async_free_pool_internal(async_pool *pool)
+{
+ if (pool == NULL)
+ return;
+
+ async_empty_pool(pool);
+ sk_ASYNC_JOB_free(pool->jobs);
+ OPENSSL_free(pool);
+ CRYPTO_THREAD_set_local(&poolkey, NULL);
+ async_local_cleanup();
+ async_ctx_free();
+}
+
+void ASYNC_cleanup_thread(void)
+{
+ async_free_pool_internal((async_pool *)CRYPTO_THREAD_get_local(&poolkey));
+}
+
+ASYNC_JOB *ASYNC_get_current_job(void)
+{
+ async_ctx *ctx;
+
+ ctx = async_get_ctx();
+ if(ctx == NULL)
+ return NULL;
+
+ return ctx->currjob;
+}
+
+ASYNC_WAIT_CTX *ASYNC_get_wait_ctx(ASYNC_JOB *job)
+{
+ return job->waitctx;
+}
+
+void ASYNC_block_pause(void)
{
- return job->status == ASYNC_JOB_PAUSED;
+ async_ctx *ctx = async_get_ctx();
+ if (ctx == NULL || ctx->currjob == NULL) {
+ /*
+ * We're not in a job anyway so ignore this
+ */
+ return;
+ }
+ ctx->blocked++;
+}
+
+void ASYNC_unblock_pause(void)
+{
+ async_ctx *ctx = async_get_ctx();
+ if (ctx == NULL || ctx->currjob == NULL) {
+ /*
+ * We're not in a job anyway so ignore this
+ */
+ return;
+ }
+ if(ctx->blocked > 0)
+ ctx->blocked--;
}