1 /* 2 * Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the OpenSSL license (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 8 */ 9 10 /* 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 :-( 15 */ 16 #undef _FORTIFY_SOURCE 17 18 /* This must be the first #include file */ 19 #include "async_locl.h" 20 21 #include <openssl/err.h> 22 #include "internal/cryptlib_int.h" 23 #include <string.h> 24 25 #define ASYNC_JOB_RUNNING 0 26 #define ASYNC_JOB_PAUSING 1 27 #define ASYNC_JOB_PAUSED 2 28 #define ASYNC_JOB_STOPPING 3 29 30 static CRYPTO_THREAD_LOCAL ctxkey; 31 static CRYPTO_THREAD_LOCAL poolkey; 32 33 static async_ctx *async_ctx_new(void) 34 { 35 async_ctx *nctx; 36 37 if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC)) 38 return NULL; 39 40 nctx = OPENSSL_malloc(sizeof(*nctx)); 41 if (nctx == NULL) { 42 ASYNCerr(ASYNC_F_ASYNC_CTX_NEW, ERR_R_MALLOC_FAILURE); 43 goto err; 44 } 45 46 async_fibre_init_dispatcher(&nctx->dispatcher); 47 nctx->currjob = NULL; 48 nctx->blocked = 0; 49 if (!CRYPTO_THREAD_set_local(&ctxkey, nctx)) 50 goto err; 51 52 return nctx; 53 err: 54 OPENSSL_free(nctx); 55 56 return NULL; 57 } 58 59 async_ctx *async_get_ctx(void) 60 { 61 return (async_ctx *)CRYPTO_THREAD_get_local(&ctxkey); 62 } 63 64 static int async_ctx_free(void) 65 { 66 async_ctx *ctx; 67 68 ctx = async_get_ctx(); 69 70 if (!CRYPTO_THREAD_set_local(&ctxkey, NULL)) 71 return 0; 72 73 OPENSSL_free(ctx); 74 75 return 1; 76 } 77 78 static ASYNC_JOB *async_job_new(void) 79 { 80 ASYNC_JOB *job = NULL; 81 82 job = OPENSSL_zalloc(sizeof(*job)); 83 if (job == NULL) { 84 ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ERR_R_MALLOC_FAILURE); 85 return NULL; 86 } 87 88 job->status = ASYNC_JOB_RUNNING; 89 90 return job; 91 } 92 93 static void async_job_free(ASYNC_JOB *job) 94 { 95 if (job != NULL) { 96 OPENSSL_free(job->funcargs); 97 async_fibre_free(&job->fibrectx); 98 OPENSSL_free(job); 99 } 100 } 101 102 static ASYNC_JOB *async_get_pool_job(void) { 103 ASYNC_JOB *job; 104 async_pool *pool; 105 106 pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey); 107 if (pool == NULL) { 108 /* 109 * Pool has not been initialised, so init with the defaults, i.e. 110 * no max size and no pre-created jobs 111 */ 112 if (ASYNC_init_thread(0, 0) == 0) 113 return NULL; 114 pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey); 115 } 116 117 job = sk_ASYNC_JOB_pop(pool->jobs); 118 if (job == NULL) { 119 /* Pool is empty */ 120 if ((pool->max_size != 0) && (pool->curr_size >= pool->max_size)) 121 return NULL; 122 123 job = async_job_new(); 124 if (job != NULL) { 125 if (! async_fibre_makecontext(&job->fibrectx)) { 126 async_job_free(job); 127 return NULL; 128 } 129 pool->curr_size++; 130 } 131 } 132 return job; 133 } 134 135 static void async_release_job(ASYNC_JOB *job) { 136 async_pool *pool; 137 138 pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey); 139 OPENSSL_free(job->funcargs); 140 job->funcargs = NULL; 141 sk_ASYNC_JOB_push(pool->jobs, job); 142 } 143 144 void async_start_func(void) 145 { 146 ASYNC_JOB *job; 147 async_ctx *ctx = async_get_ctx(); 148 149 while (1) { 150 /* Run the job */ 151 job = ctx->currjob; 152 job->ret = job->func(job->funcargs); 153 154 /* Stop the job */ 155 job->status = ASYNC_JOB_STOPPING; 156 if (!async_fibre_swapcontext(&job->fibrectx, 157 &ctx->dispatcher, 1)) { 158 /* 159 * Should not happen. Getting here will close the thread...can't do 160 * much about it 161 */ 162 ASYNCerr(ASYNC_F_ASYNC_START_FUNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT); 163 } 164 } 165 } 166 167 int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret, 168 int (*func)(void *), void *args, size_t size) 169 { 170 async_ctx *ctx; 171 172 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) 173 return ASYNC_ERR; 174 175 ctx = async_get_ctx(); 176 if (ctx == NULL) 177 ctx = async_ctx_new(); 178 if (ctx == NULL) 179 return ASYNC_ERR; 180 181 if (*job) 182 ctx->currjob = *job; 183 184 for (;;) { 185 if (ctx->currjob != NULL) { 186 if (ctx->currjob->status == ASYNC_JOB_STOPPING) { 187 *ret = ctx->currjob->ret; 188 ctx->currjob->waitctx = NULL; 189 async_release_job(ctx->currjob); 190 ctx->currjob = NULL; 191 *job = NULL; 192 return ASYNC_FINISH; 193 } 194 195 if (ctx->currjob->status == ASYNC_JOB_PAUSING) { 196 *job = ctx->currjob; 197 ctx->currjob->status = ASYNC_JOB_PAUSED; 198 ctx->currjob = NULL; 199 return ASYNC_PAUSE; 200 } 201 202 if (ctx->currjob->status == ASYNC_JOB_PAUSED) { 203 ctx->currjob = *job; 204 /* Resume previous job */ 205 if (!async_fibre_swapcontext(&ctx->dispatcher, 206 &ctx->currjob->fibrectx, 1)) { 207 ASYNCerr(ASYNC_F_ASYNC_START_JOB, 208 ASYNC_R_FAILED_TO_SWAP_CONTEXT); 209 goto err; 210 } 211 continue; 212 } 213 214 /* Should not happen */ 215 ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_INTERNAL_ERROR); 216 async_release_job(ctx->currjob); 217 ctx->currjob = NULL; 218 *job = NULL; 219 return ASYNC_ERR; 220 } 221 222 /* Start a new job */ 223 if ((ctx->currjob = async_get_pool_job()) == NULL) 224 return ASYNC_NO_JOBS; 225 226 if (args != NULL) { 227 ctx->currjob->funcargs = OPENSSL_malloc(size); 228 if (ctx->currjob->funcargs == NULL) { 229 ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_MALLOC_FAILURE); 230 async_release_job(ctx->currjob); 231 ctx->currjob = NULL; 232 return ASYNC_ERR; 233 } 234 memcpy(ctx->currjob->funcargs, args, size); 235 } else { 236 ctx->currjob->funcargs = NULL; 237 } 238 239 ctx->currjob->func = func; 240 ctx->currjob->waitctx = wctx; 241 if (!async_fibre_swapcontext(&ctx->dispatcher, 242 &ctx->currjob->fibrectx, 1)) { 243 ASYNCerr(ASYNC_F_ASYNC_START_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT); 244 goto err; 245 } 246 } 247 248 err: 249 async_release_job(ctx->currjob); 250 ctx->currjob = NULL; 251 *job = NULL; 252 return ASYNC_ERR; 253 } 254 255 int ASYNC_pause_job(void) 256 { 257 ASYNC_JOB *job; 258 async_ctx *ctx = async_get_ctx(); 259 260 if (ctx == NULL 261 || ctx->currjob == NULL 262 || ctx->blocked) { 263 /* 264 * Could be we've deliberately not been started within a job so this is 265 * counted as success. 266 */ 267 return 1; 268 } 269 270 job = ctx->currjob; 271 job->status = ASYNC_JOB_PAUSING; 272 273 if (!async_fibre_swapcontext(&job->fibrectx, 274 &ctx->dispatcher, 1)) { 275 ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT); 276 return 0; 277 } 278 /* Reset counts of added and deleted fds */ 279 async_wait_ctx_reset_counts(job->waitctx); 280 281 return 1; 282 } 283 284 static void async_empty_pool(async_pool *pool) 285 { 286 ASYNC_JOB *job; 287 288 if (!pool || !pool->jobs) 289 return; 290 291 do { 292 job = sk_ASYNC_JOB_pop(pool->jobs); 293 async_job_free(job); 294 } while (job); 295 } 296 297 int async_init(void) 298 { 299 if (!CRYPTO_THREAD_init_local(&ctxkey, NULL)) 300 return 0; 301 302 if (!CRYPTO_THREAD_init_local(&poolkey, NULL)) { 303 CRYPTO_THREAD_cleanup_local(&ctxkey); 304 return 0; 305 } 306 307 return 1; 308 } 309 310 void async_deinit(void) 311 { 312 CRYPTO_THREAD_cleanup_local(&ctxkey); 313 CRYPTO_THREAD_cleanup_local(&poolkey); 314 } 315 316 int ASYNC_init_thread(size_t max_size, size_t init_size) 317 { 318 async_pool *pool; 319 size_t curr_size = 0; 320 321 if (init_size > max_size) { 322 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_INVALID_POOL_SIZE); 323 return 0; 324 } 325 326 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) 327 return 0; 328 329 if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC)) 330 return 0; 331 332 pool = OPENSSL_zalloc(sizeof(*pool)); 333 if (pool == NULL) { 334 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE); 335 return 0; 336 } 337 338 pool->jobs = sk_ASYNC_JOB_new_reserve(NULL, init_size); 339 if (pool->jobs == NULL) { 340 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE); 341 OPENSSL_free(pool); 342 return 0; 343 } 344 345 pool->max_size = max_size; 346 347 /* Pre-create jobs as required */ 348 while (init_size--) { 349 ASYNC_JOB *job; 350 job = async_job_new(); 351 if (job == NULL || !async_fibre_makecontext(&job->fibrectx)) { 352 /* 353 * Not actually fatal because we already created the pool, just 354 * skip creation of any more jobs 355 */ 356 async_job_free(job); 357 break; 358 } 359 job->funcargs = NULL; 360 sk_ASYNC_JOB_push(pool->jobs, job); /* Cannot fail due to reserve */ 361 curr_size++; 362 } 363 pool->curr_size = curr_size; 364 if (!CRYPTO_THREAD_set_local(&poolkey, pool)) { 365 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_FAILED_TO_SET_POOL); 366 goto err; 367 } 368 369 return 1; 370 err: 371 async_empty_pool(pool); 372 sk_ASYNC_JOB_free(pool->jobs); 373 OPENSSL_free(pool); 374 return 0; 375 } 376 377 void async_delete_thread_state(void) 378 { 379 async_pool *pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey); 380 381 if (pool != NULL) { 382 async_empty_pool(pool); 383 sk_ASYNC_JOB_free(pool->jobs); 384 OPENSSL_free(pool); 385 CRYPTO_THREAD_set_local(&poolkey, NULL); 386 } 387 async_local_cleanup(); 388 async_ctx_free(); 389 } 390 391 void ASYNC_cleanup_thread(void) 392 { 393 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) 394 return; 395 396 async_delete_thread_state(); 397 } 398 399 ASYNC_JOB *ASYNC_get_current_job(void) 400 { 401 async_ctx *ctx; 402 403 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) 404 return NULL; 405 406 ctx = async_get_ctx(); 407 if (ctx == NULL) 408 return NULL; 409 410 return ctx->currjob; 411 } 412 413 ASYNC_WAIT_CTX *ASYNC_get_wait_ctx(ASYNC_JOB *job) 414 { 415 return job->waitctx; 416 } 417 418 void ASYNC_block_pause(void) 419 { 420 async_ctx *ctx; 421 422 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) 423 return; 424 425 ctx = async_get_ctx(); 426 if (ctx == NULL || ctx->currjob == NULL) { 427 /* 428 * We're not in a job anyway so ignore this 429 */ 430 return; 431 } 432 ctx->blocked++; 433 } 434 435 void ASYNC_unblock_pause(void) 436 { 437 async_ctx *ctx; 438 439 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) 440 return; 441 442 ctx = async_get_ctx(); 443 if (ctx == NULL || ctx->currjob == NULL) { 444 /* 445 * We're not in a job anyway so ignore this 446 */ 447 return; 448 } 449 if (ctx->blocked > 0) 450 ctx->blocked--; 451 } 452