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