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