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