Coverage Report

Created: 2018-08-29 13:53

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