Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-interpreter.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "ostream.h"
5
#include "mempool.h"
6
#include "array.h"
7
#include "hash.h"
8
#include "cpu-limit.h"
9
#include "mail-storage.h"
10
11
#include "sieve-common.h"
12
#include "sieve-limits.h"
13
#include "sieve-script.h"
14
#include "sieve-error.h"
15
#include "sieve-extensions.h"
16
#include "sieve-message.h"
17
#include "sieve-commands.h"
18
#include "sieve-code.h"
19
#include "sieve-actions.h"
20
#include "sieve-generator.h"
21
#include "sieve-binary.h"
22
#include "sieve-result.h"
23
#include "sieve-comparators.h"
24
#include "sieve-runtime-trace.h"
25
26
#include "sieve-interpreter.h"
27
28
#include <string.h>
29
30
static struct event_category event_category_sieve_runtime = {
31
  .parent = &event_category_sieve,
32
  .name = "sieve-runtime",
33
};
34
35
/*
36
 * Interpreter extension
37
 */
38
39
struct sieve_interpreter_extension_reg {
40
  const struct sieve_interpreter_extension *intext;
41
  const struct sieve_extension *ext;
42
43
  void *context;
44
45
  bool deferred:1;
46
  bool started:1;
47
};
48
49
/*
50
 * Code loop
51
 */
52
53
struct sieve_interpreter_loop {
54
  unsigned int level;
55
  sieve_size_t begin, end;
56
  const struct sieve_extension_def *ext_def;
57
  pool_t pool;
58
  void *context;
59
};
60
61
/*
62
 * Interpreter
63
 */
64
65
struct sieve_interpreter {
66
  pool_t pool;
67
  struct sieve_interpreter *parent;
68
69
  /* Runtime data for extensions */
70
  ARRAY(struct sieve_interpreter_extension_reg) extensions;
71
72
  sieve_size_t reset_vector;
73
74
  /* Execution status */
75
  sieve_size_t pc;          /* Program counter */
76
77
  /* Loop stack */
78
  ARRAY(struct sieve_interpreter_loop) loop_stack;
79
  sieve_size_t loop_limit;
80
  unsigned int parent_loop_level;
81
82
  /* Runtime environment */
83
  struct sieve_runtime_env runenv;
84
  struct sieve_runtime_trace trace;
85
  struct sieve_resource_usage rusage;
86
87
  /* CPU time limit for the current sieve_interpreter_continue() call;
88
     NULL when no limit is configured or not currently executing. Exposed
89
     via sieve_runtime_cpu_limit_exceeded() so long-running runtime code
90
     can enforce the limit without waiting for the next bytecode
91
     boundary. */
92
  struct cpu_limit *climit;
93
94
  /* Current operation */
95
  struct sieve_operation oprtn;
96
97
  /* Location information */
98
  struct sieve_binary_debug_reader *dreader;
99
  unsigned int command_line;
100
101
  bool running:1;       /* Interpreter is running
102
               (may be interrupted) */
103
  bool interrupted:1;         /* Interpreter interrupt requested */
104
  bool test_result:1;         /* Result of previous test command */
105
};
106
107
static struct sieve_interpreter *
108
_sieve_interpreter_create(struct sieve_binary *sbin,
109
        struct sieve_binary_block *sblock,
110
        struct sieve_script *script,
111
        struct sieve_interpreter *parent,
112
        const struct sieve_execute_env *eenv,
113
        struct sieve_error_handler *ehandler) ATTR_NULL(3, 4)
114
0
{
115
0
  const struct sieve_script_env *senv = eenv->scriptenv;
116
0
  unsigned int i, ext_count;
117
0
  struct sieve_interpreter *interp;
118
0
  pool_t pool;
119
0
  struct sieve_instance *svinst;
120
0
  const struct sieve_extension *const *ext_preloaded;
121
0
  unsigned int debug_block_id;
122
0
  sieve_size_t *address;
123
0
  bool success = TRUE;
124
125
0
  pool = pool_alloconly_create("sieve_interpreter", 4096);
126
0
  interp = p_new(pool, struct sieve_interpreter, 1);
127
0
  interp->parent = parent;
128
0
  interp->pool = pool;
129
130
0
  interp->runenv.ehandler = ehandler;
131
0
  sieve_error_handler_ref(ehandler);
132
133
0
  interp->runenv.exec_env = eenv;
134
0
  interp->runenv.interp = interp;
135
0
  interp->runenv.oprtn = &interp->oprtn;
136
0
  interp->runenv.sbin = sbin;
137
0
  interp->runenv.sblock = sblock;
138
0
  sieve_binary_ref(sbin);
139
140
0
  interp->runenv.event = event_create(eenv->event);
141
0
  event_add_category(interp->runenv.event, &event_category_sieve_runtime);
142
0
  event_add_str(interp->runenv.event, "script_name",
143
0
          sieve_binary_script_name(sbin));
144
0
  event_add_str(interp->runenv.event, "script_location",
145
0
          sieve_binary_script_location(sbin));
146
0
  event_add_str(interp->runenv.event, "binary_path",
147
0
          sieve_binary_path(sbin));
148
149
0
  svinst = sieve_binary_svinst(sbin);
150
151
0
  if (senv->trace_log != NULL) {
152
0
    interp->trace.log = senv->trace_log;
153
0
    interp->trace.config = senv->trace_config;
154
0
    interp->trace.indent = 0;
155
0
    interp->runenv.trace = &interp->trace;
156
0
  }
157
158
0
  if (script == NULL)
159
0
    interp->runenv.script = sieve_binary_script(sbin);
160
0
  else
161
0
    interp->runenv.script = script;
162
163
0
  interp->runenv.pc = 0;
164
0
  address = &(interp->runenv.pc);
165
166
0
  sieve_runtime_trace_begin(&(interp->runenv));
167
168
0
  p_array_init(&interp->extensions, pool,
169
0
         sieve_extensions_get_count(svinst));
170
171
0
  interp->parent_loop_level = 0;
172
0
  if (parent != NULL && array_is_created(&parent->loop_stack)) {
173
0
    interp->parent_loop_level = parent->parent_loop_level +
174
0
      array_count(&parent->loop_stack);
175
0
  }
176
177
  /* Pre-load core language features implemented as 'extensions' */
178
0
  ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count);
179
0
  for (i = 0; i < ext_count; i++) {
180
0
    const struct sieve_extension_def *ext_def =
181
0
      ext_preloaded[i]->def;
182
183
0
    if (ext_def != NULL && ext_def->interpreter_load != NULL) {
184
0
      (void)ext_def->interpreter_load(ext_preloaded[i],
185
0
              &interp->runenv,
186
0
              address);
187
0
    }
188
0
  }
189
190
  /* Load debug block */
191
0
  if (sieve_binary_read_unsigned(sblock, address, &debug_block_id)) {
192
0
    struct sieve_binary_block *debug_block =
193
0
      sieve_binary_block_get(sbin, debug_block_id);
194
195
0
    if (debug_block == NULL) {
196
0
      sieve_runtime_trace_error(&interp->runenv,
197
0
              "invalid id for debug block");
198
0
      success = FALSE;
199
0
    } else {
200
      /* Initialize debug reader */
201
0
      interp->dreader =
202
0
        sieve_binary_debug_reader_init(debug_block);
203
0
    }
204
0
  }
205
206
  /* Load other extensions listed in code */
207
0
  if (success && sieve_binary_read_unsigned(sblock, address,
208
0
              &ext_count)) {
209
210
0
    for (i = 0; i < ext_count; i++) {
211
0
      unsigned int code = 0, deferred;
212
0
      struct sieve_interpreter_extension_reg *reg;
213
0
      const struct sieve_extension *ext;
214
215
0
      if (!sieve_binary_read_extension(sblock, address,
216
0
               &code, &ext) ||
217
0
          !sieve_binary_read_byte(sblock, address,
218
0
                &deferred)) {
219
0
        success = FALSE;
220
0
        break;
221
0
      }
222
223
0
      if (deferred > 0 && ext->id >= 0) {
224
0
        reg = array_idx_get_space(
225
0
          &interp->extensions,
226
0
          (unsigned int)ext->id);
227
0
        reg->deferred = TRUE;
228
0
      }
229
230
0
      if (ext->def != NULL) {
231
0
        if (ext->global &&
232
0
            (eenv->flags & SIEVE_EXECUTE_FLAG_NOGLOBAL) != 0) {
233
0
          sieve_runtime_error(&interp->runenv, NULL,
234
0
            "failed to enable extension '%s': "
235
0
            "its use is restricted to global scripts",
236
0
            sieve_extension_name(ext));
237
0
          success = FALSE;
238
0
          break;
239
0
        }
240
241
0
        if (ext->def->interpreter_load != NULL &&
242
0
            !ext->def->interpreter_load(ext, &interp->runenv,
243
0
                address)) {
244
0
          success = FALSE;
245
0
          break;
246
0
        }
247
0
      }
248
0
    }
249
0
  } else {
250
0
    success = FALSE;
251
0
  }
252
253
0
  if (!success) {
254
0
    sieve_interpreter_free(&interp);
255
0
    interp = NULL;
256
0
  } else {
257
0
    interp->reset_vector = *address;
258
0
  }
259
260
0
  return interp;
261
0
}
262
263
struct sieve_interpreter *
264
sieve_interpreter_create(struct sieve_binary *sbin,
265
       struct sieve_interpreter *parent,
266
       const struct sieve_execute_env *eenv,
267
       struct sieve_error_handler *ehandler)
268
0
{
269
0
  struct sieve_binary_block *sblock;
270
271
0
  if ((sblock = sieve_binary_block_get(
272
0
    sbin, SBIN_SYSBLOCK_MAIN_PROGRAM)) == NULL)
273
0
    return NULL;
274
275
0
  return _sieve_interpreter_create(sbin, sblock, NULL, parent, eenv,
276
0
           ehandler);
277
0
}
278
279
struct sieve_interpreter *
280
sieve_interpreter_create_for_block(struct sieve_binary_block *sblock,
281
           struct sieve_script *script,
282
           struct sieve_interpreter *parent,
283
           const struct sieve_execute_env *eenv,
284
           struct sieve_error_handler *ehandler)
285
0
{
286
0
  if (sblock == NULL) return NULL;
287
288
0
  return _sieve_interpreter_create(sieve_binary_block_get_binary(sblock),
289
0
           sblock, script, parent, eenv,
290
0
           ehandler);
291
0
}
292
293
void sieve_interpreter_free(struct sieve_interpreter **_interp)
294
0
{
295
0
  struct sieve_interpreter *interp = *_interp;
296
0
  struct sieve_runtime_env *renv = &interp->runenv;
297
0
  const struct sieve_interpreter_extension_reg *eregs;
298
0
  struct sieve_interpreter_loop *loops;
299
0
  unsigned int count, i;
300
301
0
  if (interp->running) {
302
0
    struct event_passthrough *e =
303
0
      event_create_passthrough(interp->runenv.event)->
304
0
      set_name("sieve_runtime_script_finished")->
305
0
      add_str("error", "Aborted");
306
0
    e_debug(e->event(), "Aborted running script '%s'",
307
0
      sieve_binary_source(interp->runenv.sbin));
308
309
0
    interp->running = FALSE;
310
0
  }
311
312
0
  if (array_is_created(&interp->loop_stack)) {
313
0
    loops = array_get_modifiable(&interp->loop_stack, &count);
314
0
    for (i = 0; i < count; i++)
315
0
      pool_unref(&loops[i].pool);
316
0
  }
317
318
0
  interp->trace.indent = 0;
319
0
  sieve_runtime_trace_end(renv);
320
321
  /* Signal registered extensions that the interpreter is being destroyed */
322
0
  eregs = array_get(&interp->extensions, &count);
323
0
  for (i = 0; i < count; i++) {
324
0
    if (eregs[i].intext != NULL && eregs[i].intext->free != NULL) {
325
0
      eregs[i].intext->free(eregs[i].ext, interp,
326
0
                eregs[i].context);
327
0
    }
328
0
  }
329
330
0
  sieve_binary_debug_reader_deinit(&interp->dreader);
331
0
  sieve_binary_unref(&renv->sbin);
332
0
  sieve_result_unref(&interp->runenv.result);
333
0
  sieve_error_handler_unref(&renv->ehandler);
334
0
  event_unref(&renv->event);
335
336
0
  pool_unref(&interp->pool);
337
0
  *_interp = NULL;
338
0
}
339
340
/*
341
 * Accessors
342
 */
343
344
pool_t sieve_interpreter_pool(struct sieve_interpreter *interp)
345
0
{
346
0
  return interp->pool;
347
0
}
348
349
struct sieve_interpreter *
350
sieve_interpreter_get_parent(struct sieve_interpreter *interp)
351
0
{
352
0
  return interp->parent;
353
0
}
354
355
struct sieve_script *sieve_interpreter_script(struct sieve_interpreter *interp)
356
0
{
357
0
  return interp->runenv.script;
358
0
}
359
360
struct sieve_error_handler *
361
sieve_interpreter_get_error_handler(struct sieve_interpreter *interp)
362
0
{
363
0
  return interp->runenv.ehandler;
364
0
}
365
366
struct sieve_instance *
367
sieve_interpreter_svinst(struct sieve_interpreter *interp)
368
0
{
369
0
  return interp->runenv.exec_env->svinst;
370
0
}
371
372
bool sieve_runtime_cpu_limit_exceeded(const struct sieve_runtime_env *renv)
373
0
{
374
0
  struct sieve_interpreter *interp = renv->interp;
375
376
0
  if (interp->climit == NULL)
377
0
    return FALSE;
378
0
  return cpu_limit_exceeded(interp->climit);
379
0
}
380
381
/* Do not use this function for normal sieve extensions. This is intended for
382
 * the testsuite only.
383
 */
384
void sieve_interpreter_set_result(struct sieve_interpreter *interp,
385
          struct sieve_result *result)
386
0
{
387
0
  sieve_result_unref(&interp->runenv.result);
388
0
  interp->runenv.result = result;
389
0
  interp->runenv.msgctx = sieve_result_get_message_context(result);
390
0
  sieve_result_ref(result);
391
0
}
392
393
/*
394
 * Source location
395
 */
396
397
unsigned int
398
sieve_runtime_get_source_location(const struct sieve_runtime_env *renv,
399
          sieve_size_t code_address)
400
0
{
401
0
  struct sieve_interpreter *interp = renv->interp;
402
403
0
  if (interp->dreader == NULL)
404
0
    return 0;
405
406
0
  if (interp->command_line == 0) {
407
0
    interp->command_line =
408
0
      sieve_binary_debug_read_line(interp->dreader,
409
0
                 renv->oprtn->address);
410
0
  }
411
412
0
  return sieve_binary_debug_read_line(interp->dreader, code_address);
413
0
}
414
415
unsigned int
416
sieve_runtime_get_command_location(const struct sieve_runtime_env *renv)
417
0
{
418
0
  struct sieve_interpreter *interp = renv->interp;
419
420
0
  if (interp->dreader == NULL)
421
0
    return 0;
422
423
0
  if (interp->command_line == 0) {
424
0
    interp->command_line =
425
0
      sieve_binary_debug_read_line(interp->dreader,
426
0
                 renv->oprtn->address);
427
0
  }
428
429
0
  return interp->command_line;
430
0
}
431
432
const char *
433
sieve_runtime_get_full_command_location(const struct sieve_runtime_env *renv)
434
0
{
435
0
  return sieve_error_script_location(
436
0
    renv->script, sieve_runtime_get_command_location(renv));
437
0
}
438
439
/*
440
 * Extension support
441
 */
442
443
void sieve_interpreter_extension_register(
444
  struct sieve_interpreter *interp, const struct sieve_extension *ext,
445
  const struct sieve_interpreter_extension *intext, void *context)
446
0
{
447
0
  struct sieve_interpreter_extension_reg *reg;
448
449
0
  if (ext->id < 0)
450
0
    return;
451
452
0
  reg = array_idx_get_space(&interp->extensions, (unsigned int) ext->id);
453
0
  reg->intext = intext;
454
0
  reg->ext = ext;
455
0
  reg->context = context;
456
0
}
457
458
void sieve_interpreter_extension_set_context(struct sieve_interpreter *interp,
459
               const struct sieve_extension *ext,
460
               void *context)
461
0
{
462
0
  struct sieve_interpreter_extension_reg *reg;
463
464
0
  if (ext->id < 0)
465
0
    return;
466
467
0
  reg = array_idx_get_space(&interp->extensions, (unsigned int) ext->id);
468
0
  reg->context = context;
469
0
}
470
471
void *sieve_interpreter_extension_get_context(struct sieve_interpreter *interp,
472
                const struct sieve_extension *ext)
473
0
{
474
0
  const struct sieve_interpreter_extension_reg *reg;
475
476
0
  if (ext->id < 0 || ext->id >= (int) array_count(&interp->extensions))
477
0
    return NULL;
478
479
0
  reg = array_idx(&interp->extensions, (unsigned int) ext->id);
480
481
0
  return reg->context;
482
0
}
483
484
int sieve_interpreter_extension_start(struct sieve_interpreter *interp,
485
              const struct sieve_extension *ext)
486
0
{
487
0
  struct sieve_interpreter_extension_reg *reg;
488
0
  int ret;
489
490
0
  i_assert(ext->id >= 0);
491
492
0
  if (ext->id >= (int) array_count(&interp->extensions))
493
0
    return SIEVE_EXEC_OK;
494
495
0
  reg = array_idx_modifiable(&interp->extensions, (unsigned int)ext->id);
496
497
0
  if (!reg->deferred)
498
0
    return SIEVE_EXEC_OK;
499
0
  reg->deferred = FALSE;
500
0
  reg->started = TRUE;
501
502
0
  if (reg->intext != NULL && reg->intext->run != NULL &&
503
0
      (ret = reg->intext->run(ext, &interp->runenv,
504
0
            reg->context, TRUE)) <= 0)
505
0
    return ret;
506
0
  return SIEVE_EXEC_OK;
507
0
}
508
509
/*
510
 * Loop handling
511
 */
512
513
int sieve_interpreter_loop_start(struct sieve_interpreter *interp,
514
         sieve_size_t loop_end,
515
         const struct sieve_extension_def *ext_def,
516
         struct sieve_interpreter_loop **loop_r)
517
0
{
518
0
  const struct sieve_runtime_env *renv = &interp->runenv;
519
0
  struct sieve_interpreter_loop *loop;
520
521
0
  i_assert(loop_end > interp->runenv.pc);
522
523
  /* Check supplied end offset */
524
0
  if (loop_end > sieve_binary_block_get_size(renv->sblock)) {
525
0
    sieve_runtime_trace_error(renv, "loop end offset out of range");
526
0
    return SIEVE_EXEC_BIN_CORRUPT;
527
0
  }
528
529
  /* Trace */
530
0
  if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) {
531
0
    unsigned int line =
532
0
      sieve_runtime_get_source_location(renv, loop_end);
533
534
0
    if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) {
535
0
      sieve_runtime_trace(renv, 0,
536
0
              "loop ends at line %d [%08llx]",
537
0
              line,
538
0
              (long long unsigned int) loop_end);
539
0
    } else {
540
0
      sieve_runtime_trace(renv, 0,
541
0
              "loop ends at line %d", line);
542
0
    }
543
0
  }
544
545
  /* Check loop nesting limit */
546
0
  if (!array_is_created(&interp->loop_stack))
547
0
    p_array_init(&interp->loop_stack, interp->pool, 8);
548
0
  if ((interp->parent_loop_level +
549
0
       array_count(&interp->loop_stack)) >= SIEVE_MAX_LOOP_DEPTH) {
550
    /* Should normally be caught at compile time */
551
0
    sieve_runtime_error(renv, NULL,
552
0
            "new program loop exceeds "
553
0
            "the nesting limit (<= %u levels)",
554
0
            SIEVE_MAX_LOOP_DEPTH);
555
0
    return SIEVE_EXEC_FAILURE;
556
0
  }
557
558
  /* Create new loop */
559
0
  loop = array_append_space(&interp->loop_stack);
560
0
  loop->level = array_count(&interp->loop_stack)-1;
561
0
  loop->ext_def = ext_def;
562
0
  loop->begin = interp->runenv.pc;
563
0
  loop->end = loop_end;
564
0
  loop->pool =  pool_alloconly_create("sieve_interpreter", 128);
565
566
  /* Set new loop limit */
567
0
  interp->loop_limit = loop_end;
568
569
0
  *loop_r = loop;
570
0
  return SIEVE_EXEC_OK;
571
0
}
572
573
struct sieve_interpreter_loop *
574
sieve_interpreter_loop_get(struct sieve_interpreter *interp,
575
         sieve_size_t loop_end,
576
         const struct sieve_extension_def *ext_def)
577
0
{
578
0
  struct sieve_interpreter_loop *loops;
579
0
  unsigned int count, i;
580
581
0
  if (!array_is_created(&interp->loop_stack))
582
0
    return NULL;
583
584
0
  loops = array_get_modifiable(&interp->loop_stack, &count);
585
0
  for (i = count; i > 0; i--) {
586
    /* We're really making sure our loop matches */
587
0
    if (loops[i-1].end == loop_end &&
588
0
        loops[i-1].ext_def == ext_def)
589
0
      return &loops[i-1];
590
0
  }
591
0
  return NULL;
592
0
}
593
594
int sieve_interpreter_loop_next(struct sieve_interpreter *interp,
595
        struct sieve_interpreter_loop *loop,
596
        sieve_size_t loop_begin)
597
0
{
598
0
  const struct sieve_runtime_env *renv = &interp->runenv;
599
0
  struct sieve_interpreter_loop *loops;
600
0
  unsigned int count;
601
602
  /* Trace */
603
0
  if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) {
604
0
    unsigned int line =
605
0
      sieve_runtime_get_source_location(renv, loop_begin);
606
607
0
    if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) {
608
0
      sieve_runtime_trace(renv, 0,
609
0
              "looping back to line %d [%08llx]",
610
0
              line,
611
0
              (long long unsigned int) loop_begin);
612
0
    } else {
613
0
      sieve_runtime_trace(renv, 0,
614
0
              "looping back to line %d", line);
615
0
    }
616
0
  }
617
618
  /* Check the code for corruption */
619
0
  if (loop->begin != loop_begin) {
620
0
    sieve_runtime_trace_error(renv, "loop begin offset invalid");
621
0
    return SIEVE_EXEC_BIN_CORRUPT;
622
0
  }
623
624
  /* Check invariants */
625
0
  i_assert(array_is_created(&interp->loop_stack));
626
0
  loops = array_get_modifiable(&interp->loop_stack, &count);
627
0
  i_assert(&loops[count-1] == loop);
628
629
  /* Return to beginning */
630
0
  interp->runenv.pc = loop_begin;
631
0
  return SIEVE_EXEC_OK;
632
0
}
633
634
int sieve_interpreter_loop_break(struct sieve_interpreter *interp,
635
         struct sieve_interpreter_loop *loop)
636
0
{
637
0
  const struct sieve_runtime_env *renv = &interp->runenv;
638
0
  struct sieve_interpreter_loop *loops;
639
0
  sieve_size_t loop_end = loop->end;
640
0
  unsigned int count, i;
641
642
  /* Find the loop */
643
0
  i_assert(array_is_created(&interp->loop_stack));
644
0
  loops = array_get_modifiable(&interp->loop_stack, &count);
645
0
  i_assert(count > 0);
646
647
0
  i = count;
648
0
  do {
649
0
    pool_unref(&loops[i-1].pool);
650
0
    i--;
651
0
  } while (i > 0 && &loops[i] != loop);
652
0
  i_assert(&loops[i] == loop);
653
654
  /* Set new loop limit */
655
0
  if (i > 0)
656
0
    interp->loop_limit = loops[i].end;
657
0
  else
658
0
    interp->loop_limit = 0;
659
660
  /* Delete it and all deeper loops */
661
0
  array_delete(&interp->loop_stack, i, count - i);
662
663
  /* Trace */
664
0
  if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) {
665
0
    unsigned int jmp_line =
666
0
      sieve_runtime_get_source_location(renv, loop_end);
667
668
0
    if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) {
669
0
      sieve_runtime_trace(renv, 0,
670
0
              "exiting loops at line %d [%08llx]",
671
0
              jmp_line,
672
0
              (long long unsigned int) loop_end);
673
0
    } else {
674
0
      sieve_runtime_trace(renv, 0,
675
0
              "exiting loops at line %d",
676
0
              jmp_line);
677
0
    }
678
0
  }
679
680
  /* Exit loop */
681
0
  interp->runenv.pc = loop->end;
682
0
  return SIEVE_EXEC_OK;
683
0
}
684
685
static int
686
sieve_interpreter_loop_break_out(struct sieve_interpreter *interp,
687
         sieve_size_t target)
688
0
{
689
0
  struct sieve_interpreter_loop *loops;
690
0
  unsigned int count, i;
691
692
0
  if (!array_is_created(&interp->loop_stack))
693
0
    return SIEVE_EXEC_OK;
694
695
0
  loops = array_get_modifiable(&interp->loop_stack, &count);
696
0
  for (i = count; i > 0; i--) {
697
    /* We're really making sure our loop matches */
698
0
    if (loops[i-1].end > target)
699
0
      break;
700
0
  }
701
0
  if (i == count)
702
0
    return SIEVE_EXEC_OK;
703
704
0
  return sieve_interpreter_loop_break(interp, &loops[i]);
705
0
}
706
707
struct sieve_interpreter_loop *
708
sieve_interpreter_loop_get_local(struct sieve_interpreter *interp,
709
         struct sieve_interpreter_loop *loop,
710
         const struct sieve_extension_def *ext_def)
711
0
{
712
0
  struct sieve_interpreter_loop *loops;
713
0
  unsigned int count, i;
714
715
0
  if (!array_is_created(&interp->loop_stack))
716
0
    return NULL;
717
718
0
  loops = array_get_modifiable(&interp->loop_stack, &count);
719
0
  i_assert(loop == NULL || loop->level < count);
720
721
0
  for (i = (loop == NULL ? count : loop->level); i > 0; i--) {
722
0
    if (ext_def == NULL || loops[i-1].ext_def == ext_def)
723
0
      return &loops[i-1];
724
0
  }
725
0
  return NULL;
726
0
}
727
728
struct sieve_interpreter_loop *
729
sieve_interpreter_loop_get_global(struct sieve_interpreter *interp,
730
          struct sieve_interpreter_loop *loop,
731
          const struct sieve_extension_def *ext_def)
732
0
{
733
0
  struct sieve_interpreter_loop *result;
734
735
0
  while (interp != NULL) {
736
0
    result = sieve_interpreter_loop_get_local(interp, loop,
737
0
                ext_def);
738
0
    if (result != NULL)
739
0
      return result;
740
0
    interp = interp->parent;
741
0
  }
742
0
  return NULL;
743
0
}
744
745
pool_t sieve_interpreter_loop_get_pool(struct sieve_interpreter_loop *loop)
746
0
{
747
0
  return loop->pool;
748
0
}
749
750
void *sieve_interpreter_loop_get_context(struct sieve_interpreter_loop *loop)
751
0
{
752
0
  return loop->context;
753
0
}
754
755
void sieve_interpreter_loop_set_context(struct sieve_interpreter_loop *loop,
756
          void *context)
757
0
{
758
0
  loop->context = context;
759
0
}
760
761
/*
762
 * Program flow
763
 */
764
765
void sieve_interpreter_reset(struct sieve_interpreter *interp)
766
0
{
767
0
  interp->runenv.pc = interp->reset_vector;
768
0
  interp->interrupted = FALSE;
769
0
  interp->test_result = FALSE;
770
0
  sieve_result_unref(&interp->runenv.result);
771
0
}
772
773
void sieve_interpreter_interrupt(struct sieve_interpreter *interp)
774
0
{
775
0
  interp->interrupted = TRUE;
776
0
}
777
778
sieve_size_t sieve_interpreter_program_counter(struct sieve_interpreter *interp)
779
0
{
780
0
  return interp->runenv.pc;
781
0
}
782
783
static int
784
sieve_interpreter_check_program_jump(struct sieve_interpreter *interp,
785
             sieve_size_t jmp_target, bool break_loops)
786
0
{
787
0
  const struct sieve_runtime_env *renv = &interp->runenv;
788
0
  sieve_size_t loop_limit = (break_loops ? 0 : interp->loop_limit);
789
790
0
  if (jmp_target == 0 ||
791
0
      jmp_target > sieve_binary_block_get_size(renv->sblock) ||
792
0
      (loop_limit > 0 && jmp_target >= loop_limit)) {
793
0
    if (interp->loop_limit != 0) {
794
0
      sieve_runtime_trace_error(
795
0
        renv, "jump target crosses loop boundary");
796
0
    } else {
797
0
      sieve_runtime_trace_error(
798
0
        renv, "jump target out of range");
799
0
    }
800
0
    return SIEVE_EXEC_BIN_CORRUPT;
801
0
  }
802
0
  return SIEVE_EXEC_OK;
803
0
}
804
805
static int
806
sieve_interpreter_do_program_jump(struct sieve_interpreter *interp,
807
          sieve_size_t jmp_target, bool break_loops)
808
0
{
809
0
  const struct sieve_runtime_env *renv = &interp->runenv;
810
0
  sieve_size_t *address = &(interp->runenv.pc);
811
0
  int ret;
812
813
0
  if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) {
814
0
    unsigned int jmp_line =
815
0
      sieve_runtime_get_source_location(renv, jmp_target);
816
817
0
    if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) {
818
0
      sieve_runtime_trace(renv, 0, "jumping to line %d [%08llx]",
819
0
              jmp_line,
820
0
              (long long unsigned int)jmp_target);
821
0
    } else {
822
0
      sieve_runtime_trace(renv, 0, "jumping to line %d",
823
0
              jmp_line);
824
0
    }
825
0
  }
826
827
0
  if (break_loops &&
828
0
      (ret = sieve_interpreter_loop_break_out(interp,
829
0
                jmp_target)) <= 0)
830
0
    return ret;
831
832
0
  *address = jmp_target;
833
0
  return SIEVE_EXEC_OK;
834
0
}
835
836
int sieve_interpreter_program_jump_to(struct sieve_interpreter *interp,
837
              sieve_size_t jmp_target,
838
              bool break_loops)
839
0
{
840
0
  int ret;
841
842
0
  ret = sieve_interpreter_check_program_jump(interp, jmp_target,
843
0
               break_loops);
844
0
  if (ret <= 0)
845
0
    return ret;
846
847
0
  return sieve_interpreter_do_program_jump(
848
0
    interp, jmp_target, break_loops);
849
0
}
850
851
int sieve_interpreter_program_jump(struct sieve_interpreter *interp,
852
           bool jump, bool break_loops)
853
0
{
854
0
  const struct sieve_runtime_env *renv = &interp->runenv;
855
0
  sieve_size_t *address = &(interp->runenv.pc);
856
0
  sieve_size_t jmp_start = *address, jmp_target;
857
0
  sieve_offset_t jmp_offset;
858
0
  int ret;
859
860
0
  if (!sieve_binary_read_offset(renv->sblock, address, &jmp_offset)) {
861
0
    sieve_runtime_trace_error(renv, "invalid jump offset");
862
0
    return SIEVE_EXEC_BIN_CORRUPT;
863
0
  }
864
0
  jmp_target = jmp_start + jmp_offset;
865
866
0
  ret = sieve_interpreter_check_program_jump(interp, jmp_target,
867
0
               break_loops);
868
0
  if (ret <= 0)
869
0
    return ret;
870
871
0
  if (!jump) {
872
0
    sieve_runtime_trace(renv, 0, "not jumping");
873
0
    return SIEVE_EXEC_OK;
874
0
  }
875
876
0
  return sieve_interpreter_do_program_jump(
877
0
    interp, jmp_target, break_loops);
878
0
}
879
880
/*
881
 * Test results
882
 */
883
884
void sieve_interpreter_set_test_result(struct sieve_interpreter *interp,
885
               bool result)
886
0
{
887
0
  interp->test_result = result;
888
0
}
889
890
bool sieve_interpreter_get_test_result(struct sieve_interpreter *interp)
891
0
{
892
0
  return interp->test_result;
893
0
}
894
895
/*
896
 * Code execute
897
 */
898
899
static int sieve_interpreter_operation_execute(struct sieve_interpreter *interp)
900
0
{
901
0
  struct sieve_operation *oprtn = &(interp->oprtn);
902
0
  sieve_size_t *address = &(interp->runenv.pc);
903
904
0
  sieve_runtime_trace_toplevel(&interp->runenv);
905
906
  /* Read the operation */
907
0
  if (sieve_operation_read(interp->runenv.sblock, address, oprtn)) {
908
0
    const struct sieve_operation_def *op = oprtn->def;
909
0
    int result = SIEVE_EXEC_OK;
910
911
    /* Reset cached command location */
912
0
    interp->command_line = 0;
913
914
    /* Execute the operation */
915
0
    i_assert(op != NULL);
916
0
    if (op->execute != NULL) { /* Noop ? */
917
0
      T_BEGIN {
918
0
        result = op->execute(&(interp->runenv),
919
0
                 address);
920
0
      } T_END;
921
0
    } else {
922
0
      sieve_runtime_trace(&interp->runenv,
923
0
              SIEVE_TRLVL_COMMANDS,
924
0
              "OP: %s (NOOP)",
925
0
              sieve_operation_mnemonic(oprtn));
926
0
    }
927
928
0
    return result;
929
0
  }
930
931
  /* Binary corrupt */
932
0
  sieve_runtime_trace_error(&interp->runenv,
933
0
          "Encountered invalid operation");
934
0
  return SIEVE_EXEC_BIN_CORRUPT;
935
0
}
936
937
int sieve_interpreter_continue(struct sieve_interpreter *interp,
938
             bool *interrupted)
939
0
{
940
0
  const struct sieve_runtime_env *renv = &interp->runenv;
941
0
  const struct sieve_execute_env *eenv = renv->exec_env;
942
0
  struct cpu_limit *climit = NULL;
943
0
  sieve_size_t *address = &(interp->runenv.pc);
944
0
  struct sieve_instance *svinst = eenv->svinst;
945
0
  struct sieve_exec_status *exec_status = eenv->exec_status;
946
0
  struct sieve_resource_usage rusage;
947
0
  int ret = SIEVE_EXEC_OK;
948
949
0
  interp->interrupted = FALSE;
950
951
0
  if (interrupted != NULL)
952
0
    *interrupted = FALSE;
953
954
0
  if (svinst->set->max_cpu_time > 0) {
955
0
    climit = cpu_limit_init(svinst->set->max_cpu_time,
956
0
          CPU_LIMIT_TYPE_USER);
957
0
  }
958
0
  interp->climit = climit;
959
960
0
  while (ret == SIEVE_EXEC_OK && !interp->interrupted &&
961
0
         *address < sieve_binary_block_get_size(renv->sblock)) {
962
0
    if (climit != NULL && cpu_limit_exceeded(climit)) {
963
0
      sieve_runtime_error(
964
0
        renv, NULL,
965
0
        "execution exceeded CPU time limit");
966
0
      ret = SIEVE_EXEC_RESOURCE_LIMIT;
967
0
      break;
968
0
    }
969
0
    if (interp->loop_limit != 0 && *address > interp->loop_limit) {
970
0
      sieve_runtime_trace_error(
971
0
        renv, "program crossed loop boundary");
972
0
      ret = SIEVE_EXEC_BIN_CORRUPT;
973
0
      break;
974
0
    }
975
976
0
    ret = sieve_interpreter_operation_execute(interp);
977
0
  }
978
979
0
  interp->climit = NULL;
980
981
0
  if (climit != NULL) {
982
0
    sieve_resource_usage_init(&rusage);
983
0
    rusage.cpu_time_msecs =
984
0
      cpu_limit_get_usage_msecs(climit, CPU_LIMIT_TYPE_USER);
985
0
    sieve_resource_usage_add(&interp->rusage, &rusage);
986
987
0
    cpu_limit_deinit(&climit);
988
0
  }
989
990
0
  if (ret != SIEVE_EXEC_OK) {
991
0
    sieve_runtime_trace(&interp->runenv, SIEVE_TRLVL_NONE,
992
0
            "[[EXECUTION ABORTED]]");
993
0
  }
994
995
0
  if (interrupted != NULL)
996
0
    *interrupted = interp->interrupted;
997
998
0
  if (!interp->interrupted) {
999
0
    exec_status->resource_usage = interp->rusage;
1000
1001
0
    struct event_passthrough *e =
1002
0
      event_create_passthrough(interp->runenv.event)->
1003
0
      set_name("sieve_runtime_script_finished");
1004
0
    switch (ret) {
1005
0
    case SIEVE_EXEC_OK:
1006
0
      break;
1007
0
    case SIEVE_EXEC_FAILURE:
1008
0
      e->add_str("error", "Failed");
1009
0
      break;
1010
0
    case SIEVE_EXEC_TEMP_FAILURE:
1011
0
      e->add_str("error", "Failed temporarily");
1012
0
      break;
1013
0
    case SIEVE_EXEC_BIN_CORRUPT:
1014
0
      e->add_str("error", "Binary corrupt");
1015
0
      break;
1016
0
    case SIEVE_EXEC_RESOURCE_LIMIT:
1017
0
      e->add_str("error", "Resource limit exceeded");
1018
0
      break;
1019
0
    case SIEVE_EXEC_KEEP_FAILED:
1020
      /* Not supposed to occur at runtime */
1021
0
      i_unreached();
1022
0
    }
1023
0
    e_debug(e->event(), "Finished running script '%s' "
1024
0
      "(status=%s, resource usage: %s)",
1025
0
      sieve_binary_source(interp->runenv.sbin),
1026
0
      sieve_execution_exitcode_to_str(ret),
1027
0
      sieve_resource_usage_get_summary(&interp->rusage));
1028
0
    interp->running = FALSE;
1029
0
  }
1030
1031
0
  return ret;
1032
0
}
1033
1034
int sieve_interpreter_start(struct sieve_interpreter *interp,
1035
          struct sieve_result *result, bool *interrupted)
1036
0
{
1037
0
  struct sieve_interpreter_extension_reg *eregs;
1038
0
  unsigned int ext_count, i;
1039
0
  int ret;
1040
1041
0
  struct event_passthrough *e =
1042
0
    event_create_passthrough(interp->runenv.event)->
1043
0
    set_name("sieve_runtime_script_started");
1044
0
  e_debug(e->event(), "Started running script '%s'",
1045
0
    sieve_binary_source(interp->runenv.sbin));
1046
1047
0
  interp->running = TRUE;
1048
0
  interp->runenv.result = result;
1049
0
  interp->runenv.msgctx = sieve_result_get_message_context(result);
1050
0
  sieve_result_ref(result);
1051
1052
0
  sieve_resource_usage_init(&interp->rusage);
1053
1054
  /* Signal registered extensions that the interpreter is being run */
1055
0
  eregs = array_get_modifiable(&interp->extensions, &ext_count);
1056
0
  for (i = 0; i < ext_count; i++) {
1057
0
    if (!eregs[i].deferred) {
1058
0
      eregs[i].started = TRUE;
1059
0
      if (eregs[i].intext != NULL &&
1060
0
          eregs[i].intext->run != NULL &&
1061
0
          (ret = eregs[i].intext->run(
1062
0
        eregs[i].ext, &interp->runenv,
1063
0
        eregs[i].context, FALSE)) <= 0)
1064
0
        return ret;
1065
0
    }
1066
0
  }
1067
1068
0
  return sieve_interpreter_continue(interp, interrupted);
1069
0
}
1070
1071
int sieve_interpreter_run(struct sieve_interpreter *interp,
1072
        struct sieve_result *result)
1073
0
{
1074
0
  sieve_interpreter_reset(interp);
1075
1076
0
  return sieve_interpreter_start(interp, result, NULL);
1077
0
}
1078
1079
/*
1080
 * Error handling
1081
 */
1082
1083
static inline void ATTR_FORMAT(3, 0)
1084
sieve_runtime_logv(const struct sieve_runtime_env *renv,
1085
       const struct sieve_error_params *params,
1086
       const char *fmt, va_list args)
1087
0
{
1088
0
  struct sieve_error_params new_params = *params;
1089
1090
0
  new_params.event = renv->event;
1091
0
  T_BEGIN {
1092
0
    if (new_params.location == NULL) {
1093
0
      new_params.location =
1094
0
        sieve_runtime_get_full_command_location(renv);
1095
0
    }
1096
1097
0
    sieve_logv(renv->ehandler, params, fmt, args);
1098
0
  } T_END;
1099
0
}
1100
1101
#undef sieve_runtime_error
1102
void sieve_runtime_error(const struct sieve_runtime_env *renv,
1103
       const char *csrc_filename, unsigned int csrc_linenum,
1104
       const char *location, const char *fmt, ...)
1105
0
{
1106
0
  struct sieve_error_params params = {
1107
0
    .log_type = LOG_TYPE_ERROR,
1108
0
    .csrc = {
1109
0
      .filename = csrc_filename,
1110
0
      .linenum = csrc_linenum,
1111
0
    },
1112
0
    .location = location,
1113
0
  };
1114
0
  va_list args;
1115
1116
0
  va_start(args, fmt);
1117
0
  sieve_runtime_logv(renv, &params, fmt, args);
1118
0
  va_end(args);
1119
0
}
1120
1121
#undef sieve_runtime_warning
1122
void sieve_runtime_warning(const struct sieve_runtime_env *renv,
1123
         const char *csrc_filename, unsigned int csrc_linenum,
1124
         const char *location, const char *fmt, ...)
1125
0
{
1126
0
  struct sieve_error_params params = {
1127
0
    .log_type = LOG_TYPE_WARNING,
1128
0
    .csrc = {
1129
0
      .filename = csrc_filename,
1130
0
      .linenum = csrc_linenum,
1131
0
    },
1132
0
    .location = location,
1133
0
  };
1134
0
  va_list args;
1135
1136
0
  va_start(args, fmt);
1137
0
  sieve_runtime_logv(renv, &params, fmt, args);
1138
0
  va_end(args);
1139
0
}
1140
1141
#undef sieve_runtime_log
1142
void sieve_runtime_log(const struct sieve_runtime_env *renv,
1143
           const char *csrc_filename, unsigned int csrc_linenum,
1144
           const char *location, const char *fmt, ...)
1145
0
{
1146
0
  struct sieve_error_params params = {
1147
0
    .log_type = LOG_TYPE_INFO,
1148
0
    .csrc = {
1149
0
      .filename = csrc_filename,
1150
0
      .linenum = csrc_linenum,
1151
0
    },
1152
0
    .location = location,
1153
0
  };
1154
0
  va_list args;
1155
1156
0
  va_start(args, fmt);
1157
0
  sieve_runtime_logv(renv, &params, fmt, args);
1158
0
  va_end(args);
1159
0
}
1160
1161
#undef sieve_runtime_debug
1162
void sieve_runtime_debug(const struct sieve_runtime_env *renv,
1163
       const char *csrc_filename, unsigned int csrc_linenum,
1164
       const char *location, const char *fmt, ...)
1165
0
{
1166
0
  struct sieve_error_params params = {
1167
0
    .log_type = LOG_TYPE_DEBUG,
1168
0
    .csrc = {
1169
0
      .filename = csrc_filename,
1170
0
      .linenum = csrc_linenum,
1171
0
    },
1172
0
    .location = location,
1173
0
  };
1174
0
  va_list args;
1175
1176
0
  va_start(args, fmt);
1177
0
  sieve_runtime_logv(renv, &params, fmt, args);
1178
0
  va_end(args);
1179
0
}
1180
1181
#undef sieve_runtime_critical
1182
void sieve_runtime_critical(const struct sieve_runtime_env *renv,
1183
          const char *csrc_filename,
1184
          unsigned int csrc_linenum,
1185
          const char *location, const char *user_prefix,
1186
          const char *fmt, ...)
1187
0
{
1188
0
  const struct sieve_execute_env *eenv = renv->exec_env;
1189
0
  struct sieve_error_params params = {
1190
0
    .log_type = LOG_TYPE_ERROR,
1191
0
    .csrc = {
1192
0
      .filename = csrc_filename,
1193
0
      .linenum = csrc_linenum,
1194
0
    },
1195
0
    .location = location,
1196
0
  };
1197
0
  va_list args;
1198
1199
0
  va_start(args, fmt);
1200
1201
0
  params.event = renv->event;
1202
0
  T_BEGIN {
1203
0
    if (params.location == NULL) {
1204
0
      params.location =
1205
0
        sieve_runtime_get_full_command_location(renv);
1206
0
    }
1207
1208
0
    sieve_criticalv(eenv->svinst, renv->ehandler, &params,
1209
0
        user_prefix, fmt, args);
1210
0
  } T_END;
1211
1212
0
  va_end(args);
1213
0
}
1214
1215
#undef sieve_runtime_mail_error
1216
int sieve_runtime_mail_error(const struct sieve_runtime_env *renv,
1217
           struct mail *mail,
1218
           const char *csrc_filename,
1219
           unsigned int csrc_linenum,
1220
           const char *fmt, ...)
1221
0
{
1222
0
  const char *error_msg, *user_prefix;
1223
0
  va_list args;
1224
1225
0
  error_msg = mailbox_get_last_internal_error(mail->box, NULL);
1226
1227
0
  va_start(args, fmt);
1228
0
  user_prefix = t_strdup_vprintf(fmt, args);
1229
0
  sieve_runtime_critical(renv, csrc_filename, csrc_linenum,
1230
0
             NULL, user_prefix, "%s: %s",
1231
0
             user_prefix, error_msg);
1232
0
  va_end(args);
1233
1234
0
  return  SIEVE_EXEC_TEMP_FAILURE;
1235
0
}