xref: /src/crypto/openssl/test/radix/terp.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
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 
print_opc(BIO * bio,size_t op_num,size_t offset,const char * name)370 static void print_opc(BIO *bio, size_t op_num, size_t offset, const char *name)
371 {
372     if (op_num != SIZE_MAX)
373         BIO_printf(bio, "%3zu-  %4zx>\t%-8s \t", op_num,
374             offset, name);
375     else
376         BIO_printf(bio, "      %4zx>\t%-8s \t",
377             offset, name);
378 }
379 
SRDR_print_one(SRDR * srdr,BIO * bio,size_t i,int * was_end)380 static int SRDR_print_one(SRDR *srdr, BIO *bio, size_t i, int *was_end)
381 {
382     int ok = 0;
383     const uint8_t *opc_start;
384     uint64_t opc;
385 
386     if (was_end != NULL)
387         *was_end = 0;
388 
389     opc_start = srdr->cur;
390     GET_OPERAND(srdr, opc);
391 
392 #define PRINT_OPC(name) print_opc(bio, i, (size_t)(opc_start - srdr->beg), #name)
393 
394     switch (opc) {
395     case OPK_END:
396         PRINT_OPC(END);
397         opc_start = srdr->cur;
398         if (was_end != NULL)
399             *was_end = 1;
400         break;
401     case OPK_PUSH_P: {
402         void *v;
403 
404         GET_OPERAND(srdr, v);
405         PRINT_OPC(PUSH_P);
406         BIO_printf(bio, "%20p", v);
407     } break;
408     case OPK_PUSH_PZ: {
409         void *v;
410 
411         GET_OPERAND(srdr, v);
412         PRINT_OPC(PUSH_PZ);
413         if (v != NULL && strlen((const char *)v) == 1)
414             BIO_printf(bio, "%20p (%s)", v, (const char *)v);
415         else
416             BIO_printf(bio, "%20p (\"%s\")", v, (const char *)v);
417     } break;
418     case OPK_PUSH_U64: {
419         uint64_t v;
420 
421         GET_OPERAND(srdr, v);
422         PRINT_OPC(PUSH_U64);
423         BIO_printf(bio, "%#20llx (%llu)",
424             (unsigned long long)v, (unsigned long long)v);
425     } break;
426     case OPK_PUSH_SIZE: {
427         size_t v;
428 
429         GET_OPERAND(srdr, v);
430         PRINT_OPC(PUSH_SIZE);
431         BIO_printf(bio, "%#20llx (%llu)",
432             (unsigned long long)v, (unsigned long long)v);
433     } break;
434     case OPK_FUNC: {
435         helper_func_t v;
436         void *f_name = NULL, *x = NULL;
437 
438         GET_OPERAND(srdr, v);
439         GET_OPERAND(srdr, f_name);
440 
441         PRINT_OPC(FUNC);
442         memcpy(&x, &v, sizeof(x) < sizeof(v) ? sizeof(x) : sizeof(v));
443         BIO_printf(bio, "%s", (const char *)f_name);
444     } break;
445     case OPK_LABEL: {
446         void *l_name;
447 
448         GET_OPERAND(srdr, l_name);
449 
450         BIO_printf(bio, "\n%s:\n", (const char *)l_name);
451         PRINT_OPC(LABEL);
452     } break;
453     default:
454         TEST_error("unsupported opcode while printing: %llu",
455             (unsigned long long)opc);
456         goto err;
457     }
458 
459     ok = 1;
460 err:
461     return ok;
462 }
463 
GEN_SCRIPT_print(GEN_SCRIPT * gen_script,BIO * bio,const SCRIPT_INFO * script_info)464 static int GEN_SCRIPT_print(GEN_SCRIPT *gen_script, BIO *bio,
465     const SCRIPT_INFO *script_info)
466 {
467     int ok = 0;
468     size_t i;
469     SRDR srdr_v, *srdr = &srdr_v;
470     int was_end = 0;
471 
472     SRDR_init(srdr, gen_script->buf, gen_script->buf_len);
473 
474     if (script_info != NULL) {
475         BIO_printf(bio, "\nGenerated script for '%s':\n",
476             script_info->name);
477         BIO_printf(bio, "\n--GENERATED-------------------------------------"
478                         "----------------------\n");
479         BIO_printf(bio, "  # NAME:\n  #   %s\n",
480             script_info->name);
481         BIO_printf(bio, "  # SOURCE:\n  #   %s:%d\n",
482             script_info->file, script_info->line);
483         BIO_printf(bio, "  # DESCRIPTION:\n  #   %s\n", script_info->desc);
484     }
485 
486     for (i = 0; !was_end; ++i) {
487         BIO_printf(bio, "\n");
488 
489         if (!TEST_true(SRDR_print_one(srdr, bio, i, &was_end)))
490             goto err;
491     }
492 
493     if (script_info != NULL) {
494         const unsigned char *opc_start = srdr->cur;
495 
496         BIO_printf(bio, "\n");
497         PRINT_OPC(++ +);
498         BIO_printf(bio, "\n------------------------------------------------"
499                         "----------------------\n\n");
500     }
501 
502     ok = 1;
503 err:
504     return ok;
505 }
506 
SCRIPT_INFO_print(SCRIPT_INFO * script_info,BIO * bio,int error,const char * msg)507 static void SCRIPT_INFO_print(SCRIPT_INFO *script_info, BIO *bio, int error,
508     const char *msg)
509 {
510     if (error)
511         TEST_error("%s: script '%s' (%s)",
512             msg, script_info->name, script_info->desc);
513     else
514         TEST_info("%s: script '%s' (%s)",
515             msg, script_info->name, script_info->desc);
516 }
517 
518 typedef struct terp_config_st {
519     BIO *debug_bio;
520 
521     OSSL_TIME (*now_cb)(void *arg);
522     void *now_cb_arg;
523 
524     int (*per_op_cb)(TERP *terp, void *arg);
525     void *per_op_cb_arg;
526 
527     OSSL_TIME max_execution_time; /* duration */
528 } TERP_CONFIG;
529 
530 #define TERP_DEFAULT_MAX_EXECUTION_TIME (ossl_ms2time(3000))
531 
532 struct terp_st {
533     TERP_CONFIG cfg;
534     const SCRIPT_INFO *script_info;
535     const GEN_SCRIPT *gen_script;
536     SRDR srdr;
537     uint8_t *stk_beg, *stk_cur, *stk_end, *stk_save_cur;
538     FUNC_CTX fctx;
539     uint64_t ops_executed;
540     int log_execute;
541     OSSL_TIME start_time, deadline_time;
542 };
543 
TERP_init(TERP * terp,const TERP_CONFIG * cfg,const SCRIPT_INFO * script_info,const GEN_SCRIPT * gen_script)544 static int TERP_init(TERP *terp,
545     const TERP_CONFIG *cfg,
546     const SCRIPT_INFO *script_info,
547     const GEN_SCRIPT *gen_script)
548 {
549     if (!TEST_true(cfg->now_cb != NULL))
550         return 0;
551 
552     terp->cfg = *cfg;
553     terp->script_info = script_info;
554     terp->gen_script = gen_script;
555     terp->fctx.terp = terp;
556     terp->fctx.spin_again = 0;
557     terp->fctx.skip_rest = 0;
558     terp->stk_beg = NULL;
559     terp->stk_cur = NULL;
560     terp->stk_end = NULL;
561     terp->stk_save_cur = NULL;
562     terp->ops_executed = 0;
563     terp->log_execute = 1;
564 
565     if (ossl_time_is_zero(terp->cfg.max_execution_time))
566         terp->cfg.max_execution_time = TERP_DEFAULT_MAX_EXECUTION_TIME;
567 
568     return 1;
569 }
570 
TERP_cleanup(TERP * terp)571 static void TERP_cleanup(TERP *terp)
572 {
573     if (terp->script_info == NULL)
574         return;
575 
576     OPENSSL_free(terp->stk_beg);
577     terp->stk_beg = terp->stk_cur = terp->stk_end = NULL;
578     terp->script_info = NULL;
579 }
580 
TERP_stk_ensure_capacity(TERP * terp,size_t spare)581 static int TERP_stk_ensure_capacity(TERP *terp, size_t spare)
582 {
583     uint8_t *p;
584     size_t old_size, new_size, off;
585 
586     old_size = terp->stk_end - terp->stk_beg;
587     if (old_size >= spare)
588         return 1;
589 
590     off = terp->stk_end - terp->stk_cur;
591     new_size = old_size != 0 ? old_size * 2 : 256;
592     p = OPENSSL_realloc(terp->stk_beg, new_size);
593     if (!TEST_ptr(p))
594         return 0;
595 
596     terp->stk_beg = p;
597     terp->stk_end = terp->stk_beg + new_size;
598     terp->stk_cur = terp->stk_end - off;
599     return 1;
600 }
601 
TERP_stk_push(TERP * terp,const void * buf,size_t buf_len)602 static ossl_inline int TERP_stk_push(TERP *terp,
603     const void *buf, size_t buf_len)
604 {
605     if (!TEST_true(TERP_stk_ensure_capacity(terp, buf_len)))
606         return 0;
607 
608     terp->stk_cur -= buf_len;
609     memcpy(terp->stk_cur, buf, buf_len);
610     return 1;
611 }
612 
TERP_stk_pop(TERP * terp,void * buf,size_t buf_len)613 static ossl_inline int TERP_stk_pop(TERP *terp,
614     void *buf, size_t buf_len)
615 {
616     if (!TEST_size_t_ge(terp->stk_end - terp->stk_cur, buf_len))
617         return 0;
618 
619     memcpy(buf, terp->stk_cur, buf_len);
620     terp->stk_cur += buf_len;
621     return 1;
622 }
623 
TERP_print_stack(TERP * terp,BIO * bio,const char * header)624 static void TERP_print_stack(TERP *terp, BIO *bio, const char *header)
625 {
626     test_output_memory(header, terp->stk_cur, terp->stk_end - terp->stk_cur);
627     BIO_printf(bio, "  (%zu bytes)\n", (size_t)(terp->stk_end - terp->stk_cur));
628     BIO_printf(bio, "\n");
629 }
630 
631 #define TERP_GET_OPERAND(v) GET_OPERAND(&terp->srdr, (v))
632 
633 #define TERP_SPIN_AGAIN()                   \
634     do {                                    \
635         SRDR_restore(&terp->srdr);          \
636         terp->stk_cur = terp->stk_save_cur; \
637         ++spin_count;                       \
638         goto spin_again;                    \
639     } while (0)
640 
TERP_now(TERP * terp)641 static OSSL_TIME TERP_now(TERP *terp)
642 {
643     return terp->cfg.now_cb(terp->cfg.now_cb_arg);
644 }
645 
TERP_log_spin(TERP * terp,size_t spin_count)646 static void TERP_log_spin(TERP *terp, size_t spin_count)
647 {
648     if (spin_count > 0)
649         BIO_printf(terp->cfg.debug_bio, "           \t\t(span %zu times)\n",
650             spin_count);
651 }
652 
TERP_execute(TERP * terp)653 static int TERP_execute(TERP *terp)
654 {
655     int ok = 0;
656     uint64_t opc;
657     size_t op_num = 0;
658     int in_debug_output = 0;
659     size_t spin_count = 0;
660     BIO *debug_bio = terp->cfg.debug_bio;
661 
662     SRDR_init(&terp->srdr, terp->gen_script->buf, terp->gen_script->buf_len);
663 
664     terp->start_time = TERP_now(terp);
665     terp->deadline_time = ossl_time_add(terp->start_time,
666         terp->cfg.max_execution_time);
667 
668     for (;;) {
669         if (terp->log_execute) {
670             SRDR srdr_copy = terp->srdr;
671 
672             if (!in_debug_output) {
673                 BIO_printf(debug_bio, "\n--EXECUTION-----------------------------"
674                                       "------------------------------\n");
675                 in_debug_output = 1;
676             }
677 
678             TERP_log_spin(terp, spin_count);
679             if (!TEST_true(SRDR_print_one(&srdr_copy, debug_bio, SIZE_MAX, NULL)))
680                 goto err;
681 
682             BIO_printf(debug_bio, "\n");
683         }
684 
685         TERP_GET_OPERAND(opc);
686         ++op_num;
687         SRDR_save(&terp->srdr);
688         terp->stk_save_cur = terp->stk_cur;
689         spin_count = 0;
690 
691         ++terp->ops_executed;
692 
693     spin_again:
694         if (ossl_time_compare(TERP_now(terp), terp->deadline_time) >= 0) {
695             TEST_error("timed out while executing op %zu", op_num);
696             if (terp->log_execute)
697                 TERP_log_spin(terp, spin_count);
698             goto err;
699         }
700 
701         if (terp->cfg.per_op_cb != NULL)
702             if (!TEST_true(terp->cfg.per_op_cb(terp, terp->cfg.per_op_cb_arg))) {
703                 TEST_error("pre-operation processing failed at op %zu", op_num);
704                 if (terp->log_execute)
705                     TERP_log_spin(terp, spin_count);
706                 goto err;
707             }
708 
709         switch (opc) {
710         case OPK_END:
711             goto stop;
712         case OPK_PUSH_P:
713         case OPK_PUSH_PZ: {
714             void *v;
715 
716             TERP_GET_OPERAND(v);
717             TERP_STK_PUSH(terp, v);
718         } break;
719         case OPK_PUSH_U64: {
720             uint64_t v;
721 
722             TERP_GET_OPERAND(v);
723             TERP_STK_PUSH(terp, v);
724         } break;
725         case OPK_PUSH_SIZE: {
726             size_t v;
727 
728             TERP_GET_OPERAND(v);
729             TERP_STK_PUSH(terp, v);
730         } break;
731         case OPK_LABEL: {
732             const char *l_name;
733 
734             TERP_GET_OPERAND(l_name);
735             /* no-op */
736         } break;
737         case OPK_FUNC: {
738             helper_func_t v;
739             const void *f_name;
740             int ret;
741 
742             TERP_GET_OPERAND(v);
743             TERP_GET_OPERAND(f_name);
744 
745             if (!TEST_true(v != NULL))
746                 goto err;
747 
748             ret = v(&terp->fctx);
749 
750             if (terp->fctx.skip_rest) {
751                 if (!TEST_int_eq(ret, F_RET_SKIP_REST))
752                     goto err;
753 
754                 if (terp->log_execute)
755                     BIO_printf(terp->cfg.debug_bio, "           \t\t(skipping)\n");
756 
757                 terp->fctx.skip_rest = 0;
758                 goto stop;
759             } else if (terp->fctx.spin_again) {
760                 if (!TEST_int_eq(ret, F_RET_SPIN_AGAIN))
761                     goto err;
762 
763                 terp->fctx.spin_again = 0;
764                 TERP_SPIN_AGAIN();
765             } else {
766                 if (!TEST_false(terp->fctx.spin_again))
767                     goto err;
768 
769                 if (ret != 1) {
770                     TEST_error("op %zu (FUNC %s) failed with return value %d",
771                         op_num, (const char *)f_name, ret);
772                     goto err;
773                 }
774             }
775         } break;
776         default:
777             TEST_error("unknown opcode: %llu", (unsigned long long)opc);
778             goto err;
779         }
780     }
781 
782 stop:
783     ok = 1;
784 err:
785     if (in_debug_output)
786         BIO_printf(debug_bio, "----------------------------------------"
787                               "------------------------------\n");
788 
789     if (!ok) {
790         TEST_error("FAILED while executing script: %s at op %zu, error stack:",
791             terp->script_info->name, op_num);
792         ERR_print_errors(terp->cfg.debug_bio);
793         BIO_printf(debug_bio, "\n");
794     } else if (ERR_peek_last_error() != 0) {
795         TEST_info("WARNING: errors on error stack despite success:");
796         ERR_print_errors(terp->cfg.debug_bio);
797         BIO_printf(debug_bio, "\n");
798     }
799 
800     return ok;
801 }
802 
TERP_run(SCRIPT_INFO * script_info,TERP_CONFIG * cfg)803 static int TERP_run(SCRIPT_INFO *script_info, TERP_CONFIG *cfg)
804 {
805     int ok = 0, have_terp = 0;
806     TERP terp;
807     GEN_SCRIPT gen_script = { 0 };
808     BIO *debug_bio = cfg->debug_bio;
809 
810     SCRIPT_INFO_print(script_info, debug_bio, /*error=*/0, "generating script");
811 
812     /* Generate the script by calling the generator function. */
813     if (!TEST_true(GEN_SCRIPT_init(&gen_script, script_info))) {
814         SCRIPT_INFO_print(script_info, debug_bio, /*error=*/1,
815             "error while generating script");
816         goto err;
817     }
818 
819     /* Output the script for debugging purposes. */
820     if (!TEST_true(GEN_SCRIPT_print(&gen_script, debug_bio, script_info))) {
821         SCRIPT_INFO_print(script_info, debug_bio, /*error=*/1,
822             "error while printing script");
823         goto err;
824     }
825 
826     /* Execute the script. */
827     if (!TEST_true(TERP_init(&terp, cfg, script_info, &gen_script)))
828         goto err;
829 
830     have_terp = 1;
831 
832     SCRIPT_INFO_print(script_info, debug_bio, /*error=*/0, "executing script");
833 
834     if (!TERP_execute(&terp))
835         goto err;
836 
837     if (terp.stk_end - terp.stk_cur != 0) {
838         TEST_error("stack not empty: %zu bytes left",
839             (size_t)(terp.stk_end - terp.stk_cur));
840         goto err;
841     }
842 
843     ok = 1;
844 err:
845     if (have_terp) {
846         TERP_print_stack(&terp, debug_bio, "Final state of stack");
847         TERP_cleanup(&terp);
848     }
849 
850     GEN_SCRIPT_cleanup(&gen_script);
851     if (have_terp) {
852         BIO_printf(debug_bio, "Stats:\n  Ops executed: %16llu\n\n",
853             (unsigned long long)terp.ops_executed);
854     }
855     SCRIPT_INFO_print(script_info, debug_bio, /*error=*/!ok,
856         ok ? "completed" : "failed, exiting");
857     return ok;
858 }
859 
860 #define SCRIPT(name) (&script_info_##name)
861 #define USE(name) SCRIPT(name),
862