Coverage Report

Created: 2026-10-17 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-binary.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "str-sanitize.h"
6
#include "mempool.h"
7
#include "buffer.h"
8
#include "hash.h"
9
#include "array.h"
10
#include "ostream.h"
11
#include "ioloop.h"
12
#include "eacces-error.h"
13
#include "safe-mkstemp.h"
14
15
#include "sieve-error.h"
16
#include "sieve-extensions.h"
17
#include "sieve-code.h"
18
#include "sieve-script.h"
19
#include "sieve-script-private.h"
20
#include "sieve-storage-private.h"
21
#include "sieve-rusage.h"
22
23
#include "sieve-binary-private.h"
24
25
/*
26
 * Forward declarations
27
 */
28
29
static inline struct sieve_binary_extension_reg *
30
sieve_binary_extension_get_reg(struct sieve_binary *sbin,
31
             const struct sieve_extension *ext, bool create);
32
33
static inline int
34
sieve_binary_extension_register(struct sieve_binary *sbin,
35
        const struct sieve_extension *ext,
36
        struct sieve_binary_extension_reg **reg);
37
38
/*
39
 * Binary object
40
 */
41
42
void sieve_binary_update_event(struct sieve_binary *sbin, const char *new_path)
43
0
{
44
0
  if (new_path != NULL) {
45
0
    event_set_append_log_prefix(
46
0
      sbin->event, t_strdup_printf("binary %s: ", new_path));
47
0
  } else if (sbin->path != NULL) {
48
0
    event_set_append_log_prefix(
49
0
      sbin->event,
50
0
      t_strdup_printf("binary %s: ", sbin->path));
51
0
  } else if (sbin->script != NULL) {
52
0
    event_set_append_log_prefix(
53
0
      sbin->event,
54
0
      t_strdup_printf("binary %s: ",
55
0
          sieve_script_name(sbin->script)));
56
0
  } else {
57
0
    event_set_append_log_prefix(sbin->event, "binary: ");
58
0
  }
59
0
}
60
61
struct sieve_binary *
62
sieve_binary_create(struct sieve_instance *svinst, struct sieve_script *script)
63
0
{
64
0
  pool_t pool;
65
0
  struct sieve_binary *sbin;
66
0
  const struct sieve_extension *const *ext_preloaded;
67
0
  unsigned int i, ext_count;
68
69
0
  pool = pool_alloconly_create("sieve_binary", 16384);
70
0
  sbin = p_new(pool, struct sieve_binary, 1);
71
0
  sbin->pool = pool;
72
0
  sbin->refcount = 1;
73
0
  sbin->svinst = svinst;
74
75
0
  sbin->header.version_major = SIEVE_BINARY_VERSION_MAJOR;
76
0
  sbin->header.version_minor = SIEVE_BINARY_VERSION_MINOR;
77
78
0
  sbin->script = script;
79
0
  if (script != NULL)
80
0
    sieve_script_ref(script);
81
82
0
  sbin->event = event_create(svinst->event);
83
84
0
  ext_count = sieve_extensions_get_count(svinst);
85
86
0
  p_array_init(&sbin->linked_extensions, pool, ext_count);
87
0
  p_array_init(&sbin->extensions, pool, ext_count);
88
0
  p_array_init(&sbin->extension_index, pool, ext_count);
89
90
0
  p_array_init(&sbin->blocks, pool, 16);
91
92
  /* Pre-load core language features implemented as 'extensions' */
93
0
  ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count);
94
0
  for (i = 0; i < ext_count; i++) {
95
0
    const struct sieve_extension_def *ext_def = ext_preloaded[i]->def;
96
97
0
    if (ext_def != NULL && ext_def->binary_load != NULL)
98
0
      (void)ext_def->binary_load(ext_preloaded[i], sbin);
99
0
  }
100
101
0
  return sbin;
102
0
}
103
104
struct sieve_binary *sieve_binary_create_new(struct sieve_script *script)
105
0
{
106
0
  struct sieve_binary *sbin =
107
0
    sieve_binary_create(sieve_script_svinst(script), script);
108
0
  struct sieve_binary_block *sblock;
109
0
  unsigned int i;
110
111
0
  sieve_binary_update_event(sbin, NULL);
112
113
  /* Create script metadata block */
114
0
  sblock = sieve_binary_block_create(sbin);
115
0
  sieve_script_binary_write_metadata(script, sblock);
116
117
  /* Create other system blocks */
118
0
  for (i = 1; i < SBIN_SYSBLOCK_LAST; i++)
119
0
    (void)sieve_binary_block_create(sbin);
120
121
0
  return sbin;
122
0
}
123
124
void sieve_binary_ref(struct sieve_binary *sbin)
125
0
{
126
0
  sbin->refcount++;
127
0
}
128
129
static inline void sieve_binary_extensions_free(struct sieve_binary *sbin)
130
0
{
131
0
  struct sieve_binary_extension_reg *const *regs;
132
0
  unsigned int ext_count, i;
133
134
  /* Cleanup binary extensions */
135
0
  regs = array_get(&sbin->extensions, &ext_count);
136
0
  for (i = 0; i < ext_count; i++) {
137
0
    const struct sieve_binary_extension *binext = regs[i]->binext;
138
139
0
    if (binext != NULL && binext->binary_free != NULL) {
140
0
      binext->binary_free(regs[i]->extension, sbin,
141
0
              regs[i]->context);
142
0
    }
143
0
  }
144
0
}
145
146
static void sieve_binary_update_resource_usage(struct sieve_binary *sbin)
147
0
{
148
0
  enum sieve_error error_code;
149
150
0
  if (sbin->rusage_updated) {
151
0
    (void)sieve_binary_file_update_resource_usage(
152
0
      sbin, &error_code);
153
0
  }
154
0
  sbin->rusage_updated = FALSE;
155
0
}
156
157
void sieve_binary_unref(struct sieve_binary **_sbin)
158
0
{
159
0
  struct sieve_binary *sbin = *_sbin;
160
161
0
  *_sbin = NULL;
162
0
  if (sbin == NULL)
163
0
    return;
164
165
0
  i_assert(sbin->refcount > 0);
166
0
  if (--sbin->refcount != 0)
167
0
    return;
168
169
0
  sieve_binary_file_close(&sbin->file);
170
0
  sieve_binary_update_resource_usage(sbin);
171
0
  sieve_binary_extensions_free(sbin);
172
173
0
  sieve_script_unref(&sbin->script);
174
175
0
  event_unref(&sbin->event);
176
0
  pool_unref(&sbin->pool);
177
0
}
178
179
void sieve_binary_close(struct sieve_binary **_sbin)
180
0
{
181
0
  struct sieve_binary *sbin = *_sbin;
182
183
0
  *_sbin = NULL;
184
0
  if (sbin == NULL)
185
0
    return;
186
187
0
  sieve_binary_file_close(&sbin->file);
188
0
  sieve_binary_update_resource_usage(sbin);
189
0
  sieve_binary_unref(&sbin);
190
0
}
191
192
/*
193
 * Resource usage
194
 */
195
196
static struct sieve_storage *
197
sieve_binary_get_user_storage(struct sieve_binary *sbin)
198
0
{
199
0
  if (sbin->script == NULL)
200
0
    return NULL;
201
0
  if (sbin->script->storage == NULL)
202
0
    return NULL;
203
0
  if (sbin->script->storage->rusage_path == NULL)
204
0
    return NULL;
205
0
  return sbin->script->storage;
206
0
}
207
208
void sieve_binary_apply_persisted_rusage(struct sieve_binary *sbin)
209
0
{
210
0
  struct sieve_storage *storage = sieve_binary_get_user_storage(sbin);
211
0
  struct sieve_resource_usage rusage;
212
213
0
  if (storage == NULL)
214
0
    return;
215
0
  if (sieve_rusage_storage_load(storage, &rusage) <= 0)
216
0
    return;
217
218
0
  sbin->persisted_rusage.cpu_time_msecs = rusage.cpu_time_msecs;
219
0
  sbin->persisted_rusage.update_time = ioloop_time;
220
0
  sieve_resource_usage_init(&sbin->rusage);
221
0
  sbin->rusage_updated = FALSE;
222
0
}
223
224
void sieve_binary_get_resource_usage(struct sieve_binary *sbin,
225
             struct sieve_resource_usage *rusage_r)
226
0
{
227
0
  time_t update_time = sbin->persisted_rusage.update_time;
228
0
  unsigned int timeout = sbin->svinst->set->resource_usage_timeout;
229
230
0
  if (update_time != 0 && (ioloop_time - update_time) > (time_t)timeout)
231
0
    i_zero(&sbin->persisted_rusage);
232
233
0
  sieve_resource_usage_init(rusage_r);
234
0
  rusage_r->cpu_time_msecs = sbin->persisted_rusage.cpu_time_msecs;
235
0
  sieve_resource_usage_add(rusage_r, &sbin->rusage);
236
0
}
237
238
bool sieve_binary_check_resource_usage(struct sieve_binary *sbin)
239
0
{
240
0
  struct sieve_resource_usage rusage;
241
242
0
  sieve_binary_get_resource_usage(sbin, &rusage);
243
0
  return !sieve_resource_usage_is_excessive(sbin->svinst, &rusage);
244
0
}
245
246
bool sieve_binary_record_resource_usage(
247
  struct sieve_binary *sbin, const struct sieve_resource_usage *rusage)
248
0
{
249
0
  struct sieve_resource_usage rusage_total;
250
0
  struct sieve_script *script;
251
252
0
  if (sbin == NULL)
253
0
    return TRUE;
254
0
  if (!sieve_resource_usage_is_high(sbin->svinst, rusage))
255
0
    return TRUE;
256
257
0
  sieve_resource_usage_add(&sbin->rusage, rusage);
258
0
  sbin->rusage_updated = TRUE;
259
260
0
  sieve_binary_get_resource_usage(sbin, &rusage_total);
261
262
0
  e_debug(sbin->event, "Updated cumulative resource usage: %s",
263
0
    sieve_resource_usage_get_summary(&rusage_total));
264
265
0
  if (!sieve_resource_usage_is_excessive(sbin->svinst, &rusage_total))
266
0
    return TRUE;
267
268
  /* Cumulative resource usage is only tracked for personal scripts;
269
     all callers gate this on the user's own script. */
270
0
  script = sbin->script;
271
0
  i_assert(script != NULL);
272
0
  i_assert(sieve_storage_is_personal(script->storage));
273
274
0
  e_warning(sbin->event,
275
0
      "Personal sieve processing disabled for script '%s': "
276
0
      "cumulative resource usage limit exceeded (%s)",
277
0
      sieve_script_label(script),
278
0
      sieve_resource_usage_get_summary(&rusage_total));
279
0
  return FALSE;
280
0
}
281
282
void sieve_binary_set_resource_usage(struct sieve_binary *sbin,
283
             const struct sieve_resource_usage *rusage)
284
0
{
285
0
  i_zero(&sbin->persisted_rusage);
286
0
  sbin->rusage = *rusage;
287
0
  sbin->rusage_updated = TRUE;
288
289
0
  (void)sieve_binary_check_resource_usage(sbin);
290
0
}
291
292
/*
293
 * Accessors
294
 */
295
296
pool_t sieve_binary_pool(struct sieve_binary *sbin)
297
0
{
298
0
  return sbin->pool;
299
0
}
300
301
struct sieve_script *sieve_binary_script(struct sieve_binary *sbin)
302
0
{
303
0
  return sbin->script;
304
0
}
305
306
const char *sieve_binary_path(struct sieve_binary *sbin)
307
0
{
308
0
  return sbin->path;
309
0
}
310
311
bool sieve_binary_saved(struct sieve_binary *sbin)
312
0
{
313
0
  return (sbin->path != NULL);
314
0
}
315
316
bool sieve_binary_loaded(struct sieve_binary *sbin)
317
0
{
318
0
  return (sbin->file != NULL);
319
0
}
320
321
const char *sieve_binary_source(struct sieve_binary *sbin)
322
0
{
323
0
  if (sbin->script != NULL && (sbin->path == NULL || sbin->file == NULL))
324
0
    return sieve_script_label(sbin->script);
325
326
0
  return sbin->path;
327
0
}
328
329
struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin)
330
0
{
331
0
  return sbin->svinst;
332
0
}
333
334
time_t sieve_binary_mtime(struct sieve_binary *sbin)
335
0
{
336
0
  i_assert(sbin->file != NULL);
337
0
  return sbin->file->st.st_mtime;
338
0
}
339
340
const struct stat *sieve_binary_stat(struct sieve_binary *sbin)
341
0
{
342
0
  i_assert(sbin->file != NULL);
343
0
  return &sbin->file->st;
344
0
}
345
346
const char *sieve_binary_script_name(struct sieve_binary *sbin)
347
0
{
348
0
  return (sbin->script == NULL ?
349
0
    NULL : sieve_script_name(sbin->script));
350
0
}
351
352
const char *sieve_binary_script_location(struct sieve_binary *sbin)
353
0
{
354
0
  return (sbin->script == NULL ?
355
0
    NULL : sieve_script_label(sbin->script));
356
0
}
357
358
/*
359
 * Utility
360
 */
361
362
const char *sieve_binfile_from_name(const char *name)
363
0
{
364
0
  return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL);
365
0
}
366
367
/*
368
 * Block management
369
 */
370
371
unsigned int sieve_binary_block_count(struct sieve_binary *sbin)
372
0
{
373
0
  return array_count(&sbin->blocks);
374
0
}
375
376
struct sieve_binary_block *sieve_binary_block_create(struct sieve_binary *sbin)
377
0
{
378
0
  unsigned int id = sieve_binary_block_count(sbin);
379
0
  struct sieve_binary_block *sblock;
380
381
0
  sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
382
0
  sblock->data = buffer_create_dynamic(sbin->pool, 64);
383
0
  sblock->sbin = sbin;
384
0
  sblock->id = id;
385
386
0
  array_append(&sbin->blocks, &sblock, 1);
387
388
0
  return sblock;
389
0
}
390
391
struct sieve_binary_block *
392
sieve_binary_block_create_id(struct sieve_binary *sbin, unsigned int id)
393
0
{
394
0
  struct sieve_binary_block *sblock;
395
396
0
  sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
397
398
0
  array_idx_set(&sbin->blocks, id, &sblock);
399
0
  sblock->data = NULL;
400
0
  sblock->sbin = sbin;
401
0
  sblock->id = id;
402
403
0
  return sblock;
404
0
}
405
406
static bool sieve_binary_block_fetch(struct sieve_binary_block *sblock)
407
0
{
408
0
  struct sieve_binary *sbin = sblock->sbin;
409
410
0
  if (sbin->file != NULL) {
411
    /* Try to acces the block in the binary on disk (apparently we
412
       were lazy)
413
     */
414
0
    if (!sieve_binary_load_block(sblock) || sblock->data == NULL)
415
0
      return FALSE;
416
0
  } else {
417
0
    sblock->data = buffer_create_dynamic(sbin->pool, 64);
418
0
    return TRUE;
419
0
  }
420
421
0
  return TRUE;
422
0
}
423
424
struct sieve_binary_block *
425
sieve_binary_block_get(struct sieve_binary *sbin, unsigned int id)
426
0
{
427
0
  struct sieve_binary_block *sblock = sieve_binary_block_index(sbin, id);
428
429
0
  if (sblock == NULL)
430
0
    return NULL;
431
432
0
  if (sblock->data == NULL && !sieve_binary_block_fetch(sblock))
433
0
    return NULL;
434
435
0
  return sblock;
436
0
}
437
438
void sieve_binary_block_clear(struct sieve_binary_block *sblock)
439
0
{
440
0
  buffer_set_used_size(sblock->data, 0);
441
0
}
442
443
buffer_t *sieve_binary_block_get_buffer(struct sieve_binary_block *sblock)
444
0
{
445
0
  if (sblock->data == NULL && !sieve_binary_block_fetch(sblock))
446
0
    return NULL;
447
448
0
  return sblock->data;
449
0
}
450
451
struct sieve_binary *
452
sieve_binary_block_get_binary(const struct sieve_binary_block *sblock)
453
0
{
454
0
  return sblock->sbin;
455
0
}
456
457
unsigned int sieve_binary_block_get_id(const struct sieve_binary_block *sblock)
458
0
{
459
0
  return sblock->id;
460
0
}
461
462
size_t sieve_binary_block_get_size(const struct sieve_binary_block *sblock)
463
0
{
464
0
  return _sieve_binary_block_get_size(sblock);
465
0
}
466
467
/*
468
 * Up-to-date checking
469
 */
470
471
bool sieve_binary_up_to_date(struct sieve_binary *sbin,
472
           enum sieve_compile_flags cpflags)
473
0
{
474
0
  struct sieve_binary_extension_reg *const *regs;
475
0
  struct sieve_binary_block *sblock;
476
0
  sieve_size_t offset = 0;
477
0
  unsigned int ext_count, i;
478
0
  int ret;
479
480
0
  i_assert(sbin->file != NULL);
481
482
0
  sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA);
483
0
  if (sblock == NULL || sbin->script == NULL)
484
0
    return FALSE;
485
486
0
  if ((ret = sieve_script_binary_read_metadata(sbin->script, sblock,
487
0
                 &offset)) <= 0) {
488
0
    if (ret < 0) {
489
0
      e_debug(sbin->event, "up-to-date: "
490
0
        "failed to read script metadata from binary");
491
0
    } else {
492
0
      e_debug(sbin->event, "up-to-date: "
493
0
        "script metadata indicates that binary is not up-to-date");
494
0
    }
495
0
    return FALSE;
496
0
  }
497
498
0
  regs = array_get(&sbin->extensions, &ext_count);
499
0
  for (i = 0; i < ext_count; i++) {
500
0
    const struct sieve_binary_extension *binext = regs[i]->binext;
501
502
0
    if (binext != NULL && binext->binary_up_to_date != NULL &&
503
0
        !binext->binary_up_to_date(regs[i]->extension, sbin,
504
0
                 regs[i]->context, cpflags)) {
505
0
      e_debug(sbin->event, "up-to-date: "
506
0
        "the %s extension indicates binary is not up-to-date",
507
0
        sieve_extension_name(regs[i]->extension));
508
0
      return FALSE;
509
0
    }
510
0
  }
511
0
  return TRUE;
512
0
}
513
514
/*
515
 * Activate the binary (after code generation)
516
 */
517
518
void sieve_binary_activate(struct sieve_binary *sbin)
519
0
{
520
0
  struct sieve_binary_extension_reg *const *regs;
521
0
  unsigned int i, ext_count;
522
523
  /* Load other extensions into binary */
524
0
  regs = array_get(&sbin->linked_extensions, &ext_count);
525
0
  for (i = 0; i < ext_count; i++) {
526
0
    const struct sieve_extension *ext = regs[i]->extension;
527
528
0
    if (ext != NULL && ext->def != NULL &&
529
0
        ext->def->binary_load != NULL)
530
0
      ext->def->binary_load(ext, sbin);
531
0
  }
532
0
}
533
534
/*
535
 * Extension handling
536
 */
537
538
void sieve_binary_extension_set_context(struct sieve_binary *sbin,
539
          const struct sieve_extension *ext,
540
          void *context)
541
0
{
542
0
  struct sieve_binary_extension_reg *ereg =
543
0
    sieve_binary_extension_get_reg(sbin, ext, TRUE);
544
545
0
  if (ereg != NULL)
546
0
    ereg->context = context;
547
0
}
548
549
const void *
550
sieve_binary_extension_get_context(struct sieve_binary *sbin,
551
           const struct sieve_extension *ext)
552
0
{
553
0
  struct sieve_binary_extension_reg *ereg =
554
0
    sieve_binary_extension_get_reg(sbin, ext, TRUE);
555
556
0
  if (ereg != NULL)
557
0
    return ereg->context;
558
559
0
  return NULL;
560
0
}
561
562
void sieve_binary_extension_set(struct sieve_binary *sbin,
563
        const struct sieve_extension *ext,
564
        const struct sieve_binary_extension *bext,
565
        void *context)
566
0
{
567
0
  struct sieve_binary_extension_reg *ereg =
568
0
    sieve_binary_extension_get_reg(sbin, ext, TRUE);
569
570
0
  if (ereg != NULL) {
571
0
    ereg->binext = bext;
572
573
0
    if (context != NULL)
574
0
      ereg->context = context;
575
0
  }
576
0
}
577
578
struct sieve_binary_block *
579
sieve_binary_extension_create_block(struct sieve_binary *sbin,
580
            const struct sieve_extension *ext)
581
0
{
582
0
  struct sieve_binary_block *sblock;
583
0
  struct sieve_binary_extension_reg *ereg =
584
0
    sieve_binary_extension_get_reg(sbin, ext, TRUE);
585
586
0
  i_assert(ereg != NULL);
587
588
0
  sblock = sieve_binary_block_create(sbin);
589
590
0
  if (ereg->block_id < SBIN_SYSBLOCK_LAST)
591
0
    ereg->block_id = sblock->id;
592
0
  sblock->ext_index = ereg->index;
593
594
0
  return sblock;
595
0
}
596
597
struct sieve_binary_block *
598
sieve_binary_extension_get_block(struct sieve_binary *sbin,
599
         const struct sieve_extension *ext)
600
0
{
601
0
  struct sieve_binary_extension_reg *ereg =
602
0
    sieve_binary_extension_get_reg(sbin, ext, TRUE);
603
604
0
  i_assert(ereg != NULL);
605
606
0
  if (ereg->block_id < SBIN_SYSBLOCK_LAST)
607
0
    return NULL;
608
609
0
  return sieve_binary_block_get(sbin, ereg->block_id);
610
0
}
611
612
int sieve_binary_extension_link(struct sieve_binary *sbin,
613
        const struct sieve_extension *ext)
614
0
{
615
0
  return sieve_binary_extension_register(sbin, ext, NULL);
616
0
}
617
618
const struct sieve_extension *
619
sieve_binary_extension_get_by_index(struct sieve_binary *sbin, int index)
620
0
{
621
0
  struct sieve_binary_extension_reg *const *ereg;
622
623
0
  if (index < (int)array_count(&sbin->extensions)) {
624
0
    ereg = array_idx(&sbin->extensions, (unsigned int)index);
625
626
0
    return (*ereg)->extension;
627
0
  }
628
629
0
  return NULL;
630
0
}
631
632
int sieve_binary_extension_get_index(struct sieve_binary *sbin,
633
             const struct sieve_extension *ext)
634
0
{
635
0
  struct sieve_binary_extension_reg *ereg =
636
0
    sieve_binary_extension_get_reg(sbin, ext, FALSE);
637
638
0
  return (ereg == NULL ? -1 : ereg->index);
639
0
}
640
641
int sieve_binary_extensions_count(struct sieve_binary *sbin)
642
0
{
643
0
  return (int)array_count(&sbin->extensions);
644
0
}