1/*
2 * Copyright 2015-2022 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#ifdef _WIN32
11# include <windows.h>
12#endif
13
14#include <stdio.h>
15#include <string.h>
16#include <openssl/async.h>
17#include <openssl/crypto.h>
18
19static int ctr = 0;
20static ASYNC_JOB *currjob = NULL;
21
22static int only_pause(void *args)
23{
24    ASYNC_pause_job();
25
26    return 1;
27}
28
29static int add_two(void *args)
30{
31    ctr++;
32    ASYNC_pause_job();
33    ctr++;
34
35    return 2;
36}
37
38static int save_current(void *args)
39{
40    currjob = ASYNC_get_current_job();
41    ASYNC_pause_job();
42
43    return 1;
44}
45
46static int change_deflt_libctx(void *args)
47{
48    OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
49    OSSL_LIB_CTX *oldctx, *tmpctx;
50    int ret = 0;
51
52    if (libctx == NULL)
53        return 0;
54
55    oldctx = OSSL_LIB_CTX_set0_default(libctx);
56    ASYNC_pause_job();
57
58    /* Check the libctx is set up as we expect */
59    tmpctx = OSSL_LIB_CTX_set0_default(oldctx);
60    if (tmpctx != libctx)
61        goto err;
62
63    /* Set it back again to continue to use our own libctx */
64    oldctx = OSSL_LIB_CTX_set0_default(libctx);
65    ASYNC_pause_job();
66
67    /* Check the libctx is set up as we expect */
68    tmpctx = OSSL_LIB_CTX_set0_default(oldctx);
69    if (tmpctx != libctx)
70        goto err;
71
72    ret = 1;
73 err:
74    OSSL_LIB_CTX_free(libctx);
75    return ret;
76}
77
78
79#define MAGIC_WAIT_FD   ((OSSL_ASYNC_FD)99)
80static int waitfd(void *args)
81{
82    ASYNC_JOB *job;
83    ASYNC_WAIT_CTX *waitctx;
84    job = ASYNC_get_current_job();
85    if (job == NULL)
86        return 0;
87    waitctx = ASYNC_get_wait_ctx(job);
88    if (waitctx == NULL)
89        return 0;
90
91    /* First case: no fd added or removed */
92    ASYNC_pause_job();
93
94    /* Second case: one fd added */
95    if (!ASYNC_WAIT_CTX_set_wait_fd(waitctx, waitctx, MAGIC_WAIT_FD, NULL, NULL))
96        return 0;
97    ASYNC_pause_job();
98
99    /* Third case: all fd removed */
100    if (!ASYNC_WAIT_CTX_clear_fd(waitctx, waitctx))
101        return 0;
102    ASYNC_pause_job();
103
104    /* Last case: fd added and immediately removed */
105    if (!ASYNC_WAIT_CTX_set_wait_fd(waitctx, waitctx, MAGIC_WAIT_FD, NULL, NULL))
106        return 0;
107    if (!ASYNC_WAIT_CTX_clear_fd(waitctx, waitctx))
108        return 0;
109
110    return 1;
111}
112
113static int blockpause(void *args)
114{
115    ASYNC_block_pause();
116    ASYNC_pause_job();
117    ASYNC_unblock_pause();
118    ASYNC_pause_job();
119
120    return 1;
121}
122
123static int test_ASYNC_init_thread(void)
124{
125    ASYNC_JOB *job1 = NULL, *job2 = NULL, *job3 = NULL;
126    int funcret1, funcret2, funcret3;
127    ASYNC_WAIT_CTX *waitctx = NULL;
128
129    if (       !ASYNC_init_thread(2, 0)
130            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
131            || ASYNC_start_job(&job1, waitctx, &funcret1, only_pause, NULL, 0)
132                != ASYNC_PAUSE
133            || ASYNC_start_job(&job2, waitctx, &funcret2, only_pause, NULL, 0)
134                != ASYNC_PAUSE
135            || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0)
136                != ASYNC_NO_JOBS
137            || ASYNC_start_job(&job1, waitctx, &funcret1, only_pause, NULL, 0)
138                != ASYNC_FINISH
139            || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0)
140                != ASYNC_PAUSE
141            || ASYNC_start_job(&job2, waitctx, &funcret2, only_pause, NULL, 0)
142                != ASYNC_FINISH
143            || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0)
144                != ASYNC_FINISH
145            || funcret1 != 1
146            || funcret2 != 1
147            || funcret3 != 1) {
148        fprintf(stderr, "test_ASYNC_init_thread() failed\n");
149        ASYNC_WAIT_CTX_free(waitctx);
150        ASYNC_cleanup_thread();
151        return 0;
152    }
153
154    ASYNC_WAIT_CTX_free(waitctx);
155    ASYNC_cleanup_thread();
156    return 1;
157}
158
159static int test_callback(void *arg)
160{
161    printf("callback test pass\n");
162    return 1;
163}
164
165static int test_ASYNC_callback_status(void)
166{
167    ASYNC_WAIT_CTX *waitctx = NULL;
168    int set_arg = 100;
169    ASYNC_callback_fn get_callback;
170    void *get_arg;
171    int set_status = 1;
172
173    if (       !ASYNC_init_thread(1, 0)
174            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
175            || ASYNC_WAIT_CTX_set_callback(waitctx, test_callback, (void*)&set_arg)
176               != 1
177            || ASYNC_WAIT_CTX_get_callback(waitctx, &get_callback, &get_arg)
178               != 1
179            || test_callback != get_callback
180            || get_arg != (void*)&set_arg
181            || (*get_callback)(get_arg) != 1
182            || ASYNC_WAIT_CTX_set_status(waitctx, set_status) != 1
183            || set_status != ASYNC_WAIT_CTX_get_status(waitctx)) {
184        fprintf(stderr, "test_ASYNC_callback_status() failed\n");
185        ASYNC_WAIT_CTX_free(waitctx);
186        ASYNC_cleanup_thread();
187        return 0;
188    }
189
190    ASYNC_WAIT_CTX_free(waitctx);
191    ASYNC_cleanup_thread();
192    return 1;
193
194}
195
196static int test_ASYNC_start_job(void)
197{
198    ASYNC_JOB *job = NULL;
199    int funcret;
200    ASYNC_WAIT_CTX *waitctx = NULL;
201
202    ctr = 0;
203
204    if (       !ASYNC_init_thread(1, 0)
205            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
206            || ASYNC_start_job(&job, waitctx, &funcret, add_two, NULL, 0)
207               != ASYNC_PAUSE
208            || ctr != 1
209            || ASYNC_start_job(&job, waitctx, &funcret, add_two, NULL, 0)
210               != ASYNC_FINISH
211            || ctr != 2
212            || funcret != 2) {
213        fprintf(stderr, "test_ASYNC_start_job() failed\n");
214        ASYNC_WAIT_CTX_free(waitctx);
215        ASYNC_cleanup_thread();
216        return 0;
217    }
218
219    ASYNC_WAIT_CTX_free(waitctx);
220    ASYNC_cleanup_thread();
221    return 1;
222}
223
224static int test_ASYNC_get_current_job(void)
225{
226    ASYNC_JOB *job = NULL;
227    int funcret;
228    ASYNC_WAIT_CTX *waitctx = NULL;
229
230    currjob = NULL;
231
232    if (       !ASYNC_init_thread(1, 0)
233            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
234            || ASYNC_start_job(&job, waitctx, &funcret, save_current, NULL, 0)
235                != ASYNC_PAUSE
236            || currjob != job
237            || ASYNC_start_job(&job, waitctx, &funcret, save_current, NULL, 0)
238                != ASYNC_FINISH
239            || funcret != 1) {
240        fprintf(stderr, "test_ASYNC_get_current_job() failed\n");
241        ASYNC_WAIT_CTX_free(waitctx);
242        ASYNC_cleanup_thread();
243        return 0;
244    }
245
246    ASYNC_WAIT_CTX_free(waitctx);
247    ASYNC_cleanup_thread();
248    return 1;
249}
250
251static int test_ASYNC_WAIT_CTX_get_all_fds(void)
252{
253    ASYNC_JOB *job = NULL;
254    int funcret;
255    ASYNC_WAIT_CTX *waitctx = NULL;
256    OSSL_ASYNC_FD fd = OSSL_BAD_ASYNC_FD, delfd = OSSL_BAD_ASYNC_FD;
257    size_t numfds, numdelfds;
258
259    if (       !ASYNC_init_thread(1, 0)
260            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
261               /* On first run we're not expecting any wait fds */
262            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
263                != ASYNC_PAUSE
264            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
265            || numfds != 0
266            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
267                                               &numdelfds)
268            || numfds != 0
269            || numdelfds != 0
270               /* On second run we're expecting one added fd */
271            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
272                != ASYNC_PAUSE
273            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
274            || numfds != 1
275            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, &fd, &numfds)
276            || fd != MAGIC_WAIT_FD
277            || (fd = OSSL_BAD_ASYNC_FD, 0) /* Assign to something else */
278            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
279                                               &numdelfds)
280            || numfds != 1
281            || numdelfds != 0
282            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, &fd, &numfds, NULL,
283                                               &numdelfds)
284            || fd != MAGIC_WAIT_FD
285               /* On third run we expect one deleted fd */
286            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
287                != ASYNC_PAUSE
288            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
289            || numfds != 0
290            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
291                                               &numdelfds)
292            || numfds != 0
293            || numdelfds != 1
294            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, &delfd,
295                                               &numdelfds)
296            || delfd != MAGIC_WAIT_FD
297            /* On last run we are not expecting any wait fd */
298            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
299                != ASYNC_FINISH
300            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
301            || numfds != 0
302            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
303                                               &numdelfds)
304            || numfds != 0
305            || numdelfds != 0
306            || funcret != 1) {
307        fprintf(stderr, "test_ASYNC_get_wait_fd() failed\n");
308        ASYNC_WAIT_CTX_free(waitctx);
309        ASYNC_cleanup_thread();
310        return 0;
311    }
312
313    ASYNC_WAIT_CTX_free(waitctx);
314    ASYNC_cleanup_thread();
315    return 1;
316}
317
318static int test_ASYNC_block_pause(void)
319{
320    ASYNC_JOB *job = NULL;
321    int funcret;
322    ASYNC_WAIT_CTX *waitctx = NULL;
323
324    if (       !ASYNC_init_thread(1, 0)
325            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
326            || ASYNC_start_job(&job, waitctx, &funcret, blockpause, NULL, 0)
327                != ASYNC_PAUSE
328            || ASYNC_start_job(&job, waitctx, &funcret, blockpause, NULL, 0)
329                != ASYNC_FINISH
330            || funcret != 1) {
331        fprintf(stderr, "test_ASYNC_block_pause() failed\n");
332        ASYNC_WAIT_CTX_free(waitctx);
333        ASYNC_cleanup_thread();
334        return 0;
335    }
336
337    ASYNC_WAIT_CTX_free(waitctx);
338    ASYNC_cleanup_thread();
339    return 1;
340}
341
342static int test_ASYNC_start_job_ex(void)
343{
344    ASYNC_JOB *job = NULL;
345    int funcret;
346    ASYNC_WAIT_CTX *waitctx = NULL;
347    OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
348    OSSL_LIB_CTX *oldctx, *tmpctx, *globalctx;
349    int ret = 0;
350
351    if (libctx == NULL) {
352        fprintf(stderr,
353                "test_ASYNC_start_job_ex() failed to create libctx\n");
354        goto err;
355    }
356
357    globalctx = oldctx = OSSL_LIB_CTX_set0_default(libctx);
358
359    if ((waitctx = ASYNC_WAIT_CTX_new()) == NULL
360            || ASYNC_start_job(&job, waitctx, &funcret, change_deflt_libctx,
361                               NULL, 0)
362               != ASYNC_PAUSE) {
363        fprintf(stderr,
364                "test_ASYNC_start_job_ex() failed to start job\n");
365        goto err;
366    }
367
368    /* Reset the libctx temporarily to find out what it is*/
369    tmpctx = OSSL_LIB_CTX_set0_default(oldctx);
370    oldctx = OSSL_LIB_CTX_set0_default(tmpctx);
371    if (tmpctx != libctx) {
372        fprintf(stderr,
373                "test_ASYNC_start_job_ex() failed - unexpected libctx\n");
374        goto err;
375    }
376
377    if (ASYNC_start_job(&job, waitctx, &funcret, change_deflt_libctx, NULL, 0)
378               != ASYNC_PAUSE) {
379        fprintf(stderr,
380                "test_ASYNC_start_job_ex() - restarting job failed\n");
381        goto err;
382    }
383
384    /* Reset the libctx and continue with the global default libctx */
385    tmpctx = OSSL_LIB_CTX_set0_default(oldctx);
386    if (tmpctx != libctx) {
387        fprintf(stderr,
388                "test_ASYNC_start_job_ex() failed - unexpected libctx\n");
389        goto err;
390    }
391
392    if (ASYNC_start_job(&job, waitctx, &funcret, change_deflt_libctx, NULL, 0)
393               != ASYNC_FINISH
394                || funcret != 1) {
395        fprintf(stderr,
396                "test_ASYNC_start_job_ex() - finishing job failed\n");
397        goto err;
398    }
399
400    /* Reset the libctx temporarily to find out what it is*/
401    tmpctx = OSSL_LIB_CTX_set0_default(libctx);
402    OSSL_LIB_CTX_set0_default(tmpctx);
403    if (tmpctx != globalctx) {
404        fprintf(stderr,
405                "test_ASYNC_start_job_ex() failed - global libctx check failed\n");
406        goto err;
407    }
408
409    ret = 1;
410 err:
411    ASYNC_WAIT_CTX_free(waitctx);
412    ASYNC_cleanup_thread();
413    OSSL_LIB_CTX_free(libctx);
414    return ret;
415}
416
417int main(int argc, char **argv)
418{
419    if (!ASYNC_is_capable()) {
420        fprintf(stderr,
421                "OpenSSL build is not ASYNC capable - skipping async tests\n");
422    } else {
423        if (!test_ASYNC_init_thread()
424                || !test_ASYNC_callback_status()
425                || !test_ASYNC_start_job()
426                || !test_ASYNC_get_current_job()
427                || !test_ASYNC_WAIT_CTX_get_all_fds()
428                || !test_ASYNC_block_pause()
429                || !test_ASYNC_start_job_ex()) {
430            return 1;
431        }
432    }
433    printf("PASS\n");
434    return 0;
435}
436