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 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 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 138 static unsigned long RADIX_OBJ_hash(const RADIX_OBJ *obj) 139 { 140 return OPENSSL_LH_strhash(obj->name); 141 } 142 143 static int RADIX_OBJ_cmp(const RADIX_OBJ *a, const RADIX_OBJ *b) 144 { 145 return strcmp(a->name, b->name); 146 } 147 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 557 static RADIX_THREAD *radix_get_thread(void) 558 { 559 return CRYPTO_THREAD_get_local(&radix_thread); 560 } 561 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 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 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 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 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 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 649 static void per_op_tick_obj(RADIX_OBJ *obj) 650 { 651 if (obj->active) 652 SSL_handle_events(obj->ssl); 653 } 654 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 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 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 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 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 768 static void radix_activate_obj(RADIX_OBJ *obj) 769 { 770 if (obj != NULL) 771 obj->active = 1; 772 } 773 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 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 child_rt->child_script_info = script_info; 803 804 ossl_crypto_mutex_lock(child_rt->m); 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 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