Coverage Report

Created: 2024-05-15 07:16

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