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/ssl.h>
10 #include <openssl/quic.h>
11 #include <openssl/bio.h>
12 #include <openssl/lhash.h>
13 #include <openssl/rand.h>
14 #include "../testutil.h"
15 #include "internal/numbers.h" /* UINT64_C */
16 #include "internal/time.h" /* OSSL_TIME */
17
18 static const char *cert_file, *key_file;
19
20 /*
21 * TERP - Test Executive Script Interpreter
22 * ========================================
23 */
24 typedef struct gen_ctx_st GEN_CTX;
25
26 typedef void (*script_gen_t)(GEN_CTX *ctx);
27
28 typedef struct script_info_st {
29 /* name: A symbolic name, like simple_conn. */
30 const char *name;
31 /* desc: A short, one-line description. */
32 const char *desc;
33 const char *file;
34 int line;
35 /* gen_func: The script generation function. */
36 script_gen_t gen_func;
37 } SCRIPT_INFO;
38
39 struct gen_ctx_st {
40 SCRIPT_INFO *script_info;
41 const char *cur_file;
42 int error, cur_line;
43 const char *first_error_msg, *first_error_file;
44 int first_error_line;
45
46 uint8_t *build_buf_beg, *build_buf_cur, *build_buf_end;
47 };
48
GEN_CTX_init(GEN_CTX * ctx,SCRIPT_INFO * script_info)49 static int GEN_CTX_init(GEN_CTX *ctx, SCRIPT_INFO *script_info)
50 {
51 ctx->script_info = script_info;
52 ctx->error = 0;
53 ctx->cur_file = NULL;
54 ctx->cur_line = 0;
55 ctx->first_error_msg = NULL;
56 ctx->first_error_line = 0;
57 ctx->build_buf_beg = NULL;
58 ctx->build_buf_cur = NULL;
59 ctx->build_buf_end = NULL;
60 return 1;
61 }
62
GEN_CTX_cleanup(GEN_CTX * ctx)63 static void GEN_CTX_cleanup(GEN_CTX *ctx)
64 {
65 OPENSSL_free(ctx->build_buf_beg);
66 ctx->build_buf_beg = ctx->build_buf_cur = ctx->build_buf_end = NULL;
67 }
68
69 typedef struct terp_st TERP;
70
71 #define F_RET_SPIN_AGAIN 2
72 #define F_RET_SKIP_REST 3
73
74 #define F_SPIN_AGAIN() \
75 do { \
76 ok = F_RET_SPIN_AGAIN; \
77 fctx->spin_again = 1; \
78 goto err; \
79 } while (0)
80
81 #define F_SKIP_REST() \
82 do { \
83 ok = F_RET_SKIP_REST; \
84 fctx->skip_rest = 1; \
85 goto err; \
86 } while (0)
87
88 typedef struct func_ctx_st {
89 TERP *terp;
90
91 /*
92 * Set to 1 inside a user function if the function should spin again.
93 * Cleared automatically after the user function returns.
94 */
95 int spin_again;
96
97 /*
98 * Immediately exit script successfully. Useful for skipping.
99 */
100 int skip_rest;
101 } FUNC_CTX;
102
103 static ossl_inline int TERP_stk_pop(TERP *terp,
104 void *buf, size_t buf_len);
105
106 #define TERP_STK_PUSH(terp, v) \
107 do { \
108 if (!TEST_true(TERP_stk_push((terp), &(v), sizeof(v)))) \
109 goto err; \
110 } while (0)
111
112 #define TERP_STK_POP(terp, v) \
113 do { \
114 memset(&(v), 0, sizeof(v)); /* quiet warnings */ \
115 if (!TEST_true(TERP_stk_pop((terp), &(v), sizeof(v)))) \
116 goto err; \
117 } while (0)
118
119 #define TERP_STK_POP2(terp, a, b) \
120 do { \
121 TERP_STK_POP((terp), (b)); \
122 TERP_STK_POP((terp), (a)); \
123 } while (0)
124
125 #define F_PUSH(v) TERP_STK_PUSH(fctx->terp, (v))
126 #define F_POP(v) TERP_STK_POP (fctx->terp, (v))
127 #define F_POP2(a, b) TERP_STK_POP2(fctx->terp, (a), (b))
128
129 typedef int (*helper_func_t)(FUNC_CTX *fctx);
130
131 #define DEF_FUNC(name) ossl_unused static int name(FUNC_CTX *fctx)
132
133 #define DEF_SCRIPT(name, desc) \
134 static void script_gen_##name(GEN_CTX *ctx); \
135 static SCRIPT_INFO script_info_##name = { \
136 #name, desc, __FILE__, __LINE__, \
137 script_gen_##name \
138 }; \
139 static void script_gen_##name(GEN_CTX *ctx)
140
141 enum {
142 OPK_INVALID,
143 OPK_END,
144 OPK_PUSH_P,
145 /*
146 * This is exactly like PUSH_P, but the script dumper knows the pointer
147 * points to a static NUL-terminated string and can therefore print it.
148 */
149 OPK_PUSH_PZ,
150 OPK_PUSH_U64,
151 /*
152 * Could use OPK_PUSH_U64 for this but it's annoying to have to avoid using
153 * size_t in case it is a different size.
154 */
155 OPK_PUSH_SIZE,
156 OPK_FUNC,
157 OPK_LABEL
158 };
159
160 static void *openc_alloc_space(GEN_CTX *ctx, size_t num_bytes);
161
162 #define DEF_ENCODER(name, type) \
163 static void name(GEN_CTX *ctx, type v) \
164 { \
165 void *dst = openc_alloc_space(ctx, sizeof(v)); \
166 if (dst == NULL) \
167 return; \
168 \
169 memcpy(dst, &v, sizeof(v)); \
170 }
171
DEF_ENCODER(openc_u64,uint64_t)172 DEF_ENCODER(openc_u64, uint64_t)
173 DEF_ENCODER(openc_size, size_t)
174 DEF_ENCODER(openc_p, void *)
175 DEF_ENCODER(openc_fp, helper_func_t)
176 #define openc_opcode openc_u64
177
178 static void opgen_END(GEN_CTX *ctx)
179 {
180 openc_opcode(ctx, OPK_END);
181 }
182
opgen_PUSH_P(GEN_CTX * ctx,void * p)183 static ossl_unused void opgen_PUSH_P(GEN_CTX *ctx, void *p)
184 {
185 openc_opcode(ctx, OPK_PUSH_P);
186 openc_p(ctx, p);
187 }
188
opgen_PUSH_PZ(GEN_CTX * ctx,void * p)189 static void opgen_PUSH_PZ(GEN_CTX *ctx, void *p)
190 {
191 openc_opcode(ctx, OPK_PUSH_PZ);
192 openc_p(ctx, p);
193 }
194
opgen_PUSH_U64(GEN_CTX * ctx,uint64_t v)195 static void opgen_PUSH_U64(GEN_CTX *ctx, uint64_t v)
196 {
197 openc_opcode(ctx, OPK_PUSH_U64);
198 openc_u64(ctx, v);
199 }
200
opgen_PUSH_SIZE(GEN_CTX * ctx,size_t v)201 ossl_unused static void opgen_PUSH_SIZE(GEN_CTX *ctx, size_t v)
202 {
203 openc_opcode(ctx, OPK_PUSH_SIZE);
204 openc_size(ctx, v);
205 }
206
opgen_FUNC(GEN_CTX * ctx,helper_func_t f,const char * f_name)207 ossl_unused static void opgen_FUNC(GEN_CTX *ctx, helper_func_t f,
208 const char *f_name)
209 {
210 openc_opcode(ctx, OPK_FUNC);
211 openc_fp(ctx, f);
212 openc_p(ctx, (void *)f_name);
213 }
214
opgen_LABEL(GEN_CTX * ctx,const char * name)215 ossl_unused static void opgen_LABEL(GEN_CTX *ctx, const char *name)
216 {
217 openc_opcode(ctx, OPK_LABEL);
218 openc_p(ctx, (void *)name);
219 }
220
opgen_set_line(GEN_CTX * ctx,const char * file,int line)221 static void opgen_set_line(GEN_CTX *ctx, const char *file, int line)
222 {
223 ctx->cur_file = file;
224 ctx->cur_line = line;
225 }
226
opgen_fail(GEN_CTX * ctx,const char * msg)227 static ossl_unused void opgen_fail(GEN_CTX *ctx, const char *msg)
228 {
229 if (!ctx->error) {
230 ctx->first_error_file = ctx->cur_file;
231 ctx->first_error_line = ctx->cur_line;
232 ctx->first_error_msg = msg;
233 }
234
235 ctx->error = 1;
236 }
237
238 #define OPGEN(n) (opgen_set_line(ctx, __FILE__, __LINE__), \
239 opgen_##n)
240 #define OP_END() OPGEN(END) (ctx)
241 #define OP_PUSH_P(v) OPGEN(PUSH_P) (ctx, (v))
242 #define OP_PUSH_PZ(v) OPGEN(PUSH_PZ) (ctx, (v))
243 #define OP_PUSH_U64(v) OPGEN(PUSH_U64) (ctx, (v))
244 #define OP_PUSH_SIZE(v) OPGEN(PUSH_SIZE) (ctx, (v))
245 #define OP_PUSH_BUFP(p, l) (OP_PUSH_P(p), OP_PUSH_SIZE(l))
246 #define OP_PUSH_BUF(v) OP_PUSH_BUFP(&(v), sizeof(v))
247 #define OP_PUSH_LREF(v) OPGEN(PUSH_LREF)(ctx, (lref))
248 #define OP_FUNC(f) OPGEN(FUNC) (ctx, (f), #f)
249 #define OP_LABEL(name) OPGEN(LABEL) (ctx, (name))
250 #define GEN_FAIL(msg) OPGEN(fail) (ctx, (msg))
251
openc_alloc_space(GEN_CTX * ctx,size_t num_bytes)252 static void *openc_alloc_space(GEN_CTX *ctx, size_t num_bytes)
253 {
254 void *p;
255 size_t cur_spare, old_size, new_size, off;
256
257 cur_spare = ctx->build_buf_end - ctx->build_buf_cur;
258 if (cur_spare < num_bytes) {
259 off = ctx->build_buf_cur - ctx->build_buf_beg;
260 old_size = ctx->build_buf_end - ctx->build_buf_beg;
261 new_size = (old_size == 0) ? 1024 : old_size * 2;
262 p = OPENSSL_realloc(ctx->build_buf_beg, new_size);
263 if (!TEST_ptr(p))
264 return NULL;
265
266 ctx->build_buf_beg = p;
267 ctx->build_buf_cur = ctx->build_buf_beg + off;
268 ctx->build_buf_end = ctx->build_buf_beg + new_size;
269 }
270
271 p = ctx->build_buf_cur;
272 ctx->build_buf_cur += num_bytes;
273 return p;
274 }
275
276 /*
277 * Script Interpreter
278 * ============================================================================
279 */
280 typedef struct gen_script_st {
281 const uint8_t *buf;
282 size_t buf_len;
283 } GEN_SCRIPT;
284
GEN_CTX_finish(GEN_CTX * ctx,GEN_SCRIPT * script)285 static int GEN_CTX_finish(GEN_CTX *ctx, GEN_SCRIPT *script)
286 {
287 script->buf = ctx->build_buf_beg;
288 script->buf_len = ctx->build_buf_cur - ctx->build_buf_beg;
289 ctx->build_buf_beg = ctx->build_buf_cur = ctx->build_buf_end = NULL;
290 return 1;
291 }
292
GEN_SCRIPT_cleanup(GEN_SCRIPT * script)293 static void GEN_SCRIPT_cleanup(GEN_SCRIPT *script)
294 {
295 OPENSSL_free((char *)script->buf);
296
297 script->buf = NULL;
298 script->buf_len = 0;
299 }
300
GEN_SCRIPT_init(GEN_SCRIPT * gen_script,SCRIPT_INFO * script_info)301 static int GEN_SCRIPT_init(GEN_SCRIPT *gen_script, SCRIPT_INFO *script_info)
302 {
303 int ok = 0;
304 GEN_CTX gctx;
305
306 if (!TEST_true(GEN_CTX_init(&gctx, script_info)))
307 return 0;
308
309 script_info->gen_func(&gctx);
310 opgen_END(&gctx);
311
312 if (!TEST_false(gctx.error))
313 goto err;
314
315 if (!TEST_true(GEN_CTX_finish(&gctx, gen_script)))
316 goto err;
317
318 ok = 1;
319 err:
320 if (!ok) {
321 if (gctx.error)
322 TEST_error("script generation failed: %s (at %s:%d)",
323 gctx.first_error_msg,
324 gctx.first_error_file,
325 gctx.first_error_line);
326
327 GEN_CTX_cleanup(&gctx);
328 }
329 return ok;
330 }
331
332 typedef struct srdr_st {
333 const uint8_t *beg, *cur, *end, *save_cur;
334 } SRDR;
335
SRDR_init(SRDR * rdr,const uint8_t * buf,size_t buf_len)336 static void SRDR_init(SRDR *rdr, const uint8_t *buf, size_t buf_len)
337 {
338 rdr->beg = rdr->cur = buf;
339 rdr->end = rdr->beg + buf_len;
340 rdr->save_cur = NULL;
341 }
342
SRDR_get_operand(SRDR * srdr,void * buf,size_t buf_len)343 static ossl_inline int SRDR_get_operand(SRDR *srdr, void *buf, size_t buf_len)
344 {
345 if (!TEST_size_t_ge(srdr->end - srdr->cur, buf_len))
346 return 0; /* malformed script */
347
348 memcpy(buf, srdr->cur, buf_len);
349 srdr->cur += buf_len;
350 return 1;
351 }
352
SRDR_save(SRDR * srdr)353 static ossl_inline void SRDR_save(SRDR *srdr)
354 {
355 srdr->save_cur = srdr->cur;
356 }
357
SRDR_restore(SRDR * srdr)358 static ossl_inline void SRDR_restore(SRDR *srdr)
359 {
360 srdr->cur = srdr->save_cur;
361 }
362
363 #define GET_OPERAND(srdr, v) \
364 do { \
365 memset(&(v), 0, sizeof(v)); /* quiet uninitialized warn */ \
366 if (!TEST_true(SRDR_get_operand(srdr, &(v), sizeof(v)))) \
367 goto err; \
368 } while (0)
369
370
print_opc(BIO * bio,size_t op_num,size_t offset,const char * name)371 static void print_opc(BIO *bio, size_t op_num, size_t offset, const char *name)
372 {
373 if (op_num != SIZE_MAX)
374 BIO_printf(bio, "%3zu- %4zx>\t%-8s \t", op_num,
375 offset, name);
376 else
377 BIO_printf(bio, " %4zx>\t%-8s \t",
378 offset, name);
379 }
380
SRDR_print_one(SRDR * srdr,BIO * bio,size_t i,int * was_end)381 static int SRDR_print_one(SRDR *srdr, BIO *bio, size_t i, int *was_end)
382 {
383 int ok = 0;
384 const uint8_t *opc_start;
385 uint64_t opc;
386
387 if (was_end != NULL)
388 *was_end = 0;
389
390 opc_start = srdr->cur;
391 GET_OPERAND(srdr, opc);
392
393 #define PRINT_OPC(name) print_opc(bio, i, (size_t)(opc_start - srdr->beg), #name)
394
395 switch (opc) {
396 case OPK_END:
397 PRINT_OPC(END);
398 opc_start = srdr->cur;
399 if (was_end != NULL)
400 *was_end = 1;
401 break;
402 case OPK_PUSH_P:
403 {
404 void *v;
405
406 GET_OPERAND(srdr, v);
407 PRINT_OPC(PUSH_P);
408 BIO_printf(bio, "%20p", v);
409 }
410 break;
411 case OPK_PUSH_PZ:
412 {
413 void *v;
414
415 GET_OPERAND(srdr, v);
416 PRINT_OPC(PUSH_PZ);
417 if (v != NULL && strlen((const char *)v) == 1)
418 BIO_printf(bio, "%20p (%s)", v, (const char *)v);
419 else
420 BIO_printf(bio, "%20p (\"%s\")", v, (const char *)v);
421 }
422 break;
423 case OPK_PUSH_U64:
424 {
425 uint64_t v;
426
427 GET_OPERAND(srdr, v);
428 PRINT_OPC(PUSH_U64);
429 BIO_printf(bio, "%#20llx (%llu)",
430 (unsigned long long)v, (unsigned long long)v);
431 }
432 break;
433 case OPK_PUSH_SIZE:
434 {
435 size_t v;
436
437 GET_OPERAND(srdr, v);
438 PRINT_OPC(PUSH_SIZE);
439 BIO_printf(bio, "%#20llx (%llu)",
440 (unsigned long long)v, (unsigned long long)v);
441 }
442 break;
443 case OPK_FUNC:
444 {
445 helper_func_t v;
446 void *f_name = NULL, *x = NULL;
447
448 GET_OPERAND(srdr, v);
449 GET_OPERAND(srdr, f_name);
450
451 PRINT_OPC(FUNC);
452 memcpy(&x, &v, sizeof(x) < sizeof(v) ? sizeof(x) : sizeof(v));
453 BIO_printf(bio, "%s", (const char *)f_name);
454 }
455 break;
456 case OPK_LABEL:
457 {
458 void *l_name;
459
460 GET_OPERAND(srdr, l_name);
461
462 BIO_printf(bio, "\n%s:\n", (const char *)l_name);
463 PRINT_OPC(LABEL);
464 }
465 break;
466 default:
467 TEST_error("unsupported opcode while printing: %llu",
468 (unsigned long long)opc);
469 goto err;
470 }
471
472 ok = 1;
473 err:
474 return ok;
475 }
476
GEN_SCRIPT_print(GEN_SCRIPT * gen_script,BIO * bio,const SCRIPT_INFO * script_info)477 static int GEN_SCRIPT_print(GEN_SCRIPT *gen_script, BIO *bio,
478 const SCRIPT_INFO *script_info)
479 {
480 int ok = 0;
481 size_t i;
482 SRDR srdr_v, *srdr = &srdr_v;
483 int was_end = 0;
484
485 SRDR_init(srdr, gen_script->buf, gen_script->buf_len);
486
487 if (script_info != NULL) {
488 BIO_printf(bio, "\nGenerated script for '%s':\n",
489 script_info->name);
490 BIO_printf(bio, "\n--GENERATED-------------------------------------"
491 "----------------------\n");
492 BIO_printf(bio, " # NAME:\n # %s\n",
493 script_info->name);
494 BIO_printf(bio, " # SOURCE:\n # %s:%d\n",
495 script_info->file, script_info->line);
496 BIO_printf(bio, " # DESCRIPTION:\n # %s\n", script_info->desc);
497 }
498
499 for (i = 0; !was_end; ++i) {
500 BIO_printf(bio, "\n");
501
502 if (!TEST_true(SRDR_print_one(srdr, bio, i, &was_end)))
503 goto err;
504 }
505
506 if (script_info != NULL) {
507 const unsigned char *opc_start = srdr->cur;
508
509 BIO_printf(bio, "\n");
510 PRINT_OPC(+++);
511 BIO_printf(bio, "\n------------------------------------------------"
512 "----------------------\n\n");
513 }
514
515 ok = 1;
516 err:
517 return ok;
518 }
519
SCRIPT_INFO_print(SCRIPT_INFO * script_info,BIO * bio,int error,const char * msg)520 static void SCRIPT_INFO_print(SCRIPT_INFO *script_info, BIO *bio, int error,
521 const char *msg)
522 {
523 if (error)
524 TEST_error("%s: script '%s' (%s)",
525 msg, script_info->name, script_info->desc);
526 else
527 TEST_info("%s: script '%s' (%s)",
528 msg, script_info->name, script_info->desc);
529 }
530
531 typedef struct terp_config_st {
532 BIO *debug_bio;
533
534 OSSL_TIME (*now_cb)(void *arg);
535 void *now_cb_arg;
536
537 int (*per_op_cb)(TERP *terp, void *arg);
538 void *per_op_cb_arg;
539
540 OSSL_TIME max_execution_time; /* duration */
541 } TERP_CONFIG;
542
543 #define TERP_DEFAULT_MAX_EXECUTION_TIME (ossl_ms2time(3000))
544
545 struct terp_st {
546 TERP_CONFIG cfg;
547 const SCRIPT_INFO *script_info;
548 const GEN_SCRIPT *gen_script;
549 SRDR srdr;
550 uint8_t *stk_beg, *stk_cur, *stk_end, *stk_save_cur;
551 FUNC_CTX fctx;
552 uint64_t ops_executed;
553 int log_execute;
554 OSSL_TIME start_time, deadline_time;
555 };
556
TERP_init(TERP * terp,const TERP_CONFIG * cfg,const SCRIPT_INFO * script_info,const GEN_SCRIPT * gen_script)557 static int TERP_init(TERP *terp,
558 const TERP_CONFIG *cfg,
559 const SCRIPT_INFO *script_info,
560 const GEN_SCRIPT *gen_script)
561 {
562 if (!TEST_true(cfg->now_cb != NULL))
563 return 0;
564
565 terp->cfg = *cfg;
566 terp->script_info = script_info;
567 terp->gen_script = gen_script;
568 terp->fctx.terp = terp;
569 terp->fctx.spin_again = 0;
570 terp->fctx.skip_rest = 0;
571 terp->stk_beg = NULL;
572 terp->stk_cur = NULL;
573 terp->stk_end = NULL;
574 terp->stk_save_cur = NULL;
575 terp->ops_executed = 0;
576 terp->log_execute = 1;
577
578 if (ossl_time_is_zero(terp->cfg.max_execution_time))
579 terp->cfg.max_execution_time = TERP_DEFAULT_MAX_EXECUTION_TIME;
580
581 return 1;
582 }
583
TERP_cleanup(TERP * terp)584 static void TERP_cleanup(TERP *terp)
585 {
586 if (terp->script_info == NULL)
587 return;
588
589 OPENSSL_free(terp->stk_beg);
590 terp->stk_beg = terp->stk_cur = terp->stk_end = NULL;
591 terp->script_info = NULL;
592 }
593
TERP_stk_ensure_capacity(TERP * terp,size_t spare)594 static int TERP_stk_ensure_capacity(TERP *terp, size_t spare)
595 {
596 uint8_t *p;
597 size_t old_size, new_size, off;
598
599 old_size = terp->stk_end - terp->stk_beg;
600 if (old_size >= spare)
601 return 1;
602
603 off = terp->stk_end - terp->stk_cur;
604 new_size = old_size != 0 ? old_size * 2 : 256;
605 p = OPENSSL_realloc(terp->stk_beg, new_size);
606 if (!TEST_ptr(p))
607 return 0;
608
609 terp->stk_beg = p;
610 terp->stk_end = terp->stk_beg + new_size;
611 terp->stk_cur = terp->stk_end - off;
612 return 1;
613 }
614
TERP_stk_push(TERP * terp,const void * buf,size_t buf_len)615 static ossl_inline int TERP_stk_push(TERP *terp,
616 const void *buf, size_t buf_len)
617 {
618 if (!TEST_true(TERP_stk_ensure_capacity(terp, buf_len)))
619 return 0;
620
621 terp->stk_cur -= buf_len;
622 memcpy(terp->stk_cur, buf, buf_len);
623 return 1;
624 }
625
TERP_stk_pop(TERP * terp,void * buf,size_t buf_len)626 static ossl_inline int TERP_stk_pop(TERP *terp,
627 void *buf, size_t buf_len)
628 {
629 if (!TEST_size_t_ge(terp->stk_end - terp->stk_cur, buf_len))
630 return 0;
631
632 memcpy(buf, terp->stk_cur, buf_len);
633 terp->stk_cur += buf_len;
634 return 1;
635 }
636
TERP_print_stack(TERP * terp,BIO * bio,const char * header)637 static void TERP_print_stack(TERP *terp, BIO *bio, const char *header)
638 {
639 test_output_memory(header, terp->stk_cur, terp->stk_end - terp->stk_cur);
640 BIO_printf(bio, " (%zu bytes)\n", (size_t)(terp->stk_end - terp->stk_cur));
641 BIO_printf(bio, "\n");
642 }
643
644 #define TERP_GET_OPERAND(v) GET_OPERAND(&terp->srdr, (v))
645
646 #define TERP_SPIN_AGAIN() \
647 do { \
648 SRDR_restore(&terp->srdr); \
649 terp->stk_cur = terp->stk_save_cur; \
650 ++spin_count; \
651 goto spin_again; \
652 } while (0)
653
TERP_now(TERP * terp)654 static OSSL_TIME TERP_now(TERP *terp)
655 {
656 return terp->cfg.now_cb(terp->cfg.now_cb_arg);
657 }
658
TERP_log_spin(TERP * terp,size_t spin_count)659 static void TERP_log_spin(TERP *terp, size_t spin_count)
660 {
661 if (spin_count > 0)
662 BIO_printf(terp->cfg.debug_bio, " \t\t(span %zu times)\n",
663 spin_count);
664 }
665
TERP_execute(TERP * terp)666 static int TERP_execute(TERP *terp)
667 {
668 int ok = 0;
669 uint64_t opc;
670 size_t op_num = 0;
671 int in_debug_output = 0;
672 size_t spin_count = 0;
673 BIO *debug_bio = terp->cfg.debug_bio;
674
675 SRDR_init(&terp->srdr, terp->gen_script->buf, terp->gen_script->buf_len);
676
677 terp->start_time = TERP_now(terp);
678 terp->deadline_time = ossl_time_add(terp->start_time,
679 terp->cfg.max_execution_time);
680
681 for (;;) {
682 if (terp->log_execute) {
683 SRDR srdr_copy = terp->srdr;
684
685 if (!in_debug_output) {
686 BIO_printf(debug_bio, "\n--EXECUTION-----------------------------"
687 "------------------------------\n");
688 in_debug_output = 1;
689 }
690
691 TERP_log_spin(terp, spin_count);
692 if (!TEST_true(SRDR_print_one(&srdr_copy, debug_bio, SIZE_MAX, NULL)))
693 goto err;
694
695 BIO_printf(debug_bio, "\n");
696 }
697
698 TERP_GET_OPERAND(opc);
699 ++op_num;
700 SRDR_save(&terp->srdr);
701 terp->stk_save_cur = terp->stk_cur;
702 spin_count = 0;
703
704 ++terp->ops_executed;
705
706 spin_again:
707 if (ossl_time_compare(TERP_now(terp), terp->deadline_time) >= 0) {
708 TEST_error("timed out while executing op %zu", op_num);
709 if (terp->log_execute)
710 TERP_log_spin(terp, spin_count);
711 goto err;
712 }
713
714 if (terp->cfg.per_op_cb != NULL)
715 if (!TEST_true(terp->cfg.per_op_cb(terp, terp->cfg.per_op_cb_arg))) {
716 TEST_error("pre-operation processing failed at op %zu", op_num);
717 if (terp->log_execute)
718 TERP_log_spin(terp, spin_count);
719 goto err;
720 }
721
722 switch (opc) {
723 case OPK_END:
724 goto stop;
725 case OPK_PUSH_P:
726 case OPK_PUSH_PZ:
727 {
728 void *v;
729
730 TERP_GET_OPERAND(v);
731 TERP_STK_PUSH(terp, v);
732 }
733 break;
734 case OPK_PUSH_U64:
735 {
736 uint64_t v;
737
738 TERP_GET_OPERAND(v);
739 TERP_STK_PUSH(terp, v);
740 }
741 break;
742 case OPK_PUSH_SIZE:
743 {
744 size_t v;
745
746 TERP_GET_OPERAND(v);
747 TERP_STK_PUSH(terp, v);
748 }
749 break;
750 case OPK_LABEL:
751 {
752 const char *l_name;
753
754 TERP_GET_OPERAND(l_name);
755 /* no-op */
756 }
757 break;
758 case OPK_FUNC:
759 {
760 helper_func_t v;
761 const void *f_name;
762 int ret;
763
764 TERP_GET_OPERAND(v);
765 TERP_GET_OPERAND(f_name);
766
767 if (!TEST_true(v != NULL))
768 goto err;
769
770 ret = v(&terp->fctx);
771
772 if (terp->fctx.skip_rest) {
773 if (!TEST_int_eq(ret, F_RET_SKIP_REST))
774 goto err;
775
776 if (terp->log_execute)
777 BIO_printf(terp->cfg.debug_bio, " \t\t(skipping)\n");
778
779 terp->fctx.skip_rest = 0;
780 goto stop;
781 } else if (terp->fctx.spin_again) {
782 if (!TEST_int_eq(ret, F_RET_SPIN_AGAIN))
783 goto err;
784
785 terp->fctx.spin_again = 0;
786 TERP_SPIN_AGAIN();
787 } else {
788 if (!TEST_false(terp->fctx.spin_again))
789 goto err;
790
791 if (ret != 1) {
792 TEST_error("op %zu (FUNC %s) failed with return value %d",
793 op_num, (const char *)f_name, ret);
794 goto err;
795 }
796 }
797 }
798 break;
799 default:
800 TEST_error("unknown opcode: %llu", (unsigned long long)opc);
801 goto err;
802 }
803 }
804
805 stop:
806 ok = 1;
807 err:
808 if (in_debug_output)
809 BIO_printf(debug_bio, "----------------------------------------"
810 "------------------------------\n");
811
812 if (!ok) {
813 TEST_error("FAILED while executing script: %s at op %zu, error stack:",
814 terp->script_info->name, op_num);
815 ERR_print_errors(terp->cfg.debug_bio);
816 BIO_printf(debug_bio, "\n");
817 } else if (ERR_peek_last_error() != 0) {
818 TEST_info("WARNING: errors on error stack despite success:");
819 ERR_print_errors(terp->cfg.debug_bio);
820 BIO_printf(debug_bio, "\n");
821 }
822
823 return ok;
824 }
825
TERP_run(SCRIPT_INFO * script_info,TERP_CONFIG * cfg)826 static int TERP_run(SCRIPT_INFO *script_info, TERP_CONFIG *cfg)
827 {
828 int ok = 0, have_terp = 0;
829 TERP terp;
830 GEN_SCRIPT gen_script = {0};
831 BIO *debug_bio = cfg->debug_bio;
832
833 SCRIPT_INFO_print(script_info, debug_bio, /*error=*/0, "generating script");
834
835 /* Generate the script by calling the generator function. */
836 if (!TEST_true(GEN_SCRIPT_init(&gen_script, script_info))) {
837 SCRIPT_INFO_print(script_info, debug_bio, /*error=*/1,
838 "error while generating script");
839 goto err;
840 }
841
842 /* Output the script for debugging purposes. */
843 if (!TEST_true(GEN_SCRIPT_print(&gen_script, debug_bio, script_info))) {
844 SCRIPT_INFO_print(script_info, debug_bio, /*error=*/1,
845 "error while printing script");
846 goto err;
847 }
848
849 /* Execute the script. */
850 if (!TEST_true(TERP_init(&terp, cfg, script_info, &gen_script)))
851 goto err;
852
853 have_terp = 1;
854
855 SCRIPT_INFO_print(script_info, debug_bio, /*error=*/0, "executing script");
856
857 if (!TERP_execute(&terp))
858 goto err;
859
860 if (terp.stk_end - terp.stk_cur != 0) {
861 TEST_error("stack not empty: %zu bytes left",
862 (size_t)(terp.stk_end - terp.stk_cur));
863 goto err;
864 }
865
866 ok = 1;
867 err:
868 if (have_terp) {
869 TERP_print_stack(&terp, debug_bio, "Final state of stack");
870 TERP_cleanup(&terp);
871 }
872
873 GEN_SCRIPT_cleanup(&gen_script);
874 BIO_printf(debug_bio, "Stats:\n Ops executed: %16llu\n\n",
875 (unsigned long long)terp.ops_executed);
876 SCRIPT_INFO_print(script_info, debug_bio, /*error=*/!ok,
877 ok ? "completed" : "failed, exiting");
878 return ok;
879 }
880
881 #define SCRIPT(name) (&script_info_##name)
882 #define USE(name) SCRIPT(name),
883