Coverage Report

Created: 2026-05-30 06:37

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