xref: /freebsd/crypto/openssl/test/radix/terp.c (revision e7be843b4a162e68651d3911f0357ed464915629)
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