xref: /freebsd/crypto/openssl/test/radix/quic_bindings.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1 /*
2  * Copyright 2024-2025 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 #include <openssl/lhash.h>
10 #include <assert.h>
11 
12 #include "internal/quic_engine.h"
13 #include "internal/quic_channel.h"
14 #include "internal/quic_ssl.h"
15 #include "internal/quic_error.h"
16 
17 /*
18  * RADIX 6D QUIC Test Framework
19  * =============================================================================
20  *
21  * The radix test framework is a six-dimension script-driven facility to support
22  * execution of
23  *
24  *   multi-stream
25  *   multi-client
26  *   multi-server
27  *   multi-thread
28  *   multi-process
29  *   multi-node
30  *
31  * test vignettes for QUIC. Unlike the older multistream test framework, it does
32  * not assume a single client and a single server. Examples of vignettes
33  * designed to be supported by the radix test framework in future include:
34  *
35  *      single client    <-> single server
36  *      multiple clients <-> single server
37  *      single client    <-> multiple servers
38  *      multiple clients <-> multiple servers
39  *
40  * 'Multi-process' and 'multi-node' means there has been some consideration
41  * given to support of multi-process and multi-node testing in the future,
42  * though this is not currently supported.
43  */
44 
45 /*
46  * An object is something associated with a name in the process-level state. The
47  * process-level state primarily revolves around a global dictionary of SSL
48  * objects.
49  */
50 typedef struct radix_obj_st {
51     char                *name;  /* owned, zero-terminated */
52     SSL                 *ssl;   /* owns one reference */
53     unsigned int        registered      : 1; /* in LHASH? */
54     unsigned int        active          : 1; /* tick? */
55 } RADIX_OBJ;
56 
57 DEFINE_LHASH_OF_EX(RADIX_OBJ);
58 
59 /* Process-level state (i.e. "globals" in the normal sense of the word) */
60 typedef struct radix_process_st {
61     size_t                  node_idx;
62     size_t                  process_idx;
63     size_t                  next_thread_idx;
64     STACK_OF(RADIX_THREAD)  *threads;
65 
66     /* Process-global state. */
67     CRYPTO_MUTEX            *gm;            /* global mutex */
68     LHASH_OF(RADIX_OBJ)     *objs;          /* protected by gm */
69     OSSL_TIME               time_slip;      /* protected by gm */
70     BIO                     *keylog_out;    /* protected by gm */
71 
72     int                     done_join_all_threads;
73 
74     /*
75      * Valid if done_join_all threads. Logical AND of all child worker results.
76      */
77     int                     thread_composite_testresult;
78 } RADIX_PROCESS;
79 
80 #define NUM_SLOTS       8
81 
82 /* Thread-level state within a process */
83 typedef struct radix_thread_st {
84     RADIX_PROCESS       *rp;
85     CRYPTO_THREAD       *t;
86     unsigned char       *tmp_buf;
87     size_t              tmp_buf_offset;
88     size_t              thread_idx; /* 0=main thread */
89     RADIX_OBJ           *slot[NUM_SLOTS];
90     SSL                 *ssl[NUM_SLOTS];
91 
92     /* child thread spawn arguments */
93     SCRIPT_INFO         *child_script_info;
94     BIO                 *debug_bio;
95 
96     /* m protects all of the below values */
97     CRYPTO_MUTEX        *m;
98     int                 done;
99     int                 testresult; /* valid if done */
100 
101     uint64_t            scratch0;
102 } RADIX_THREAD;
103 
DEFINE_STACK_OF(RADIX_THREAD)104 DEFINE_STACK_OF(RADIX_THREAD)
105 
106 /* ssl reference is transferred. name is copied and is required. */
107 static RADIX_OBJ *RADIX_OBJ_new(const char *name, SSL *ssl)
108 {
109     RADIX_OBJ *obj;
110 
111     if (!TEST_ptr(name) || !TEST_ptr(ssl))
112         return NULL;
113 
114     if (!TEST_ptr(obj = OPENSSL_zalloc(sizeof(*obj))))
115        return NULL;
116 
117     if (!TEST_ptr(obj->name = OPENSSL_strdup(name))) {
118         OPENSSL_free(obj);
119         return NULL;
120     }
121 
122     obj->ssl  = ssl;
123     return obj;
124 }
125 
RADIX_OBJ_free(RADIX_OBJ * obj)126 static void RADIX_OBJ_free(RADIX_OBJ *obj)
127 {
128     if (obj == NULL)
129         return;
130 
131     assert(!obj->registered);
132 
133     SSL_free(obj->ssl);
134     OPENSSL_free(obj->name);
135     OPENSSL_free(obj);
136 }
137 
RADIX_OBJ_hash(const RADIX_OBJ * obj)138 static unsigned long RADIX_OBJ_hash(const RADIX_OBJ *obj)
139 {
140     return OPENSSL_LH_strhash(obj->name);
141 }
142 
RADIX_OBJ_cmp(const RADIX_OBJ * a,const RADIX_OBJ * b)143 static int RADIX_OBJ_cmp(const RADIX_OBJ *a, const RADIX_OBJ *b)
144 {
145     return strcmp(a->name, b->name);
146 }
147 
RADIX_PROCESS_init(RADIX_PROCESS * rp,size_t node_idx,size_t process_idx)148 static int RADIX_PROCESS_init(RADIX_PROCESS *rp, size_t node_idx, size_t process_idx)
149 {
150     const char *keylog_path;
151 
152 #if defined(OPENSSL_THREADS)
153     if (!TEST_ptr(rp->gm = ossl_crypto_mutex_new()))
154         goto err;
155 #endif
156 
157     if (!TEST_ptr(rp->objs = lh_RADIX_OBJ_new(RADIX_OBJ_hash, RADIX_OBJ_cmp)))
158         goto err;
159 
160     if (!TEST_ptr(rp->threads = sk_RADIX_THREAD_new(NULL)))
161         goto err;
162 
163     rp->keylog_out = NULL;
164     keylog_path = ossl_safe_getenv("SSLKEYLOGFILE");
165     if (keylog_path != NULL && *keylog_path != '\0'
166         && !TEST_ptr(rp->keylog_out = BIO_new_file(keylog_path, "a")))
167         goto err;
168 
169     rp->node_idx                = node_idx;
170     rp->process_idx             = process_idx;
171     rp->done_join_all_threads   = 0;
172     rp->next_thread_idx         = 0;
173     return 1;
174 
175 err:
176     lh_RADIX_OBJ_free(rp->objs);
177     rp->objs = NULL;
178     ossl_crypto_mutex_free(&rp->gm);
179     return 0;
180 }
181 
stream_state_to_str(int state)182 static const char *stream_state_to_str(int state)
183 {
184     switch (state) {
185     case SSL_STREAM_STATE_NONE:
186         return "none";
187     case SSL_STREAM_STATE_OK:
188         return "OK";
189     case SSL_STREAM_STATE_WRONG_DIR:
190         return "wrong-dir";
191     case SSL_STREAM_STATE_FINISHED:
192         return "finished";
193     case SSL_STREAM_STATE_RESET_LOCAL:
194         return "reset-local";
195     case SSL_STREAM_STATE_RESET_REMOTE:
196         return "reset-remote";
197     case SSL_STREAM_STATE_CONN_CLOSED:
198         return "conn-closed";
199     default:
200         return "?";
201     }
202 }
203 
report_ssl_state(BIO * bio,const char * pfx,int is_write,int state,uint64_t ec)204 static void report_ssl_state(BIO *bio, const char *pfx, int is_write,
205                              int state, uint64_t ec)
206 {
207     const char *state_s = stream_state_to_str(state);
208 
209     BIO_printf(bio, "%s%-15s%s(%d)", pfx, is_write ? "Write state: " : "Read state: ",
210         state_s, state);
211     if (ec != UINT64_MAX)
212         BIO_printf(bio, ", %llu", (unsigned long long)ec);
213     BIO_printf(bio, "\n");
214 }
215 
report_ssl(SSL * ssl,BIO * bio,const char * pfx)216 static void report_ssl(SSL *ssl, BIO *bio, const char *pfx)
217 {
218     const char *type = "SSL";
219     int is_quic = SSL_is_quic(ssl), is_conn = 0, is_listener = 0;
220     SSL_CONN_CLOSE_INFO cc_info = {0};
221     const char *e_str, *f_str;
222 
223     if (is_quic) {
224         is_conn = SSL_is_connection(ssl);
225         is_listener = SSL_is_listener(ssl);
226 
227         if (is_listener)
228             type = "QLSO";
229         else if (is_conn)
230             type = "QCSO";
231         else
232             type = "QSSO";
233     }
234 
235     BIO_printf(bio, "%sType:          %s\n", pfx, type);
236 
237     if (is_quic && is_conn
238         && SSL_get_conn_close_info(ssl, &cc_info, sizeof(cc_info))) {
239 
240         e_str = ossl_quic_err_to_string(cc_info.error_code);
241         f_str = ossl_quic_frame_type_to_string(cc_info.frame_type);
242 
243         if (e_str == NULL)
244             e_str = "?";
245         if (f_str == NULL)
246             f_str = "?";
247 
248         BIO_printf(bio, "%sConnection is closed: %s(%llu)/%s(%llu), "
249                    "%s, %s, reason: \"%s\"\n",
250                    pfx,
251                    e_str,
252                    (unsigned long long)cc_info.error_code,
253                    f_str,
254                    (unsigned long long)cc_info.frame_type,
255                    (cc_info.flags & SSL_CONN_CLOSE_FLAG_LOCAL) != 0
256                      ? "local" : "remote",
257                    (cc_info.flags & SSL_CONN_CLOSE_FLAG_TRANSPORT) != 0
258                      ? "transport" : "app",
259                    cc_info.reason != NULL ? cc_info.reason : "-");
260     }
261 
262     if (is_quic && !is_listener) {
263         uint64_t stream_id = SSL_get_stream_id(ssl), rec, wec;
264         int rstate, wstate;
265 
266         if (stream_id != UINT64_MAX)
267             BIO_printf(bio, "%sStream ID: %llu\n", pfx,
268                        (unsigned long long)stream_id);
269 
270         rstate = SSL_get_stream_read_state(ssl);
271         wstate = SSL_get_stream_write_state(ssl);
272 
273         if (SSL_get_stream_read_error_code(ssl, &rec) != 1)
274             rec = UINT64_MAX;
275 
276         if (SSL_get_stream_write_error_code(ssl, &wec) != 1)
277             wec = UINT64_MAX;
278 
279         report_ssl_state(bio, pfx, 0, rstate, rec);
280         report_ssl_state(bio, pfx, 1, wstate, wec);
281     }
282 }
283 
report_obj(RADIX_OBJ * obj,void * arg)284 static void report_obj(RADIX_OBJ *obj, void *arg)
285 {
286     BIO *bio = arg;
287     SSL *ssl = obj->ssl;
288 
289     BIO_printf(bio, "      - %-16s @ %p\n", obj->name, (void *)obj->ssl);
290     ERR_set_mark();
291     report_ssl(ssl, bio, "          ");
292     ERR_pop_to_mark();
293 }
294 
RADIX_THREAD_report_state(RADIX_THREAD * rt,BIO * bio)295 static void RADIX_THREAD_report_state(RADIX_THREAD *rt, BIO *bio)
296 {
297     size_t i;
298 
299     BIO_printf(bio, "  Slots:\n");
300     for (i = 0; i < NUM_SLOTS; ++i)
301         if (rt->slot[i] == NULL)
302             BIO_printf(bio, "  %3zu) <NULL>\n", i);
303         else
304             BIO_printf(bio, "  %3zu) '%s' (SSL: %p)\n", i,
305                        rt->slot[i]->name,
306                        (void *)rt->ssl[i]);
307 }
308 
RADIX_PROCESS_report_state(RADIX_PROCESS * rp,BIO * bio,int verbose)309 static void RADIX_PROCESS_report_state(RADIX_PROCESS *rp, BIO *bio,
310                                        int verbose)
311 {
312     BIO_printf(bio, "Final process state for node %zu, process %zu:\n",
313                rp->node_idx, rp->process_idx);
314 
315     BIO_printf(bio, "  Threads (incl. main):        %zu\n",
316                rp->next_thread_idx);
317     BIO_printf(bio, "  Time slip:                   %llu ms\n",
318                (unsigned long long)ossl_time2ms(rp->time_slip));
319 
320     BIO_printf(bio, "  Objects:\n");
321     lh_RADIX_OBJ_doall_arg(rp->objs, report_obj, bio);
322 
323     if (verbose)
324         RADIX_THREAD_report_state(sk_RADIX_THREAD_value(rp->threads, 0),
325                                   bio_err);
326 
327     BIO_printf(bio, "\n==========================================="
328                "===========================\n");
329 }
330 
RADIX_PROCESS_report_thread_results(RADIX_PROCESS * rp,BIO * bio)331 static void RADIX_PROCESS_report_thread_results(RADIX_PROCESS *rp, BIO *bio)
332 {
333     size_t i;
334     RADIX_THREAD *rt;
335     char *p;
336     long l;
337     char pfx_buf[64];
338     int rt_testresult;
339 
340     for (i = 1; i < (size_t)sk_RADIX_THREAD_num(rp->threads); ++i) {
341         rt = sk_RADIX_THREAD_value(rp->threads, i);
342 
343         ossl_crypto_mutex_lock(rt->m);
344         assert(rt->done);
345         rt_testresult = rt->testresult;
346         ossl_crypto_mutex_unlock(rt->m);
347 
348         BIO_printf(bio, "\n====(n%zu/p%zu/t%zu)============================"
349                    "===========================\n"
350                    "Result for child thread with index %zu:\n",
351                    rp->node_idx, rp->process_idx, rt->thread_idx, rt->thread_idx);
352 
353         BIO_snprintf(pfx_buf, sizeof(pfx_buf), "#  -T-%2zu:\t# ", rt->thread_idx);
354         BIO_set_prefix(bio_err, pfx_buf);
355 
356         l = BIO_get_mem_data(rt->debug_bio, &p);
357         BIO_write(bio, p, l);
358         BIO_printf(bio, "\n");
359         BIO_set_prefix(bio_err, "# ");
360         BIO_printf(bio, "==> Child thread with index %zu exited with %d\n",
361                    rt->thread_idx, rt_testresult);
362         if (!rt_testresult)
363             RADIX_THREAD_report_state(rt, bio);
364     }
365 
366     BIO_printf(bio, "\n==========================================="
367                "===========================\n");
368 }
369 
370 static int RADIX_THREAD_join(RADIX_THREAD *rt);
371 
RADIX_PROCESS_join_all_threads(RADIX_PROCESS * rp,int * testresult)372 static int RADIX_PROCESS_join_all_threads(RADIX_PROCESS *rp, int *testresult)
373 {
374     int ok = 1;
375     size_t i;
376     RADIX_THREAD *rt;
377     int composite_testresult = 1;
378 
379     if (rp->done_join_all_threads) {
380         *testresult = rp->thread_composite_testresult;
381         return 1;
382     }
383 
384     for (i = 1; i < (size_t)sk_RADIX_THREAD_num(rp->threads); ++i) {
385         rt = sk_RADIX_THREAD_value(rp->threads, i);
386 
387         BIO_printf(bio_err, "==> Joining thread %zu\n", i);
388 
389         if (!TEST_true(RADIX_THREAD_join(rt)))
390             ok = 0;
391 
392         if (!rt->testresult)
393             composite_testresult = 0;
394     }
395 
396     rp->thread_composite_testresult = composite_testresult;
397     *testresult                     = composite_testresult;
398     rp->done_join_all_threads       = 1;
399 
400     RADIX_PROCESS_report_thread_results(rp, bio_err);
401     return ok;
402 }
403 
cleanup_one(RADIX_OBJ * obj)404 static void cleanup_one(RADIX_OBJ *obj)
405 {
406     obj->registered = 0;
407     RADIX_OBJ_free(obj);
408 }
409 
410 static void RADIX_THREAD_free(RADIX_THREAD *rt);
411 
RADIX_PROCESS_cleanup(RADIX_PROCESS * rp)412 static void RADIX_PROCESS_cleanup(RADIX_PROCESS *rp)
413 {
414     size_t i;
415 
416     assert(rp->done_join_all_threads);
417 
418     for (i = 0; i < (size_t)sk_RADIX_THREAD_num(rp->threads); ++i)
419         RADIX_THREAD_free(sk_RADIX_THREAD_value(rp->threads, i));
420 
421     sk_RADIX_THREAD_free(rp->threads);
422     rp->threads = NULL;
423 
424     lh_RADIX_OBJ_doall(rp->objs, cleanup_one);
425     lh_RADIX_OBJ_free(rp->objs);
426     rp->objs = NULL;
427 
428     BIO_free_all(rp->keylog_out);
429     rp->keylog_out = NULL;
430     ossl_crypto_mutex_free(&rp->gm);
431 }
432 
RADIX_PROCESS_get_obj(RADIX_PROCESS * rp,const char * name)433 static RADIX_OBJ *RADIX_PROCESS_get_obj(RADIX_PROCESS *rp, const char *name)
434 {
435     RADIX_OBJ key;
436 
437     key.name = (char *)name;
438     return lh_RADIX_OBJ_retrieve(rp->objs, &key);
439 }
440 
RADIX_PROCESS_set_obj(RADIX_PROCESS * rp,const char * name,RADIX_OBJ * obj)441 static int RADIX_PROCESS_set_obj(RADIX_PROCESS *rp,
442                                  const char *name, RADIX_OBJ *obj)
443 {
444     RADIX_OBJ *existing;
445 
446     if (obj != NULL && !TEST_false(obj->registered))
447         return 0;
448 
449     existing = RADIX_PROCESS_get_obj(rp, name);
450     if (existing != NULL && obj != existing) {
451         if (!TEST_true(existing->registered))
452             return 0;
453 
454         lh_RADIX_OBJ_delete(rp->objs, existing);
455         existing->registered = 0;
456         RADIX_OBJ_free(existing);
457     }
458 
459     if (obj != NULL) {
460         lh_RADIX_OBJ_insert(rp->objs, obj);
461         obj->registered = 1;
462     }
463 
464     return 1;
465 }
466 
RADIX_PROCESS_set_ssl(RADIX_PROCESS * rp,const char * name,SSL * ssl)467 static int RADIX_PROCESS_set_ssl(RADIX_PROCESS *rp, const char *name, SSL *ssl)
468 {
469     RADIX_OBJ *obj;
470 
471     if (!TEST_ptr(obj = RADIX_OBJ_new(name, ssl)))
472         return 0;
473 
474     if (!TEST_true(RADIX_PROCESS_set_obj(rp, name, obj))) {
475         RADIX_OBJ_free(obj);
476         return 0;
477     }
478 
479     return 1;
480 }
481 
RADIX_PROCESS_get_ssl(RADIX_PROCESS * rp,const char * name)482 static SSL *RADIX_PROCESS_get_ssl(RADIX_PROCESS *rp, const char *name)
483 {
484     RADIX_OBJ *obj = RADIX_PROCESS_get_obj(rp, name);
485 
486     if (obj == NULL)
487         return NULL;
488 
489     return obj->ssl;
490 }
491 
RADIX_THREAD_new(RADIX_PROCESS * rp)492 static RADIX_THREAD *RADIX_THREAD_new(RADIX_PROCESS *rp)
493 {
494     RADIX_THREAD *rt;
495 
496     if (!TEST_ptr(rp)
497         || !TEST_ptr(rt = OPENSSL_zalloc(sizeof(*rt))))
498         return 0;
499 
500     rt->rp          = rp;
501 
502 #if defined(OPENSSL_THREADS)
503     if (!TEST_ptr(rt->m = ossl_crypto_mutex_new())) {
504         OPENSSL_free(rt);
505         return 0;
506     }
507 #endif
508 
509     if (!TEST_true(sk_RADIX_THREAD_push(rp->threads, rt))) {
510         OPENSSL_free(rt);
511         return 0;
512     }
513 
514     rt->thread_idx  = rp->next_thread_idx++;
515     assert(rt->thread_idx + 1 == (size_t)sk_RADIX_THREAD_num(rp->threads));
516     return rt;
517 }
518 
RADIX_THREAD_free(RADIX_THREAD * rt)519 static void RADIX_THREAD_free(RADIX_THREAD *rt)
520 {
521     if (rt == NULL)
522         return;
523 
524     assert(rt->t == NULL);
525     BIO_free_all(rt->debug_bio);
526     OPENSSL_free(rt->tmp_buf);
527     ossl_crypto_mutex_free(&rt->m);
528     OPENSSL_free(rt);
529 }
530 
RADIX_THREAD_join(RADIX_THREAD * rt)531 static int RADIX_THREAD_join(RADIX_THREAD *rt)
532 {
533     CRYPTO_THREAD_RETVAL rv;
534 
535     if (rt->t != NULL)
536         ossl_crypto_thread_native_join(rt->t, &rv);
537 
538     ossl_crypto_thread_native_clean(rt->t);
539     rt->t = NULL;
540 
541     if (!TEST_true(rt->done))
542         return 0;
543 
544     return 1;
545 }
546 
547 static RADIX_PROCESS        radix_process;
548 static CRYPTO_THREAD_LOCAL  radix_thread;
549 
radix_thread_cleanup_tl(void * p)550 static void radix_thread_cleanup_tl(void *p)
551 {
552     /* Should already have been cleaned up. */
553     if (!TEST_ptr_null(p))
554         abort();
555 }
556 
radix_get_thread(void)557 static RADIX_THREAD *radix_get_thread(void)
558 {
559     return CRYPTO_THREAD_get_local(&radix_thread);
560 }
561 
radix_thread_init(RADIX_THREAD * rt)562 static int radix_thread_init(RADIX_THREAD *rt)
563 {
564     if (!TEST_ptr(rt)
565         || !TEST_ptr_null(CRYPTO_THREAD_get_local(&radix_thread)))
566         return 0;
567 
568     if (!TEST_true(CRYPTO_THREAD_set_local(&radix_thread, rt)))
569         return 0;
570 
571     set_override_bio_out(rt->debug_bio);
572     set_override_bio_err(rt->debug_bio);
573     return 1;
574 }
575 
radix_thread_cleanup(void)576 static void radix_thread_cleanup(void)
577 {
578     RADIX_THREAD *rt = radix_get_thread();
579 
580     if (!TEST_ptr(rt))
581         return;
582 
583     if (!TEST_true(CRYPTO_THREAD_set_local(&radix_thread, NULL)))
584         return;
585 }
586 
bindings_process_init(size_t node_idx,size_t process_idx)587 static int bindings_process_init(size_t node_idx, size_t process_idx)
588 {
589     RADIX_THREAD *rt;
590 
591     if (!TEST_true(RADIX_PROCESS_init(&radix_process, node_idx, process_idx)))
592         return 0;
593 
594     if (!TEST_true(CRYPTO_THREAD_init_local(&radix_thread,
595                                             radix_thread_cleanup_tl)))
596         return 0;
597 
598     if (!TEST_ptr(rt = RADIX_THREAD_new(&radix_process)))
599         return 0;
600 
601     /* Allocate structures for main thread. */
602     return radix_thread_init(rt);
603 }
604 
bindings_process_finish(int testresult_main)605 static int bindings_process_finish(int testresult_main)
606 {
607     int testresult, testresult_child;
608 
609     if (!TEST_true(RADIX_PROCESS_join_all_threads(&radix_process,
610                                                   &testresult_child)))
611         return 0;
612 
613     testresult = testresult_main && testresult_child;
614     RADIX_PROCESS_report_state(&radix_process, bio_err,
615                                /*verbose=*/!testresult);
616     radix_thread_cleanup(); /* cleanup main thread */
617     RADIX_PROCESS_cleanup(&radix_process);
618 
619     if (testresult)
620         BIO_printf(bio_err, "==> OK\n\n");
621     else
622         BIO_printf(bio_err, "==> ERROR (main=%d, children=%d)\n\n",
623                    testresult_main, testresult_child);
624 
625     return testresult;
626 }
627 
628 #define RP()    (&radix_process)
629 #define RT()    (radix_get_thread())
630 
get_time(void * arg)631 static OSSL_TIME get_time(void *arg)
632 {
633     OSSL_TIME time_slip;
634 
635     ossl_crypto_mutex_lock(RP()->gm);
636     time_slip = RP()->time_slip;
637     ossl_crypto_mutex_unlock(RP()->gm);
638 
639     return ossl_time_add(ossl_time_now(), time_slip);
640 }
641 
radix_skip_time(OSSL_TIME t)642 ossl_unused static void radix_skip_time(OSSL_TIME t)
643 {
644     ossl_crypto_mutex_lock(RP()->gm);
645     RP()->time_slip = ossl_time_add(RP()->time_slip, t);
646     ossl_crypto_mutex_unlock(RP()->gm);
647 }
648 
per_op_tick_obj(RADIX_OBJ * obj)649 static void per_op_tick_obj(RADIX_OBJ *obj)
650 {
651     if (obj->active)
652         SSL_handle_events(obj->ssl);
653 }
654 
do_per_op(TERP * terp,void * arg)655 static int do_per_op(TERP *terp, void *arg)
656 {
657     lh_RADIX_OBJ_doall(RP()->objs, per_op_tick_obj);
658     return 1;
659 }
660 
bindings_adjust_terp_config(TERP_CONFIG * cfg)661 static int bindings_adjust_terp_config(TERP_CONFIG *cfg)
662 {
663     cfg->now_cb     = get_time;
664     cfg->per_op_cb  = do_per_op;
665     return 1;
666 }
667 
expect_slot_ssl(FUNC_CTX * fctx,size_t idx,SSL ** p_ssl)668 static int expect_slot_ssl(FUNC_CTX *fctx, size_t idx, SSL **p_ssl)
669 {
670     if (!TEST_size_t_lt(idx, NUM_SLOTS)
671         || !TEST_ptr(*p_ssl = RT()->ssl[idx]))
672         return 0;
673 
674     return 1;
675 }
676 
677 #define REQUIRE_SSL_N(idx, ssl)                                 \
678     do {                                                        \
679         (ssl) = NULL; /* quiet uninitialized warnings */        \
680         if (!TEST_true(expect_slot_ssl(fctx, (idx), &(ssl))))   \
681             goto err;                                           \
682     } while (0)
683 #define REQUIRE_SSL(ssl)    REQUIRE_SSL_N(0, (ssl))
684 
685 #define REQUIRE_SSL_2(a, b)                                     \
686     do {                                                        \
687         REQUIRE_SSL_N(0, (a));                                  \
688         REQUIRE_SSL_N(1, (b));                                  \
689     } while (0)
690 
691 #define REQUIRE_SSL_3(a, b, c)                                  \
692     do {                                                        \
693         REQUIRE_SSL_N(0, (a));                                  \
694         REQUIRE_SSL_N(1, (b));                                  \
695         REQUIRE_SSL_N(2, (c));                                  \
696     } while (0)
697 
698 #define REQUIRE_SSL_4(a, b, c, d)                               \
699     do {                                                        \
700         REQUIRE_SSL_N(0, (a));                                  \
701         REQUIRE_SSL_N(1, (b));                                  \
702         REQUIRE_SSL_N(2, (c));                                  \
703         REQUIRE_SSL_N(3, (d));                                  \
704     } while (0)
705 
706 #define REQUIRE_SSL_5(a, b, c, d, e)                            \
707     do {                                                        \
708         REQUIRE_SSL_N(0, (a));                                  \
709         REQUIRE_SSL_N(1, (b));                                  \
710         REQUIRE_SSL_N(2, (c));                                  \
711         REQUIRE_SSL_N(3, (d));                                  \
712         REQUIRE_SSL_N(4, (e));                                  \
713     } while (0)
714 
715 #define C_BIDI_ID(ordinal) \
716     (((ordinal) << 2) | QUIC_STREAM_INITIATOR_CLIENT | QUIC_STREAM_DIR_BIDI)
717 #define S_BIDI_ID(ordinal) \
718     (((ordinal) << 2) | QUIC_STREAM_INITIATOR_SERVER | QUIC_STREAM_DIR_BIDI)
719 #define C_UNI_ID(ordinal) \
720     (((ordinal) << 2) | QUIC_STREAM_INITIATOR_CLIENT | QUIC_STREAM_DIR_UNI)
721 #define S_UNI_ID(ordinal) \
722     (((ordinal) << 2) | QUIC_STREAM_INITIATOR_SERVER | QUIC_STREAM_DIR_UNI)
723 
724 #if defined(OPENSSL_THREADS)
725 
RADIX_THREAD_worker_run(RADIX_THREAD * rt)726 static int RADIX_THREAD_worker_run(RADIX_THREAD *rt)
727 {
728     int ok = 0;
729     TERP_CONFIG cfg = {0};
730 
731     cfg.debug_bio = rt->debug_bio;
732     if (!TEST_true(bindings_adjust_terp_config(&cfg)))
733         goto err;
734 
735     if (!TERP_run(rt->child_script_info, &cfg))
736         goto err;
737 
738     ok = 1;
739 err:
740     return ok;
741 }
742 
RADIX_THREAD_worker_main(void * p)743 static unsigned int RADIX_THREAD_worker_main(void *p)
744 {
745     int testresult = 0;
746     RADIX_THREAD *rt = p;
747 
748     if (!TEST_true(radix_thread_init(rt)))
749         return 0;
750 
751     /* Wait until thread-specific init is done (e.g. setting rt->t) */
752     ossl_crypto_mutex_lock(rt->m);
753     ossl_crypto_mutex_unlock(rt->m);
754 
755     testresult = RADIX_THREAD_worker_run(rt);
756 
757     ossl_crypto_mutex_lock(rt->m);
758     rt->testresult  = testresult;
759     rt->done        = 1;
760     ossl_crypto_mutex_unlock(rt->m);
761 
762     radix_thread_cleanup();
763     return 1;
764 }
765 
766 #endif
767 
radix_activate_obj(RADIX_OBJ * obj)768 static void radix_activate_obj(RADIX_OBJ *obj)
769 {
770     if (obj != NULL)
771         obj->active = 1;
772 }
773 
radix_activate_slot(size_t idx)774 static void radix_activate_slot(size_t idx)
775 {
776     if (idx >= NUM_SLOTS)
777         return;
778 
779     radix_activate_obj(RT()->slot[idx]);
780 }
781 
DEF_FUNC(hf_spawn_thread)782 DEF_FUNC(hf_spawn_thread)
783 {
784     int ok = 0;
785     RADIX_THREAD *child_rt = NULL;
786     SCRIPT_INFO *script_info = NULL;
787 
788     F_POP(script_info);
789     if (!TEST_ptr(script_info))
790         goto err;
791 
792 #if !defined(OPENSSL_THREADS)
793     TEST_skip("threading not supported, skipping");
794     F_SKIP_REST();
795 #else
796     if (!TEST_ptr(child_rt = RADIX_THREAD_new(&radix_process)))
797         return 0;
798 
799     if (!TEST_ptr(child_rt->debug_bio = BIO_new(BIO_s_mem())))
800         goto err;
801 
802     ossl_crypto_mutex_lock(child_rt->m);
803 
804     child_rt->child_script_info = script_info;
805     if (!TEST_ptr(child_rt->t = ossl_crypto_thread_native_start(RADIX_THREAD_worker_main,
806                                                                 child_rt, 1))) {
807         ossl_crypto_mutex_unlock(child_rt->m);
808         goto err;
809     }
810 
811     ossl_crypto_mutex_unlock(child_rt->m);
812     ok = 1;
813 #endif
814 err:
815     if (!ok)
816         RADIX_THREAD_free(child_rt);
817 
818     return ok;
819 }
820 
DEF_FUNC(hf_clear)821 DEF_FUNC(hf_clear)
822 {
823     RADIX_THREAD *rt = RT();
824     size_t i;
825 
826     ossl_crypto_mutex_lock(RP()->gm);
827 
828     lh_RADIX_OBJ_doall(RP()->objs, cleanup_one);
829     lh_RADIX_OBJ_flush(RP()->objs);
830 
831     for (i = 0; i < NUM_SLOTS; ++i) {
832         rt->slot[i] = NULL;
833         rt->ssl[i]  = NULL;
834     }
835 
836     ossl_crypto_mutex_unlock(RP()->gm);
837     return 1;
838 }
839 
840 #define OP_SPAWN_THREAD(script_name)                            \
841     (OP_PUSH_P(SCRIPT(script_name)), OP_FUNC(hf_spawn_thread))
842 #define OP_CLEAR()                                              \
843     (OP_FUNC(hf_clear))
844