2 * Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
11 * Without this we start getting longjmp crashes because it thinks we're jumping
12 * up the stack when in fact we are jumping to an entirely different stack. The
13 * cost of this is not having certain buffer overrun/underrun checks etc for
14 * this source file :-(
16 #undef _FORTIFY_SOURCE
18 /* This must be the first #include file */
19 #include "async_locl.h"
21 #include <openssl/err.h>
22 #include "internal/cryptlib_int.h"
25 #define ASYNC_JOB_RUNNING 0
26 #define ASYNC_JOB_PAUSING 1
27 #define ASYNC_JOB_PAUSED 2
28 #define ASYNC_JOB_STOPPING 3
30 static CRYPTO_THREAD_LOCAL ctxkey
;
31 static CRYPTO_THREAD_LOCAL poolkey
;
33 static void async_delete_thread_state(void *arg
);
35 static async_ctx
*async_ctx_new(void)
39 if (!ossl_init_thread_start(NULL
, async_delete_thread_state
))
42 nctx
= OPENSSL_malloc(sizeof(*nctx
));
44 ASYNCerr(ASYNC_F_ASYNC_CTX_NEW
, ERR_R_MALLOC_FAILURE
);
48 async_fibre_init_dispatcher(&nctx
->dispatcher
);
51 if (!CRYPTO_THREAD_set_local(&ctxkey
, nctx
))
61 async_ctx
*async_get_ctx(void)
63 return (async_ctx
*)CRYPTO_THREAD_get_local(&ctxkey
);
66 static int async_ctx_free(void)
70 ctx
= async_get_ctx();
72 if (!CRYPTO_THREAD_set_local(&ctxkey
, NULL
))
80 static ASYNC_JOB
*async_job_new(void)
82 ASYNC_JOB
*job
= NULL
;
84 job
= OPENSSL_zalloc(sizeof(*job
));
86 ASYNCerr(ASYNC_F_ASYNC_JOB_NEW
, ERR_R_MALLOC_FAILURE
);
90 job
->status
= ASYNC_JOB_RUNNING
;
95 static void async_job_free(ASYNC_JOB
*job
)
98 OPENSSL_free(job
->funcargs
);
99 async_fibre_free(&job
->fibrectx
);
104 static ASYNC_JOB
*async_get_pool_job(void) {
108 pool
= (async_pool
*)CRYPTO_THREAD_get_local(&poolkey
);
111 * Pool has not been initialised, so init with the defaults, i.e.
112 * no max size and no pre-created jobs
114 if (ASYNC_init_thread(0, 0) == 0)
116 pool
= (async_pool
*)CRYPTO_THREAD_get_local(&poolkey
);
119 job
= sk_ASYNC_JOB_pop(pool
->jobs
);
122 if ((pool
->max_size
!= 0) && (pool
->curr_size
>= pool
->max_size
))
125 job
= async_job_new();
127 if (! async_fibre_makecontext(&job
->fibrectx
)) {
137 static void async_release_job(ASYNC_JOB
*job
) {
140 pool
= (async_pool
*)CRYPTO_THREAD_get_local(&poolkey
);
141 OPENSSL_free(job
->funcargs
);
142 job
->funcargs
= NULL
;
143 sk_ASYNC_JOB_push(pool
->jobs
, job
);
146 void async_start_func(void)
149 async_ctx
*ctx
= async_get_ctx();
154 job
->ret
= job
->func(job
->funcargs
);
157 job
->status
= ASYNC_JOB_STOPPING
;
158 if (!async_fibre_swapcontext(&job
->fibrectx
,
159 &ctx
->dispatcher
, 1)) {
161 * Should not happen. Getting here will close the thread...can't do
164 ASYNCerr(ASYNC_F_ASYNC_START_FUNC
, ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
169 int ASYNC_start_job(ASYNC_JOB
**job
, ASYNC_WAIT_CTX
*wctx
, int *ret
,
170 int (*func
)(void *), void *args
, size_t size
)
174 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
))
177 ctx
= async_get_ctx();
179 ctx
= async_ctx_new();
187 if (ctx
->currjob
!= NULL
) {
188 if (ctx
->currjob
->status
== ASYNC_JOB_STOPPING
) {
189 *ret
= ctx
->currjob
->ret
;
190 ctx
->currjob
->waitctx
= NULL
;
191 async_release_job(ctx
->currjob
);
197 if (ctx
->currjob
->status
== ASYNC_JOB_PAUSING
) {
199 ctx
->currjob
->status
= ASYNC_JOB_PAUSED
;
204 if (ctx
->currjob
->status
== ASYNC_JOB_PAUSED
) {
206 /* Resume previous job */
207 if (!async_fibre_swapcontext(&ctx
->dispatcher
,
208 &ctx
->currjob
->fibrectx
, 1)) {
209 ASYNCerr(ASYNC_F_ASYNC_START_JOB
,
210 ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
216 /* Should not happen */
217 ASYNCerr(ASYNC_F_ASYNC_START_JOB
, ERR_R_INTERNAL_ERROR
);
218 async_release_job(ctx
->currjob
);
224 /* Start a new job */
225 if ((ctx
->currjob
= async_get_pool_job()) == NULL
)
226 return ASYNC_NO_JOBS
;
229 ctx
->currjob
->funcargs
= OPENSSL_malloc(size
);
230 if (ctx
->currjob
->funcargs
== NULL
) {
231 ASYNCerr(ASYNC_F_ASYNC_START_JOB
, ERR_R_MALLOC_FAILURE
);
232 async_release_job(ctx
->currjob
);
236 memcpy(ctx
->currjob
->funcargs
, args
, size
);
238 ctx
->currjob
->funcargs
= NULL
;
241 ctx
->currjob
->func
= func
;
242 ctx
->currjob
->waitctx
= wctx
;
243 if (!async_fibre_swapcontext(&ctx
->dispatcher
,
244 &ctx
->currjob
->fibrectx
, 1)) {
245 ASYNCerr(ASYNC_F_ASYNC_START_JOB
, ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
251 async_release_job(ctx
->currjob
);
257 int ASYNC_pause_job(void)
260 async_ctx
*ctx
= async_get_ctx();
263 || ctx
->currjob
== NULL
266 * Could be we've deliberately not been started within a job so this is
267 * counted as success.
273 job
->status
= ASYNC_JOB_PAUSING
;
275 if (!async_fibre_swapcontext(&job
->fibrectx
,
276 &ctx
->dispatcher
, 1)) {
277 ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB
, ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
280 /* Reset counts of added and deleted fds */
281 async_wait_ctx_reset_counts(job
->waitctx
);
286 static void async_empty_pool(async_pool
*pool
)
290 if (!pool
|| !pool
->jobs
)
294 job
= sk_ASYNC_JOB_pop(pool
->jobs
);
301 if (!CRYPTO_THREAD_init_local(&ctxkey
, NULL
))
304 if (!CRYPTO_THREAD_init_local(&poolkey
, NULL
)) {
305 CRYPTO_THREAD_cleanup_local(&ctxkey
);
312 void async_deinit(void)
314 CRYPTO_THREAD_cleanup_local(&ctxkey
);
315 CRYPTO_THREAD_cleanup_local(&poolkey
);
318 int ASYNC_init_thread(size_t max_size
, size_t init_size
)
321 size_t curr_size
= 0;
323 if (init_size
> max_size
) {
324 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ASYNC_R_INVALID_POOL_SIZE
);
328 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
))
331 if (!ossl_init_thread_start(NULL
, async_delete_thread_state
))
334 pool
= OPENSSL_zalloc(sizeof(*pool
));
336 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ERR_R_MALLOC_FAILURE
);
340 pool
->jobs
= sk_ASYNC_JOB_new_reserve(NULL
, init_size
);
341 if (pool
->jobs
== NULL
) {
342 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ERR_R_MALLOC_FAILURE
);
347 pool
->max_size
= max_size
;
349 /* Pre-create jobs as required */
350 while (init_size
--) {
352 job
= async_job_new();
353 if (job
== NULL
|| !async_fibre_makecontext(&job
->fibrectx
)) {
355 * Not actually fatal because we already created the pool, just
356 * skip creation of any more jobs
361 job
->funcargs
= NULL
;
362 sk_ASYNC_JOB_push(pool
->jobs
, job
); /* Cannot fail due to reserve */
365 pool
->curr_size
= curr_size
;
366 if (!CRYPTO_THREAD_set_local(&poolkey
, pool
)) {
367 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ASYNC_R_FAILED_TO_SET_POOL
);
373 async_empty_pool(pool
);
374 sk_ASYNC_JOB_free(pool
->jobs
);
379 /* TODO(3.0): arg ignored for now */
380 static void async_delete_thread_state(void *arg
)
382 async_pool
*pool
= (async_pool
*)CRYPTO_THREAD_get_local(&poolkey
);
385 async_empty_pool(pool
);
386 sk_ASYNC_JOB_free(pool
->jobs
);
388 CRYPTO_THREAD_set_local(&poolkey
, NULL
);
390 async_local_cleanup();
394 void ASYNC_cleanup_thread(void)
396 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
))
399 async_delete_thread_state(NULL
);
402 ASYNC_JOB
*ASYNC_get_current_job(void)
406 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
))
409 ctx
= async_get_ctx();
416 ASYNC_WAIT_CTX
*ASYNC_get_wait_ctx(ASYNC_JOB
*job
)
421 void ASYNC_block_pause(void)
425 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
))
428 ctx
= async_get_ctx();
429 if (ctx
== NULL
|| ctx
->currjob
== NULL
) {
431 * We're not in a job anyway so ignore this
438 void ASYNC_unblock_pause(void)
442 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
))
445 ctx
= async_get_ctx();
446 if (ctx
== NULL
|| ctx
->currjob
== NULL
) {
448 * We're not in a job anyway so ignore this
452 if (ctx
->blocked
> 0)