• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

drakenclimber / libseccomp / 12955456452

24 Jan 2025 12:43AM UTC coverage: 90.252% (+0.8%) from 89.454%
12955456452

push

github

pcmoore
doc: CHANGELOG entry for libseccomp v2.6.0

Signed-off-by: Paul Moore <paul@paul-moore.com>

2722 of 3016 relevant lines covered (90.25%)

301106.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.63
/src/db.c
1
/**
2
 * Enhanced Seccomp Filter DB
3
 *
4
 * Copyright (c) 2012,2016,2018 Red Hat <pmoore@redhat.com>
5
 * Copyright (c) 2019 Cisco Systems, Inc. <pmoore2@cisco.com>
6
 * Copyright (c) 2022 Microsoft Corporation <paulmoore@microsoft.com>
7
 * Author: Paul Moore <paul@paul-moore.com>
8
 */
9

10
/*
11
 * This library is free software; you can redistribute it and/or modify it
12
 * under the terms of version 2.1 of the GNU Lesser General Public License as
13
 * published by the Free Software Foundation.
14
 *
15
 * This library is distributed in the hope that it will be useful, but WITHOUT
16
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
18
 * for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with this library; if not, see <http://www.gnu.org/licenses>.
22
 */
23

24
#include <assert.h>
25
#include <errno.h>
26
#include <inttypes.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <stdarg.h>
30

31
#include <seccomp.h>
32

33
#include "arch.h"
34
#include "db.h"
35
#include "system.h"
36
#include "helper.h"
37

38
/* state values */
39
#define _DB_STA_VALID                        0xA1B2C3D4
40
#define _DB_STA_FREED                        0x1A2B3C4D
41

42
/* the priority field is fairly simple - without any user hints, or in the case
43
 * of a hint "tie", we give higher priority to syscalls with less chain nodes
44
 * (filter is easier to evaluate) */
45
#define _DB_PRI_MASK_CHAIN                0x0000FFFF
46
#define _DB_PRI_MASK_USER                0x00FF0000
47
#define _DB_PRI_USER(x)                        (((x) << 16) & _DB_PRI_MASK_USER)
48

49
/* prove information about the sub-tree check results */
50
struct db_iter_state {
51
#define _DB_IST_NONE                        0x00000000
52
#define _DB_IST_MATCH                        0x00000001
53
#define _DB_IST_MATCH_ONCE                0x00000002
54
#define _DB_IST_X_FINISHED                0x00000010
55
#define _DB_IST_N_FINISHED                0x00000020
56
#define _DB_IST_X_PREFIX                0x00000100
57
#define _DB_IST_N_PREFIX                0x00000200
58
#define _DB_IST_M_MATCHSET                (_DB_IST_MATCH|_DB_IST_MATCH_ONCE)
59
#define _DB_IST_M_REDUNDANT                (_DB_IST_MATCH| \
60
                                         _DB_IST_X_FINISHED| \
61
                                         _DB_IST_N_PREFIX)
62
        unsigned int flags;
63
        uint32_t action;
64
        struct db_sys_list *sx;
65
};
66

67
static unsigned int _db_node_put(struct db_arg_chain_tree **node);
68

69
/**
70
 * Define the syscall argument priority for nodes on the same level of the tree
71
 * @param a tree node
72
 *
73
 * Prioritize the syscall argument value, taking into account hi/lo words.
74
 * Should only ever really be called by _db_chain_{lt,eq}().  Returns an
75
 * arbitrary value indicating priority.
76
 *
77
 */
78
static unsigned int __db_chain_arg_priority(const struct db_arg_chain_tree *a)
2,181,820✔
79
{
80
        return (a->arg << 1) + (a->arg_h_flg ? 1 : 0);
3,272,730✔
81
}
82

83
/**
84
 * Define the "op" priority for nodes on the same level of the tree
85
 * @param op the argument operator
86
 *
87
 * Prioritize the syscall argument comparison operator.  Should only ever
88
 * really be called by _db_chain_{lt,eq}().  Returns an arbitrary value
89
 * indicating priority.
90
 *
91
 */
92
static unsigned int __db_chain_op_priority(enum scmp_compare op)
857,544✔
93
{
94
        /* the distinction between LT/LT and GT/GE is mostly to make the
95
         * ordering as repeatable as possible regardless of the order in which
96
         * the rules are added */
97
        switch (op) {
857,544✔
98
        case SCMP_CMP_MASKED_EQ:
99
        case SCMP_CMP_EQ:
100
        case SCMP_CMP_NE:
101
                return 3;
102
        case SCMP_CMP_LE:
5,776✔
103
        case SCMP_CMP_LT:
104
                return 2;
5,776✔
105
        case SCMP_CMP_GE:
3,372✔
106
        case SCMP_CMP_GT:
107
                return 1;
3,372✔
108
        default:
×
109
                return 0;
×
110
        }
111
}
112

113
/**
114
 * Determine if node "a" is less than node "b"
115
 * @param a tree node
116
 * @param b tree node
117
 *
118
 * The logic is best explained by looking at the comparison code in the
119
 * function.
120
 *
121
 */
122
static bool _db_chain_lt(const struct db_arg_chain_tree *a,
454,326✔
123
                         const struct db_arg_chain_tree *b)
124
{
125
        unsigned int a_arg, b_arg;
454,326✔
126
        unsigned int a_op, b_op;
454,326✔
127

128
        a_arg = __db_chain_arg_priority(a);
454,326✔
129
        b_arg = __db_chain_arg_priority(b);
454,326✔
130
        if (a_arg < b_arg)
454,326✔
131
                return true;
132
        else if (a_arg > b_arg)
442,268✔
133
                return false;
134

135
        a_op = __db_chain_op_priority(a->op_orig);
428,772✔
136
        b_op = __db_chain_op_priority(b->op_orig);
428,772✔
137
        if (a_op < b_op)
428,772✔
138
                return true;
139
        else if (a_op > b_op)
428,112✔
140
                return false;
141

142
        /* NOTE: at this point the arg and op priorities are equal */
143

144
        switch (a->op_orig) {
426,428✔
145
        case SCMP_CMP_LE:
2,184✔
146
        case SCMP_CMP_LT:
147
                /* in order to ensure proper ordering for LT/LE comparisons we
148
                 * need to invert the argument value so smaller values come
149
                 * first */
150
                if (a->datum > b->datum)
2,184✔
151
                        return true;
×
152
                break;
153
        default:
424,244✔
154
                if (a->datum < b->datum)
424,244✔
155
                        return true;
394,092✔
156
                break;
157
        }
158

159
        return false;
160
}
161

162
/**
163
 * Determine if two nodes have equal argument datum values
164
 * @param a tree node
165
 * @param b tree node
166
 *
167
 * In order to return true the nodes must have the same datum and mask for the
168
 * same argument.
169
 *
170
 */
171
static bool _db_chain_eq(const struct db_arg_chain_tree *a,
636,584✔
172
                         const struct db_arg_chain_tree *b)
173
{
174
        unsigned int a_arg, b_arg;
636,584✔
175

176
        a_arg = __db_chain_arg_priority(a);
636,584✔
177
        b_arg = __db_chain_arg_priority(b);
636,584✔
178

179
        return ((a_arg == b_arg) && (a->op == b->op) &&
611,030✔
180
                (a->datum == b->datum) && (a->mask == b->mask));
1,223,990✔
181
}
182

183
/**
184
 * Determine if a given tree node is a leaf node
185
 * @param iter the node to test
186
 *
187
 * A leaf node is a node with no other nodes beneath it.
188
 *
189
 */
190
static bool _db_chain_leaf(const struct db_arg_chain_tree *iter)
11,370✔
191
{
192
        return (iter->nxt_t == NULL && iter->nxt_f == NULL);
10,278✔
193
}
194

195
/**
196
 * Determine if a given tree node is a zombie node
197
 * @param iter the node to test
198
 *
199
 * A zombie node is a leaf node that also has no true or false actions.
200
 *
201
 */
202
static bool _db_chain_zombie(const struct db_arg_chain_tree *iter)
9,152✔
203
{
204
        return (_db_chain_leaf(iter) &&
18,304✔
205
                !(iter->act_t_flg) && !(iter->act_f_flg));
9,152✔
206
}
207

208
/**
209
 * Get a node reference
210
 * @param node pointer to a node
211
 *
212
 * This function gets a reference to an individual node.  Returns a pointer
213
 * to the node.
214
 *
215
 */
216
static struct db_arg_chain_tree *_db_node_get(struct db_arg_chain_tree *node)
635,248✔
217
{
218
        if (node != NULL)
635,248✔
219
                node->refcnt++;
122,312✔
220
        return node;
572,512✔
221
}
222

223
/**
224
 * Garbage collect a level of the tree
225
 * @param node tree node
226
 *
227
 * Check the entire level on which @node resides, if there is no other part of
228
 * the tree which points to a node on this level, remove the entire level.
229
 * Returns the number of nodes removed.
230
 *
231
 */
232
static unsigned int _db_level_clean(struct db_arg_chain_tree *node)
112,970✔
233
{
234
        int cnt = 0;
112,970✔
235
        unsigned int links;
112,970✔
236
        struct db_arg_chain_tree *n = node;
112,970✔
237
        struct db_arg_chain_tree *start;
112,970✔
238

239
        while (n->lvl_prv)
121,992✔
240
                n = n->lvl_prv;
9,022✔
241
        start = n;
218,606✔
242

243
        while (n != NULL) {
218,606✔
244
                links = 0;
179,918✔
245
                if (n->lvl_prv)
179,918✔
246
                        links++;
66,948✔
247
                if (n->lvl_nxt)
179,918✔
248
                        links++;
133,896✔
249

250
                if (n->refcnt > links)
179,918✔
251
                        return cnt;
252

253
                n = n->lvl_nxt;
105,636✔
254
        }
255

256
        n = start;
38,688✔
257
        while (n != NULL)
105,636✔
258
                cnt += _db_node_put(&n);
66,948✔
259

260
        return cnt;
38,688✔
261
}
262

263
/**
264
 * Free a syscall filter argument chain tree
265
 * @param tree the argument chain list
266
 *
267
 * This function drops a reference to the tree pointed to by @tree and garbage
268
 * collects the top level.  Returns the number of nodes removed.
269
 *
270
 */
271
static unsigned int _db_tree_put(struct db_arg_chain_tree **tree)
1,171,130✔
272
{
273
        unsigned int cnt;
1,171,130✔
274

275
        cnt = _db_node_put(tree);
1,171,130✔
276
        if (*tree)
1,171,130✔
277
                cnt += _db_level_clean(*tree);
112,970✔
278

279
        return cnt;
1,171,130✔
280
}
281

282
/**
283
 * Release a node reference
284
 * @param node pointer to a node
285
 *
286
 * This function drops a reference to an individual node, unless this is the
287
 * last reference in which the entire sub-tree is affected.  Returns the number
288
 * of nodes freed.
289
 *
290
 */
291
static unsigned int _db_node_put(struct db_arg_chain_tree **node)
1,676,930✔
292
{
293
        unsigned int cnt = 0;
1,676,930✔
294
        struct db_arg_chain_tree *n = *node;
1,676,930✔
295
        struct db_arg_chain_tree *lvl_p, *lvl_n, *nxt_t, *nxt_f;
1,676,930✔
296

297
        if (n == NULL)
1,676,930✔
298
                return 0;
299

300
        if (--(n->refcnt) == 0) {
920,916✔
301
                lvl_p = n->lvl_prv;
423,560✔
302
                lvl_n = n->lvl_nxt;
423,560✔
303
                nxt_t = n->nxt_t;
423,560✔
304
                nxt_f = n->nxt_f;
423,560✔
305

306
                /* split the current level */
307
                /* NOTE: we still hold a ref for both lvl_p and lvl_n */
308
                if (lvl_p)
423,560✔
309
                        lvl_p->lvl_nxt = NULL;
×
310
                if (lvl_n)
423,560✔
311
                        lvl_n->lvl_prv = NULL;
66,948✔
312

313
                /* drop refcnts on the current level */
314
                if (lvl_p)
423,560✔
315
                        cnt += _db_node_put(&lvl_p);
×
316
                if (lvl_n)
423,560✔
317
                        cnt += _db_node_put(&lvl_n);
66,948✔
318

319
                /* re-link current level if it still exists */
320
                if (lvl_p)
423,560✔
321
                        lvl_p->lvl_nxt = _db_node_get(lvl_n);
×
322
                if (lvl_n)
423,560✔
323
                        lvl_n->lvl_prv = _db_node_get(lvl_p);
28,260✔
324

325
                /* update caller's pointer */
326
                if (lvl_p)
423,560✔
327
                        *node = lvl_p;
×
328
                else if (lvl_n)
423,560✔
329
                        *node = lvl_n;
28,260✔
330
                else
331
                        *node = NULL;
395,300✔
332

333
                /* drop the next level(s) */
334
                cnt += _db_tree_put(&nxt_t);
423,560✔
335
                cnt += _db_tree_put(&nxt_f);
423,560✔
336

337
                /* cleanup and accounting */
338
                free(n);
423,560✔
339
                cnt++;
423,560✔
340
        }
341

342
        return cnt;
343
}
344

345
/**
346
 * Remove a node from an argument chain tree
347
 * @param tree the pointer to the tree
348
 * @param node the node to remove
349
 *
350
 * This function searches the tree looking for the node and removes it as well
351
 * as any sub-trees beneath it.  Returns the number of nodes freed.
352
 *
353
 */
354
static unsigned int _db_tree_remove(struct db_arg_chain_tree **tree,
20,592✔
355
                                    struct db_arg_chain_tree *node)
356
{
357
        int cnt = 0;
20,592✔
358
        struct db_arg_chain_tree *c_iter;
20,592✔
359

360
        if (tree == NULL || *tree == NULL || node == NULL)
20,592✔
361
                return 0;
362

363
        c_iter = *tree;
11,440✔
364
        while (c_iter->lvl_prv != NULL)
11,440✔
365
                c_iter = c_iter->lvl_prv;
×
366

367
        do {
11,440✔
368
                /* current node? */
369
                if (c_iter == node)
11,440✔
370
                        goto remove;
2,288✔
371

372
                /* check the sub-trees */
373
                cnt += _db_tree_remove(&(c_iter->nxt_t), node);
9,152✔
374
                cnt += _db_tree_remove(&(c_iter->nxt_f), node);
9,152✔
375

376
                /* check for empty/zombie nodes */
377
                if (_db_chain_zombie(c_iter))
18,304✔
378
                        goto remove;
9,152✔
379

380
                /* next node on this level */
381
                c_iter = c_iter->lvl_nxt;
×
382
        } while (c_iter != NULL && cnt == 0);
×
383

384
        return cnt;
385

386
remove:
11,440✔
387
        /* reset the tree pointer if needed */
388
        if (c_iter == *tree) {
11,440✔
389
                if (c_iter->lvl_prv != NULL)
11,440✔
390
                        *tree = c_iter->lvl_prv;
×
391
                else
392
                        *tree = c_iter->lvl_nxt;
11,440✔
393
        }
394

395
        /* remove the node from the current level */
396
        if (c_iter->lvl_prv)
11,440✔
397
                c_iter->lvl_prv->lvl_nxt = c_iter->lvl_nxt;
×
398
        if (c_iter->lvl_nxt)
11,440✔
399
                c_iter->lvl_nxt->lvl_prv = c_iter->lvl_prv;
×
400
        c_iter->lvl_prv = NULL;
11,440✔
401
        c_iter->lvl_nxt = NULL;
11,440✔
402

403
        /* free the node and any sub-trees */
404
        cnt += _db_node_put(&c_iter);
11,440✔
405

406
        return cnt;
11,440✔
407
}
408

409
/**
410
 * Traverse a tree checking the action values
411
 * @param tree the pointer to the tree
412
 * @param action the action
413
 *
414
 * Traverse the tree inspecting each action to see if it matches the given
415
 * action.  Returns zero if all actions match the given action, negative values
416
 * on failure.
417
 *
418
 */
419
static int _db_tree_act_check(struct db_arg_chain_tree *tree, uint32_t action)
7,784,680✔
420
{
421
        int rc;
7,784,680✔
422
        struct db_arg_chain_tree *c_iter;
7,784,680✔
423

424
        if (tree == NULL)
7,784,680✔
425
                return 0;
426

427
        c_iter = tree;
428
        while (c_iter->lvl_prv != NULL)
3,357,510✔
429
                c_iter = c_iter->lvl_prv;
430

431
        do {
3,816,650✔
432
                if (c_iter->act_t_flg && c_iter->act_t != action)
3,816,650✔
433
                        return -EEXIST;
434
                if (c_iter->act_f_flg && c_iter->act_f != action)
3,806,200✔
435
                        return -EEXIST;
436

437
                rc = _db_tree_act_check(c_iter->nxt_t, action);
3,806,050✔
438
                if (rc < 0)
3,806,050✔
439
                        return rc;
16,220✔
440
                rc = _db_tree_act_check(c_iter->nxt_f, action);
3,789,830✔
441
                if (rc < 0)
3,789,830✔
442
                        return rc;
156✔
443

444
                c_iter = c_iter->lvl_nxt;
3,789,670✔
445
        } while (c_iter != NULL);
3,789,670✔
446

447
        return 0;
448
}
449

450
/**
451
 * Checks for a sub-tree match in an existing tree and prunes the tree
452
 * @param existing pointer to the existing tree
453
 * @param new pointer to the new tree
454
 * @param state pointer to a state structure
455
 *
456
 * This function searches the existing tree trying to prune it based on the
457
 * new tree.  Returns the number of nodes removed from the tree on success,
458
 * zero if no changes were made.
459
 *
460
 */
461
static int _db_tree_prune(struct db_arg_chain_tree **existing,
462
                          struct db_arg_chain_tree *new,
463
                          struct db_iter_state *state)
464
{
465
        int cnt = 0;
466
        struct db_iter_state state_nxt;
467
        struct db_iter_state state_new = *state;
468
        struct db_arg_chain_tree *x_iter_next;
469
        struct db_arg_chain_tree *x_iter = *existing;
470
        struct db_arg_chain_tree *n_iter = new;
471

472
        /* check if either tree is finished */
473
        if (n_iter == NULL || x_iter == NULL)
474
                goto prune_return;
475

476
        /* bail out if we have a broken match */
477
        if ((state->flags & _DB_IST_M_MATCHSET) == _DB_IST_MATCH_ONCE)
478
                goto prune_return;
479

480
        /* get to the start of the existing level */
481
        while (x_iter->lvl_prv)
482
                x_iter = x_iter->lvl_prv;
483

484
        /* NOTE: a few comments on the code below ...
485
         * 1) we need to take a reference before we go down a level in case
486
         *    we end up dropping the sub-tree (see the _db_node_get() calls)
487
         * 2) since the new tree really only has one branch, we can only ever
488
         *    match on one branch in the existing tree, if we "hit" then we
489
         *    can bail on the other branches */
490

491
        do {
492
                /* store this now in case we remove x_iter */
493
                x_iter_next = x_iter->lvl_nxt;
494

495
                /* compare the two nodes */
496
                if (_db_chain_eq(x_iter, n_iter)) {
497
                        /* we have a match */
498
                        state_new.flags |= _DB_IST_M_MATCHSET;
499

500
                        /* check if either tree is finished */
501
                        if (_db_chain_leaf(n_iter))
502
                                state_new.flags |= _DB_IST_N_FINISHED;
503
                        if (_db_chain_leaf(x_iter))
504
                                state_new.flags |= _DB_IST_X_FINISHED;
505

506
                        /* don't remove nodes if we have more actions/levels */
507
                        if ((x_iter->act_t_flg || x_iter->nxt_t) &&
508
                            !(n_iter->act_t_flg || n_iter->nxt_t))
509
                                goto prune_return;
510
                        if ((x_iter->act_f_flg || x_iter->nxt_f) &&
511
                            !(n_iter->act_f_flg || n_iter->nxt_f))
512
                                goto prune_return;
513

514
                        /* if finished, compare actions */
515
                        if ((state_new.flags & _DB_IST_N_FINISHED) &&
516
                            (state_new.flags & _DB_IST_X_FINISHED)) {
517
                                if (n_iter->act_t_flg != x_iter->act_t_flg)
518
                                        goto prune_return;
519
                                if (n_iter->act_t != x_iter->act_t)
520
                                        goto prune_return;
521

522
                                if (n_iter->act_f_flg != x_iter->act_f_flg)
523
                                        goto prune_return;
524
                                if (n_iter->act_f != x_iter->act_f)
525
                                        goto prune_return;
526
                        }
527

528
                        /* check next level */
529
                        if (n_iter->nxt_t) {
530
                                _db_node_get(x_iter);
531
                                state_nxt = *state;
532
                                state_nxt.flags |= _DB_IST_M_MATCHSET;
533
                                cnt += _db_tree_prune(&x_iter->nxt_t,
534
                                                      n_iter->nxt_t,
535
                                                      &state_nxt);
536
                                cnt += _db_node_put(&x_iter);
537
                                if (state_nxt.flags & _DB_IST_MATCH) {
538
                                        state_new.flags |= state_nxt.flags;
539
                                        /* don't return yet, we need to check
540
                                         * the current node */
541
                                }
542
                                if (x_iter == NULL)
543
                                        goto prune_next_node;
544
                        }
545
                        if (n_iter->nxt_f) {
546
                                _db_node_get(x_iter);
547
                                state_nxt = *state;
548
                                state_nxt.flags |= _DB_IST_M_MATCHSET;
549
                                cnt += _db_tree_prune(&x_iter->nxt_f,
550
                                                      n_iter->nxt_f,
551
                                                      &state_nxt);
552
                                cnt += _db_node_put(&x_iter);
553
                                if (state_nxt.flags & _DB_IST_MATCH) {
554
                                        state_new.flags |= state_nxt.flags;
555
                                        /* don't return yet, we need to check
556
                                         * the current node */
557
                                }
558
                                if (x_iter == NULL)
559
                                        goto prune_next_node;
560
                        }
561

562
                        /* remove the node? */
563
                        if (!_db_tree_act_check(x_iter, state_new.action) &&
564
                            (state_new.flags & _DB_IST_MATCH) &&
565
                            (state_new.flags & _DB_IST_N_FINISHED) &&
566
                            (state_new.flags & _DB_IST_X_PREFIX)) {
567
                                /* yes - the new tree is "shorter" */
568
                                cnt += _db_tree_remove(&state->sx->chains,
569
                                                       x_iter);
570
                                if (state->sx->chains == NULL)
571
                                        goto prune_return;
572
                        } else if (!_db_tree_act_check(x_iter, state_new.action)
573
                                   && (state_new.flags & _DB_IST_MATCH) &&
574
                                   (state_new.flags & _DB_IST_X_FINISHED) &&
575
                                   (state_new.flags & _DB_IST_N_PREFIX)) {
576
                                /* no - the new tree is "longer" */
577
                                goto prune_return;
578
                        }
579
                } else if (_db_chain_lt(x_iter, n_iter)) {
580
                        /* bail if we have a prefix on the new tree */
581
                        if (state->flags & _DB_IST_N_PREFIX)
582
                                goto prune_return;
583

584
                        /* check the next level in the existing tree */
585
                        if (x_iter->nxt_t) {
586
                                _db_node_get(x_iter);
587
                                state_nxt = *state;
588
                                state_nxt.flags &= ~_DB_IST_MATCH;
589
                                state_nxt.flags |= _DB_IST_X_PREFIX;
590
                                cnt += _db_tree_prune(&x_iter->nxt_t, n_iter,
591
                                                      &state_nxt);
592
                                cnt += _db_node_put(&x_iter);
593
                                if (state_nxt.flags & _DB_IST_MATCH) {
594
                                        state_new.flags |= state_nxt.flags;
595
                                        goto prune_return;
596
                                }
597
                                if (x_iter == NULL)
598
                                        goto prune_next_node;
599
                        }
600
                        if (x_iter->nxt_f) {
601
                                _db_node_get(x_iter);
602
                                state_nxt = *state;
603
                                state_nxt.flags &= ~_DB_IST_MATCH;
604
                                state_nxt.flags |= _DB_IST_X_PREFIX;
605
                                cnt += _db_tree_prune(&x_iter->nxt_f, n_iter,
606
                                                      &state_nxt);
607
                                cnt += _db_node_put(&x_iter);
608
                                if (state_nxt.flags & _DB_IST_MATCH) {
609
                                        state_new.flags |= state_nxt.flags;
610
                                        goto prune_return;
611
                                }
612
                                if (x_iter == NULL)
613
                                        goto prune_next_node;
614
                        }
615
                } else {
616
                        /* bail if we have a prefix on the existing tree */
617
                        if (state->flags & _DB_IST_X_PREFIX)
618
                                goto prune_return;
619

620
                        /* check the next level in the new tree */
621
                        if (n_iter->nxt_t) {
622
                                _db_node_get(x_iter);
623
                                state_nxt = *state;
624
                                state_nxt.flags &= ~_DB_IST_MATCH;
625
                                state_nxt.flags |= _DB_IST_N_PREFIX;
626
                                cnt += _db_tree_prune(&x_iter, n_iter->nxt_t,
627
                                                      &state_nxt);
628
                                cnt += _db_node_put(&x_iter);
629
                                if (state_nxt.flags & _DB_IST_MATCH) {
630
                                        state_new.flags |= state_nxt.flags;
631
                                        goto prune_return;
632
                                }
633
                                if (x_iter == NULL)
634
                                        goto prune_next_node;
635
                        }
636
                        if (n_iter->nxt_f) {
637
                                _db_node_get(x_iter);
638
                                state_nxt = *state;
639
                                state_nxt.flags &= ~_DB_IST_MATCH;
640
                                state_nxt.flags |= _DB_IST_N_PREFIX;
641
                                cnt += _db_tree_prune(&x_iter, n_iter->nxt_f,
642
                                                      &state_nxt);
643
                                cnt += _db_node_put(&x_iter);
644
                                if (state_nxt.flags & _DB_IST_MATCH) {
645
                                        state_new.flags |= state_nxt.flags;
646
                                        goto prune_return;
647
                                }
648
                                if (x_iter == NULL)
649
                                        goto prune_next_node;
650
                        }
651
                }
652

653
prune_next_node:
654
                /* check next node on this level */
655
                x_iter = x_iter_next;
656
        } while (x_iter);
657

658
        /* if we are falling through, we clearly didn't match on anything */
659
        state_new.flags &= ~_DB_IST_MATCH;
660

661
prune_return:
662
        /* no more nodes on this level, return to the level above */
663
        if (state_new.flags & _DB_IST_MATCH)
664
                state->flags |= state_new.flags;
665
        else
666
                state->flags &= ~_DB_IST_MATCH;
667
        return cnt;
668
}
669

670
/**
671
 * Add a new tree into an existing tree
672
 * @param existing pointer to the existing tree
673
 * @param new pointer to the new tree
674
 * @param state pointer to a state structure
675
 *
676
 * This function adds the new tree into the existing tree, fetching additional
677
 * references as necessary.  Returns zero on success, negative values on
678
 * failure.
679
 *
680
 */
681
static int _db_tree_add(struct db_arg_chain_tree **existing,
145,466✔
682
                        struct db_arg_chain_tree *new,
683
                        struct db_iter_state *state)
684
{
685
        int rc;
145,466✔
686
        struct db_arg_chain_tree *x_iter = *existing;
145,466✔
687
        struct db_arg_chain_tree *n_iter = new;
145,466✔
688

689
        do {
159,884✔
690
                if (_db_chain_eq(x_iter, n_iter)) {
159,884✔
691
                        if (n_iter->act_t_flg) {
78,518✔
692
                                if (!x_iter->act_t_flg) {
1,066✔
693
                                        /* new node has a true action */
694

695
                                        /* do the actions match? */
696
                                        rc = _db_tree_act_check(x_iter->nxt_t,
282✔
697
                                                                n_iter->act_t);
698
                                        if (rc != 0)
282✔
699
                                                return rc;
700

701
                                        /* update with the new action */
702
                                        rc = _db_node_put(&x_iter->nxt_t);
282✔
703
                                        x_iter->nxt_t = NULL;
282✔
704
                                        x_iter->act_t = n_iter->act_t;
282✔
705
                                        x_iter->act_t_flg = true;
282✔
706
                                        state->sx->node_cnt -= rc;
282✔
707
                                } else if (n_iter->act_t != x_iter->act_t) {
784✔
708
                                        /* if we are dealing with a 64-bit
709
                                         * comparison, we need to adjust our
710
                                         * action based on the full 64-bit
711
                                         * value to ensure we handle GT/GE
712
                                         * comparisons correctly */
713
                                        if (n_iter->arg_h_flg &&
580✔
714
                                            (n_iter->datum_full >
546✔
715
                                             x_iter->datum_full))
546✔
716
                                                x_iter->act_t = n_iter->act_t;
546✔
717
                                        if (_db_chain_leaf(x_iter) ||
1,706✔
718
                                            _db_chain_leaf(n_iter))
546✔
719
                                                return -EEXIST;
720
                                }
721
                        }
722
                        if (n_iter->act_f_flg) {
78,484✔
723
                                if (!x_iter->act_f_flg) {
5,744✔
724
                                        /* new node has a false action */
725

726
                                        /* do the actions match? */
727
                                        rc = _db_tree_act_check(x_iter->nxt_f,
5,198✔
728
                                                                n_iter->act_f);
729
                                        if (rc != 0)
5,198✔
730
                                                return rc;
731

732
                                        /* update with the new action */
733
                                        rc = _db_node_put(&x_iter->nxt_f);
5,198✔
734
                                        x_iter->nxt_f = NULL;
5,198✔
735
                                        x_iter->act_f = n_iter->act_f;
5,198✔
736
                                        x_iter->act_f_flg = true;
5,198✔
737
                                        state->sx->node_cnt -= rc;
5,198✔
738
                                } else if (n_iter->act_f != x_iter->act_f) {
546✔
739
                                        /* if we are dealing with a 64-bit
740
                                         * comparison, we need to adjust our
741
                                         * action based on the full 64-bit
742
                                         * value to ensure we handle LT/LE
743
                                         * comparisons correctly */
744
                                        if (n_iter->arg_h_flg &&
546✔
745
                                            (n_iter->datum_full <
546✔
746
                                             x_iter->datum_full))
546✔
747
                                                x_iter->act_t = n_iter->act_t;
546✔
748
                                        if (_db_chain_leaf(x_iter) ||
1,638✔
749
                                            _db_chain_leaf(n_iter))
546✔
750
                                                return -EEXIST;
751
                                }
752
                        }
753

754
                        if (n_iter->nxt_t) {
78,484✔
755
                                if (x_iter->nxt_t) {
73,936✔
756
                                        /* compare the next level */
757
                                        rc = _db_tree_add(&x_iter->nxt_t,
72,724✔
758
                                                          n_iter->nxt_t,
759
                                                          state);
760
                                        if (rc != 0)
72,724✔
761
                                                return rc;
762
                                } else if (!x_iter->act_t_flg) {
1,212✔
763
                                        /* add a new sub-tree */
764
                                        x_iter->nxt_t = _db_node_get(n_iter->nxt_t);
1,144✔
765
                                } else
766
                                        /* done - existing tree is "shorter" */
767
                                        return 0;
768
                        }
769
                        if (n_iter->nxt_f) {
78,382✔
770
                                if (x_iter->nxt_f) {
2,044✔
771
                                        /* compare the next level */
772
                                        rc = _db_tree_add(&x_iter->nxt_f,
1,636✔
773
                                                          n_iter->nxt_f,
774
                                                          state);
775
                                        if (rc != 0)
1,636✔
776
                                                return rc;
777
                                } else if (!x_iter->act_f_flg) {
408✔
778
                                        /* add a new sub-tree */
779
                                        x_iter->nxt_f = _db_node_get(n_iter->nxt_f);
136✔
780
                                } else
781
                                        /* done - existing tree is "shorter" */
782
                                        return 0;
783
                        }
784

785
                        return 0;
78,110✔
786
                } else if (!_db_chain_lt(x_iter, n_iter)) {
81,366✔
787
                        /* try to move along the current level */
788
                        if (x_iter->lvl_nxt == NULL) {
18,630✔
789
                                /* add to the end of this level */
790
                                n_iter->lvl_prv = _db_node_get(x_iter);
4,212✔
791
                                x_iter->lvl_nxt = _db_node_get(n_iter);
4,212✔
792
                                return 0;
4,212✔
793
                        } else
794
                                /* next */
795
                                x_iter = x_iter->lvl_nxt;
14,418✔
796
                } else {
797
                        /* add before the existing node on this level*/
798
                        if (x_iter->lvl_prv != NULL) {
62,736✔
799
                                x_iter->lvl_prv->lvl_nxt = _db_node_get(n_iter);
3,160✔
800
                                n_iter->lvl_prv = x_iter->lvl_prv;
3,160✔
801
                                x_iter->lvl_prv = _db_node_get(n_iter);
3,160✔
802
                                n_iter->lvl_nxt = x_iter;
3,160✔
803
                        } else {
804
                                x_iter->lvl_prv = _db_node_get(n_iter);
59,576✔
805
                                n_iter->lvl_nxt = _db_node_get(x_iter);
59,576✔
806
                        }
807
                        if (*existing == x_iter) {
62,736✔
808
                                *existing = _db_node_get(n_iter);
59,576✔
809
                                _db_node_put(&x_iter);
59,576✔
810
                        }
811
                        return 0;
62,736✔
812
                }
813
        } while (x_iter);
14,418✔
814

815
        return 0;
816
}
817

818
/**
819
 * Free and reset the seccomp filter DB
820
 * @param db the seccomp filter DB
821
 *
822
 * This function frees any existing filters and resets the filter DB to a
823
 * default state; only the DB architecture is preserved.
824
 *
825
 */
826
static void _db_reset(struct db_filter *db)
82,306✔
827
{
828
        struct db_sys_list *s_iter;
82,306✔
829
        struct db_api_rule_list *r_iter;
82,306✔
830

831
        if (db == NULL)
82,306✔
832
                return;
833

834
        /* free any filters */
835
        if (db->syscalls != NULL) {
82,306✔
836
                s_iter = db->syscalls;
837
                while (s_iter != NULL) {
284,631✔
838
                        db->syscalls = s_iter->next;
247,219✔
839
                        _db_tree_put(&s_iter->chains);
247,219✔
840
                        free(s_iter);
247,219✔
841
                        s_iter = db->syscalls;
247,219✔
842
                }
843
                db->syscalls = NULL;
37,412✔
844
        }
845
        db->syscall_cnt = 0;
82,306✔
846

847
        /* free any rules */
848
        if (db->rules != NULL) {
82,306✔
849
                /* split the loop first then loop and free */
850
                db->rules->prev->next = NULL;
38,114✔
851
                r_iter = db->rules;
38,114✔
852
                while (r_iter != NULL) {
339,774✔
853
                        db->rules = r_iter->next;
301,660✔
854
                        free(r_iter);
301,660✔
855
                        r_iter = db->rules;
301,660✔
856
                }
857
                db->rules = NULL;
38,114✔
858
        }
859
}
860

861
/**
862
 * Initialize a seccomp filter DB
863
 * @param arch the architecture definition
864
 *
865
 * This function initializes a seccomp filter DB and readies it for use.
866
 * Returns a pointer to the DB on success, NULL on failure.
867
 *
868
 */
869
static struct db_filter *_db_init(const struct arch_def *arch)
41,153✔
870
{
871
        struct db_filter *db;
41,153✔
872

873
        db = zmalloc(sizeof(*db));
41,153✔
874
        if (db == NULL)
41,153✔
875
                return NULL;
876

877
        /* set the arch and reset the DB to a known state */
878
        db->arch = arch;
41,153✔
879
        _db_reset(db);
41,153✔
880

881
        return db;
41,153✔
882
}
883

884
/**
885
 * Destroy a seccomp filter DB
886
 * @param db the seccomp filter DB
887
 *
888
 * This function destroys a seccomp filter DB.  After calling this function,
889
 * the filter should no longer be referenced.
890
 *
891
 */
892
static void _db_release(struct db_filter *db)
41,153✔
893
{
894
        if (db == NULL)
41,153✔
895
                return;
896

897
        /* free and reset the DB */
898
        _db_reset(db);
16,669✔
899
        free(db);
35,851✔
900
}
901

902
/**
903
 * Destroy a seccomp filter snapshot
904
 * @param snap the seccomp filter snapshot
905
 *
906
 * This function destroys a seccomp filter snapshot.  After calling this
907
 * function, the snapshot should no longer be referenced.
908
 *
909
 */
910
static void _db_snap_release(struct db_filter_snap *snap)
8,983✔
911
{
912
        unsigned int iter;
8,983✔
913

914
        if (snap == NULL)
8,983✔
915
                return;
916

917
        if (snap->filter_cnt > 0) {
8,983✔
918
                for (iter = 0; iter < snap->filter_cnt; iter++) {
28,165✔
919
                        if (snap->filters[iter])
19,182✔
920
                                _db_release(snap->filters[iter]);
38,364✔
921
                }
922
                free(snap->filters);
8,983✔
923
        }
924
        free(snap);
8,983✔
925
}
926

927
/**
928
 * Update the user specified portion of the syscall priority
929
 * @param db the seccomp filter db
930
 * @param syscall the syscall number
931
 * @param priority the syscall priority
932
 *
933
 * This function sets, or updates, the syscall priority; the highest priority
934
 * value between the existing and specified value becomes the new syscall
935
 * priority.  If the syscall entry does not already exist, a new phantom
936
 * syscall entry is created as a placeholder.  Returns zero on success,
937
 * negative values on failure.
938
 *
939
 */
940
static int _db_syscall_priority(struct db_filter *db,
79✔
941
                                int syscall, uint8_t priority)
942
{
943
        unsigned int sys_pri = _DB_PRI_USER(priority);
79✔
944
        struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
79✔
945

946
        assert(db != NULL);
79✔
947

948
        s_iter = db->syscalls;
79✔
949
        while (s_iter != NULL && s_iter->num < syscall) {
189✔
950
                s_prev = s_iter;
110✔
951
                s_iter = s_iter->next;
110✔
952
        }
953

954
        /* matched an existing syscall entry */
955
        if (s_iter != NULL && s_iter->num == syscall) {
79✔
956
                if (sys_pri > (s_iter->priority & _DB_PRI_MASK_USER)) {
30✔
957
                        s_iter->priority &= (~_DB_PRI_MASK_USER);
30✔
958
                        s_iter->priority |= sys_pri;
30✔
959
                }
960
                return 0;
30✔
961
        }
962

963
        /* no existing syscall entry - create a phantom entry */
964
        s_new = zmalloc(sizeof(*s_new));
49✔
965
        if (s_new == NULL)
49✔
966
                return -ENOMEM;
967
        s_new->num = syscall;
49✔
968
        s_new->priority = sys_pri;
49✔
969
        s_new->valid = false;
49✔
970

971
        /* add it before s_iter */
972
        if (s_prev != NULL) {
49✔
973
                s_new->next = s_prev->next;
29✔
974
                s_prev->next = s_new;
29✔
975
        } else {
976
                s_new->next = db->syscalls;
20✔
977
                db->syscalls = s_new;
20✔
978
        }
979

980
        return 0;
981
}
982

983
/**
984
 * Create a new rule
985
 * @param strict the strict value
986
 * @param action the rule's action
987
 * @param syscall the syscall number
988
 * @param chain the syscall argument filter
989
 *
990
 * This function creates a new rule structure based on the given arguments.
991
 * Returns a pointer to the new rule on success, NULL on failure.
992
 *
993
 */
994
static struct db_api_rule_list *_db_rule_new(bool strict,
132,422✔
995
                                             uint32_t action, int syscall,
996
                                             struct db_api_arg *chain)
997
{
998
        struct db_api_rule_list *rule;
132,422✔
999

1000
        rule = zmalloc(sizeof(*rule));
132,422✔
1001
        if (rule == NULL)
132,422✔
1002
                return NULL;
1003
        rule->action = action;
132,422✔
1004
        rule->syscall = syscall;
132,422✔
1005
        rule->strict = strict;
132,422✔
1006
        memcpy(rule->args, chain, sizeof(*chain) * ARG_COUNT_MAX);
132,422✔
1007

1008
        return rule;
132,422✔
1009
}
1010

1011
/**
1012
 * Duplicate an existing filter rule
1013
 * @param src the rule to duplicate
1014
 *
1015
 * This function makes an exact copy of the given rule, but does not add it
1016
 * to any lists.  Returns a pointer to the new rule on success, NULL on
1017
 * failure.
1018
 *
1019
 */
1020
struct db_api_rule_list *db_rule_dup(const struct db_api_rule_list *src)
496,864✔
1021
{
1022
        struct db_api_rule_list *dest;
496,864✔
1023

1024
        dest = malloc(sizeof(*dest));
496,864✔
1025
        if (dest == NULL)
496,864✔
1026
                return NULL;
1027
        memcpy(dest, src, sizeof(*dest));
496,864✔
1028
        dest->prev = NULL;
496,864✔
1029
        dest->next = NULL;
496,864✔
1030

1031
        return dest;
496,864✔
1032
}
1033

1034
/**
1035
 * Free and reset the seccomp filter collection
1036
 * @param col the seccomp filter collection
1037
 * @param def_action the default filter action
1038
 *
1039
 * This function frees any existing filter DBs and resets the collection to a
1040
 * default state.  In the case of failure the filter collection may be in an
1041
 * unknown state and should be released.  Returns zero on success, negative
1042
 * values on failure.
1043
 *
1044
 */
1045
int db_col_reset(struct db_filter_col *col, uint32_t def_action)
8,215✔
1046
{
1047
        unsigned int iter;
8,215✔
1048
        struct db_filter *db;
8,215✔
1049
        struct db_filter_snap *snap;
8,215✔
1050

1051
        if (col == NULL)
8,215✔
1052
                return -EINVAL;
1053

1054
        /* free any filters */
1055
        for (iter = 0; iter < col->filter_cnt; iter++)
8,580✔
1056
                _db_release(col->filters[iter]);
730✔
1057
        col->filter_cnt = 0;
8,215✔
1058
        if (col->filters)
8,215✔
1059
                free(col->filters);
365✔
1060
        col->filters = NULL;
8,215✔
1061

1062
        /* set the endianness to undefined */
1063
        col->endian = 0;
8,215✔
1064

1065
        /* set the default attribute values */
1066
        col->attr.act_default = def_action;
8,215✔
1067
        col->attr.act_badarch = SCMP_ACT_KILL;
8,215✔
1068
        col->attr.nnp_enable = 1;
8,215✔
1069
        col->attr.tsync_enable = 0;
8,215✔
1070
        col->attr.api_tskip = 0;
8,215✔
1071
        col->attr.log_enable = 0;
8,215✔
1072
        col->attr.spec_allow = 0;
8,215✔
1073
        col->attr.optimize = 1;
8,215✔
1074
        col->attr.api_sysrawrc = 0;
8,215✔
1075
        col->attr.wait_killable_recv = 0;
8,215✔
1076

1077
        /* set the state */
1078
        col->state = _DB_STA_VALID;
8,215✔
1079
        if (def_action == SCMP_ACT_NOTIFY)
8,215✔
1080
                col->notify_used = true;
×
1081
        else
1082
                col->notify_used = false;
8,215✔
1083

1084
        /* reset the initial db */
1085
        db = _db_init(arch_def_native);
8,215✔
1086
        if (db == NULL)
8,215✔
1087
                return -ENOMEM;
1088
        if (db_col_db_add(col, db) < 0) {
8,215✔
1089
                _db_release(db);
×
1090
                return -ENOMEM;
×
1091
        }
1092

1093
        /* reset the transactions */
1094
        while (col->snapshots) {
8,580✔
1095
                snap = col->snapshots;
365✔
1096
                col->snapshots = snap->next;
365✔
1097
                for (iter = 0; iter < snap->filter_cnt; iter++)
730✔
1098
                        _db_release(snap->filters[iter]);
730✔
1099
                free(snap->filters);
365✔
1100
                free(snap);
365✔
1101
        }
1102

1103
        /* reset the precomputed programs */
1104
        db_col_precompute_reset(col);
16,430✔
1105

1106
        return 0;
1107
}
1108

1109
/**
1110
 * Initialize a seccomp filter collection
1111
 * @param def_action the default filter action
1112
 *
1113
 * This function initializes a seccomp filter collection and readies it for
1114
 * use.  Returns a pointer to the collection on success, NULL on failure.
1115
 *
1116
 */
1117
struct db_filter_col *db_col_init(uint32_t def_action)
7,850✔
1118
{
1119
        struct db_filter_col *col;
7,850✔
1120

1121
        col = zmalloc(sizeof(*col));
7,850✔
1122
        if (col == NULL)
7,850✔
1123
                return NULL;
1124

1125
        /* reset the DB to a known state */
1126
        if (db_col_reset(col, def_action) < 0) {
7,850✔
1127
                db_col_release(col);
×
1128
                return NULL;
×
1129
        }
1130

1131
        return col;
1132
}
1133

1134
/**
1135
 * Destroy a seccomp filter collection
1136
 * @param col the seccomp filter collection
1137
 *
1138
 * This function destroys a seccomp filter collection.  After calling this
1139
 * function, the filter should no longer be referenced.
1140
 *
1141
 */
1142
void db_col_release(struct db_filter_col *col)
7,850✔
1143
{
1144
        unsigned int iter;
7,850✔
1145
        struct db_filter_snap *snap;
7,850✔
1146

1147
        if (col == NULL)
7,850✔
1148
                return;
1149

1150
        /* set the state, just in case */
1151
        col->state = _DB_STA_FREED;
7,850✔
1152

1153
        /* free any snapshots */
1154
        while (col->snapshots != NULL) {
14,271✔
1155
                snap = col->snapshots;
6,421✔
1156
                col->snapshots = snap->next;
6,421✔
1157
                _db_snap_release(snap);
6,421✔
1158
        }
1159

1160
        /* free any filters */
1161
        for (iter = 0; iter < col->filter_cnt; iter++)
22,511✔
1162
                _db_release(col->filters[iter]);
29,322✔
1163
        col->filter_cnt = 0;
7,850✔
1164
        if (col->filters)
7,850✔
1165
                free(col->filters);
7,568✔
1166
        col->filters = NULL;
7,850✔
1167

1168
        /* free any precompute */
1169
        db_col_precompute_reset(col);
15,700✔
1170

1171
        /* free the collection */
1172
        free(col);
7,850✔
1173
}
1174

1175
/**
1176
 * Validate a filter collection
1177
 * @param col the seccomp filter collection
1178
 *
1179
 * This function validates a seccomp filter collection.  Returns zero if the
1180
 * collection is valid, negative values on failure.
1181
 *
1182
 */
1183
int db_col_valid(struct db_filter_col *col)
68,966✔
1184
{
1185
        if (col != NULL && col->state == _DB_STA_VALID && col->filter_cnt > 0)
68,966✔
1186
                return 0;
68,958✔
1187
        return -EINVAL;
1188
}
1189

1190
/**
1191
 * Validate the seccomp action
1192
 * @param col the seccomp filter collection
1193
 * @param action the seccomp action
1194
 *
1195
 * Verify that the given action is a valid seccomp action; return zero if
1196
 * valid, -EINVAL if invalid.
1197
 */
1198
int db_col_action_valid(const struct db_filter_col *col, uint32_t action)
61,922✔
1199
{
1200
        if (col != NULL) {
61,922✔
1201
                /* NOTE: in some cases we don't have a filter collection yet,
1202
                 *       but when we do we need to do the following checks */
1203

1204
                /* kernel disallows TSYNC and NOTIFY in one filter unless we
1205
                 * have the TSYNC_ESRCH flag */
1206
                if (sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH) < 1 &&
53,706✔
1207
                    col->attr.tsync_enable && action == SCMP_ACT_NOTIFY)
2,152✔
1208
                        return -EINVAL;
1209
        }
1210

1211
        if (sys_chk_seccomp_action(action) == 1)
61,922✔
1212
                return 0;
61,918✔
1213
        return -EINVAL;
1214
}
1215

1216
/**
1217
 * Merge two filter collections
1218
 * @param col_dst the destination filter collection
1219
 * @param col_src the source filter collection
1220
 *
1221
 * This function merges two filter collections into the given destination
1222
 * collection.  The source filter collection is no longer valid if the function
1223
 * returns successfully.  Returns zero on success, negative values on failure.
1224
 *
1225
 */
1226
int db_col_merge(struct db_filter_col *col_dst, struct db_filter_col *col_src)
26✔
1227
{
1228
        unsigned int iter_a, iter_b;
26✔
1229
        struct db_filter **dbs;
26✔
1230

1231
        /* verify that the endianness is a match */
1232
        if (col_dst->endian != col_src->endian)
26✔
1233
                return -EDOM;
1234

1235
        /* make sure we don't have any arch/filter collisions */
1236
        for (iter_a = 0; iter_a < col_dst->filter_cnt; iter_a++) {
52✔
1237
                for (iter_b = 0; iter_b < col_src->filter_cnt; iter_b++) {
52✔
1238
                        if (col_dst->filters[iter_a]->arch->token ==
26✔
1239
                            col_src->filters[iter_b]->arch->token)
26✔
1240
                                return -EEXIST;
1241
                }
1242
        }
1243

1244
        /* expand the destination */
1245
        dbs = realloc(col_dst->filters,
26✔
1246
                      sizeof(struct db_filter *) *
1247
                      (col_dst->filter_cnt + col_src->filter_cnt));
26✔
1248
        if (dbs == NULL)
26✔
1249
                return -ENOMEM;
1250
        col_dst->filters = dbs;
26✔
1251

1252
        /* transfer the architecture filters */
1253
        for (iter_a = col_dst->filter_cnt, iter_b = 0;
26✔
1254
             iter_b < col_src->filter_cnt; iter_a++, iter_b++) {
52✔
1255
                col_dst->filters[iter_a] = col_src->filters[iter_b];
26✔
1256
                col_dst->filter_cnt++;
26✔
1257
        }
1258

1259
        /* reset the precompute */
1260
        db_col_precompute_reset(col_dst);
52✔
1261

1262
        /* free the source */
1263
        col_src->filter_cnt = 0;
26✔
1264
        db_col_release(col_src);
26✔
1265

1266
        return 0;
26✔
1267
}
1268

1269
/**
1270
 * Check to see if an architecture filter exists in the filter collection
1271
 * @param col the seccomp filter collection
1272
 * @param arch_token the architecture token
1273
 *
1274
 * Iterate through the given filter collection checking to see if a filter
1275
 * exists for the specified architecture.  Returns -EEXIST if a filter is found,
1276
 * zero if a matching filter does not exist.
1277
 *
1278
 */
1279
int db_col_arch_exist(struct db_filter_col *col, uint32_t arch_token)
38,307✔
1280
{
1281
        unsigned int iter;
38,307✔
1282

1283
        for (iter = 0; iter < col->filter_cnt; iter++)
154,605✔
1284
                if (col->filters[iter]->arch->token == arch_token)
121,882✔
1285
                        return -EEXIST;
1286

1287
        return 0;
1288
}
1289

1290
/**
1291
 * Get a filter attribute
1292
 * @param col the seccomp filter collection
1293
 * @param attr the filter attribute
1294
 * @param value the filter attribute value
1295
 *
1296
 * Get the requested filter attribute and provide it via @value.  Returns zero
1297
 * on success, negative values on failure.
1298
 *
1299
 */
1300
int db_col_attr_get(const struct db_filter_col *col,
15✔
1301
                    enum scmp_filter_attr attr, uint32_t *value)
1302
{
1303
        int rc = 0;
15✔
1304

1305
        switch (attr) {
15✔
1306
        case SCMP_FLTATR_ACT_DEFAULT:
1✔
1307
                *value = col->attr.act_default;
1✔
1308
                break;
1✔
1309
        case SCMP_FLTATR_ACT_BADARCH:
1✔
1310
                *value = col->attr.act_badarch;
1✔
1311
                break;
1✔
1312
        case SCMP_FLTATR_CTL_NNP:
1✔
1313
                *value = col->attr.nnp_enable;
1✔
1314
                break;
1✔
1315
        case SCMP_FLTATR_CTL_TSYNC:
1✔
1316
                *value = col->attr.tsync_enable;
1✔
1317
                break;
1✔
1318
        case SCMP_FLTATR_API_TSKIP:
1✔
1319
                *value = col->attr.api_tskip;
1✔
1320
                break;
1✔
1321
        case SCMP_FLTATR_CTL_LOG:
1✔
1322
                *value = col->attr.log_enable;
1✔
1323
                break;
1✔
1324
        case SCMP_FLTATR_CTL_SSB:
1✔
1325
                *value = col->attr.spec_allow;
1✔
1326
                break;
1✔
1327
        case SCMP_FLTATR_CTL_OPTIMIZE:
1✔
1328
                *value = col->attr.optimize;
1✔
1329
                break;
1✔
1330
        case SCMP_FLTATR_API_SYSRAWRC:
5✔
1331
                *value = col->attr.api_sysrawrc;
5✔
1332
                break;
5✔
1333
        case SCMP_FLTATR_CTL_WAITKILL:
1✔
1334
                *value = col->attr.wait_killable_recv;
1✔
1335
                break;
1✔
1336
        default:
1337
                rc = -EINVAL;
1338
                break;
1339
        }
1340

1341
        return rc;
15✔
1342
}
1343

1344
/**
1345
 * Get a filter attribute
1346
 * @param col the seccomp filter collection
1347
 * @param attr the filter attribute
1348
 *
1349
 * Returns the requested filter attribute value with zero on any error.
1350
 * Special care must be given with this function as error conditions can be
1351
 * hidden from the caller.
1352
 *
1353
 */
1354
uint32_t db_col_attr_read(const struct db_filter_col *col,
4✔
1355
                          enum scmp_filter_attr attr)
1356
{
1357
        uint32_t value = 0;
4✔
1358

1359
        db_col_attr_get(col, attr, &value);
4✔
1360
        return value;
4✔
1361
}
1362

1363
/**
1364
 * Set a filter attribute
1365
 * @param col the seccomp filter collection
1366
 * @param attr the filter attribute
1367
 * @param value the filter attribute value
1368
 *
1369
 * Set the requested filter attribute with the given value.  Returns zero on
1370
 * success, negative values on failure.
1371
 *
1372
 */
1373
int db_col_attr_set(struct db_filter_col *col,
529✔
1374
                    enum scmp_filter_attr attr, uint32_t value)
1375
{
1376
        int rc = 0;
529✔
1377

1378
        switch (attr) {
529✔
1379
        case SCMP_FLTATR_ACT_DEFAULT:
1380
                /* read only */
1381
                return -EACCES;
1382
                break;
1✔
1383
        case SCMP_FLTATR_ACT_BADARCH:
1✔
1384
                if (db_col_action_valid(col, value) == 0)
1✔
1385
                        col->attr.act_badarch = value;
1✔
1386
                else
1387
                        return -EINVAL;
1388
                db_col_precompute_reset(col);
530✔
1389
                break;
1390
        case SCMP_FLTATR_CTL_NNP:
1✔
1391
                col->attr.nnp_enable = (value ? 1 : 0);
1✔
1392
                break;
1✔
1393
        case SCMP_FLTATR_CTL_TSYNC:
2✔
1394
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC);
2✔
1395
                if (rc == 1) {
2✔
1396
                        /* supported */
1397
                        rc = 0;
2✔
1398
                        /* kernel disallows TSYNC and NOTIFY in one filter
1399
                         * unless we have TSYNC_ESRCH */
1400
                        if (sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH) < 1 &&
2✔
1401
                            value && col->notify_used)
1✔
1402
                                return -EINVAL;
1403
                        col->attr.tsync_enable = (value ? 1 : 0);
2✔
1404
                } else if (rc == 0)
×
1405
                        /* unsupported */
1406
                        rc = -EOPNOTSUPP;
×
1407
                break;
1408
        case SCMP_FLTATR_API_TSKIP:
4✔
1409
                col->attr.api_tskip = (value ? 1 : 0);
4✔
1410
                db_col_precompute_reset(col);
533✔
1411
                break;
1412
        case SCMP_FLTATR_CTL_LOG:
2✔
1413
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_LOG);
2✔
1414
                if (rc == 1) {
2✔
1415
                        /* supported */
1416
                        rc = 0;
2✔
1417
                        col->attr.log_enable = (value ? 1 : 0);
2✔
1418
                } else if (rc == 0) {
×
1419
                        /* unsupported */
1420
                        rc = -EOPNOTSUPP;
×
1421
                }
1422
                break;
1423
        case SCMP_FLTATR_CTL_SSB:
2✔
1424
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW);
2✔
1425
                if (rc == 1) {
2✔
1426
                        /* supported */
1427
                        rc = 0;
2✔
1428
                        col->attr.spec_allow = (value ? 1 : 0);
2✔
1429
                } else if (rc == 0) {
×
1430
                        /* unsupported */
1431
                        rc = -EOPNOTSUPP;
×
1432
                }
1433
                break;
1434
        case SCMP_FLTATR_CTL_OPTIMIZE:
512✔
1435
                switch (value) {
512✔
1436
                case 1:
512✔
1437
                case 2:
1438
                        col->attr.optimize = value;
512✔
1439
                        break;
512✔
1440
                default:
1441
                        rc = -EOPNOTSUPP;
1442
                        break;
1443
                }
1444
                db_col_precompute_reset(col);
1,041✔
1445
                break;
1446
        case SCMP_FLTATR_API_SYSRAWRC:
2✔
1447
                col->attr.api_sysrawrc = (value ? 1 : 0);
2✔
1448
                break;
2✔
1449
        case SCMP_FLTATR_CTL_WAITKILL:
1✔
1450
                col->attr.wait_killable_recv = (value ? 1 : 0);
1✔
1451
                break;
1✔
1452
        default:
1453
                rc = -EINVAL;
1454
                break;
1455
        }
1456

1457
        return rc;
1458
}
1459

1460
/**
1461
 * Add a new architecture filter to a filter collection
1462
 * @param col the seccomp filter collection
1463
 * @param arch the architecture
1464
 *
1465
 * This function adds a new architecture filter DB to an existing seccomp
1466
 * filter collection assuming there isn't a filter DB already present with the
1467
 * same architecture.  Returns zero on success, negative values on failure.
1468
 *
1469
 */
1470
int db_col_db_new(struct db_filter_col *col, const struct arch_def *arch)
12,113✔
1471
{
1472
        int rc;
12,113✔
1473
        struct db_filter *db;
12,113✔
1474

1475
        db = _db_init(arch);
12,113✔
1476
        if (db == NULL)
12,113✔
1477
                return -ENOMEM;
1478
        rc = db_col_db_add(col, db);
12,113✔
1479
        if (rc < 0)
12,113✔
1480
                _db_release(db);
×
1481
        else
1482
                db_col_precompute_reset(col);
24,226✔
1483

1484
        return rc;
1485
}
1486

1487
/**
1488
 * Add a new filter DB to a filter collection
1489
 * @param col the seccomp filter collection
1490
 * @param db the seccomp filter DB
1491
 *
1492
 * This function adds an existing seccomp filter DB to an existing seccomp
1493
 * filter collection assuming there isn't a filter DB already present with the
1494
 * same architecture.  Returns zero on success, negative values on failure.
1495
 *
1496
 */
1497
int db_col_db_add(struct db_filter_col *col, struct db_filter *db)
20,328✔
1498
{
1499
        struct db_filter **dbs;
20,328✔
1500

1501
        if (col->endian != 0 && col->endian != db->arch->endian)
20,328✔
1502
                return -EDOM;
1503

1504
        if (db_col_arch_exist(col, db->arch->token))
40,656✔
1505
                return -EEXIST;
1506

1507
        dbs = realloc(col->filters,
20,328✔
1508
                      sizeof(struct db_filter *) * (col->filter_cnt + 1));
20,328✔
1509
        if (dbs == NULL)
20,328✔
1510
                return -ENOMEM;
1511
        col->filters = dbs;
20,328✔
1512
        col->filter_cnt++;
20,328✔
1513
        col->filters[col->filter_cnt - 1] = db;
20,328✔
1514
        if (col->endian == 0)
20,328✔
1515
                col->endian = db->arch->endian;
10,129✔
1516

1517
        return 0;
1518
}
1519

1520
/**
1521
 * Remove a filter DB from a filter collection
1522
 * @param col the seccomp filter collection
1523
 * @param arch_token the architecture token
1524
 *
1525
 * This function removes an existing seccomp filter DB from an existing seccomp
1526
 * filter collection.  Returns zero on success, negative values on failure.
1527
 *
1528
 */
1529
int db_col_db_remove(struct db_filter_col *col, uint32_t arch_token)
5,302✔
1530
{
1531
        unsigned int iter;
5,302✔
1532
        unsigned int found;
5,302✔
1533
        struct db_filter **dbs;
5,302✔
1534

1535
        if ((col->filter_cnt <= 0) || (db_col_arch_exist(col, arch_token) == 0))
10,604✔
1536
                return -EINVAL;
1537

1538
        for (found = 0, iter = 0; iter < col->filter_cnt; iter++) {
29,220✔
1539
                if (found)
23,918✔
1540
                        col->filters[iter - 1] = col->filters[iter];
18,612✔
1541
                else if (col->filters[iter]->arch->token == arch_token) {
5,306✔
1542
                        _db_release(col->filters[iter]);
5,302✔
1543
                        found = 1;
5,302✔
1544
                }
1545
        }
1546
        col->filters[--col->filter_cnt] = NULL;
5,302✔
1547

1548
        if (col->filter_cnt > 0) {
5,302✔
1549
                /* NOTE: if we can't do the realloc it isn't fatal, we just
1550
                 *       have some extra space allocated */
1551
                dbs = realloc(col->filters,
3,106✔
1552
                              sizeof(struct db_filter *) * col->filter_cnt);
1553
                if (dbs != NULL)
3,106✔
1554
                        col->filters = dbs;
3,106✔
1555
        } else {
1556
                /* this was the last filter so free all the associated memory
1557
                 * and reset the endian token */
1558
                free(col->filters);
2,196✔
1559
                col->filters = NULL;
2,196✔
1560
                col->endian = 0;
2,196✔
1561
        }
1562

1563
        db_col_precompute_reset(col);
10,604✔
1564

1565
        return 0;
1566
}
1567

1568
/**
1569
 * Test if the argument filter can be skipped because it's a tautology
1570
 * @param arg argument filter
1571
 *
1572
 * If this argument filter applied to the lower 32 bit can be skipped this
1573
 * function returns false.
1574
 *
1575
 */
1576
static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg)
61,776✔
1577
{
1578
        if (arg->op == SCMP_CMP_MASKED_EQ && D64_LO(arg->mask) == 0)
12✔
1579
                return false;
68✔
1580

1581
        return true;
1582
}
1583

1584
/**
1585
 * Test if the argument filter can be skipped because it's a tautology
1586
 * @param arg argument filter
1587
 *
1588
 * If this argument filter applied to the upper 32 bit can be skipped this
1589
 * function returns false.
1590
 *
1591
 */
1592
static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg)
183,082✔
1593
{
1594
        if (arg->op == SCMP_CMP_MASKED_EQ && D64_HI(arg->mask) == 0)
11,356✔
1595
                return false;
11,356✔
1596

1597
        return true;
1598
}
1599

1600
/**
1601
 * Fixup the node based on the op/mask
1602
 * @param node the chain node
1603
 *
1604
 * Ensure the datum is masked as well.
1605
 *
1606
 */
1607
static void _db_node_mask_fixup(struct db_arg_chain_tree *node)
233,434✔
1608
{
1609
        node->datum &= node->mask;
233,434✔
1610
}
1611

1612
/**
1613
 * Generate a new filter rule for a 64 bit system
1614
 * @param arch the architecture definition
1615
 * @param rule the new filter rule
1616
 *
1617
 * This function generates a new syscall filter for a 64 bit system. Returns
1618
 * zero on success, negative values on failure.
1619
 *
1620
 */
1621
static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
230,638✔
1622
                                           const struct db_api_rule_list *rule)
1623
{
1624
        unsigned int iter;
230,638✔
1625
        struct db_sys_list *s_new;
230,638✔
1626
        const struct db_api_arg *chain = rule->args;
230,638✔
1627
        struct db_arg_chain_tree *c_iter[3] = { NULL, NULL, NULL };
230,638✔
1628
        struct db_arg_chain_tree *c_prev[3] = { NULL, NULL, NULL };
230,638✔
1629
        enum scmp_compare op_prev = _SCMP_CMP_MIN;
230,638✔
1630
        unsigned int arg;
230,638✔
1631
        scmp_datum_t mask;
230,638✔
1632
        scmp_datum_t datum;
230,638✔
1633

1634
        s_new = zmalloc(sizeof(*s_new));
230,638✔
1635
        if (s_new == NULL)
230,638✔
1636
                return NULL;
1637
        s_new->num = rule->syscall;
230,638✔
1638
        s_new->valid = true;
230,638✔
1639
        /* run through the argument chain */
1640
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
1,614,470✔
1641
                if (chain[iter].valid == 0)
1,383,830✔
1642
                        continue;
1,200,750✔
1643

1644
                /* TODO: handle the case were either hi or lo isn't needed */
1645

1646
                /* skip generating instruction which are no-ops */
1647
                if (!_db_arg_cmp_need_hi(&chain[iter]) &&
183,082✔
1648
                    !_db_arg_cmp_need_lo(&chain[iter]))
11,356✔
1649
                        continue;
68✔
1650

1651
                c_iter[0] = zmalloc(sizeof(*c_iter[0]));
183,014✔
1652
                if (c_iter[0] == NULL)
183,014✔
1653
                        goto gen_64_failure;
×
1654
                c_iter[1] = zmalloc(sizeof(*c_iter[1]));
183,014✔
1655
                if (c_iter[1] == NULL) {
183,014✔
1656
                        free(c_iter[0]);
×
1657
                        goto gen_64_failure;
×
1658
                }
1659
                c_iter[2] = NULL;
183,014✔
1660

1661
                arg = chain[iter].arg;
183,014✔
1662
                mask = chain[iter].mask;
183,014✔
1663
                datum = chain[iter].datum;
183,014✔
1664

1665
                /* NOTE: with the idea that a picture is worth a thousand
1666
                 *       words, i'm presenting the following diagrams which
1667
                 *       show how we should compare 64-bit syscall arguments
1668
                 *       using 32-bit comparisons.
1669
                 *
1670
                 *       in the diagrams below "A(x)" is the syscall argument
1671
                 *       being evaluated and "R(x)" is the syscall argument
1672
                 *       value specified in the libseccomp rule.  the "ACCEPT"
1673
                 *       verdict indicates a rule match and processing should
1674
                 *       continue on to the rest of the rule, or the final rule
1675
                 *       action should be triggered.  the "REJECT" verdict
1676
                 *       indicates that the rule does not match and processing
1677
                 *       should continue to the next rule or the default
1678
                 *       action.
1679
                 *
1680
                 * SCMP_CMP_GT:
1681
                 *                   +------------------+
1682
                 *                +--|  Ah(x) >  Rh(x)  |------+
1683
                 *                |  +------------------+      |
1684
                 *              FALSE                         TRUE     A
1685
                 *                |                            |       C
1686
                 *                +-----------+                +---->  C
1687
                 *                            v                +---->  E
1688
                 *                   +------------------+      |       P
1689
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1690
                 *        R       |  +------------------+  |   |
1691
                 *        E     FALSE                     TRUE |
1692
                 *        J  <----+                        |   |
1693
                 *        E  <----+           +------------+   |
1694
                 *        C     FALSE         v                |
1695
                 *        T       |  +------------------+      |
1696
                 *                +--|  Al(x) >  Rl(x)  |------+
1697
                 *                   +------------------+
1698
                 *
1699
                 * SCMP_CMP_GE:
1700
                 *                   +------------------+
1701
                 *                +--|  Ah(x) >  Rh(x)  |------+
1702
                 *                |  +------------------+      |
1703
                 *              FALSE                         TRUE     A
1704
                 *                |                            |       C
1705
                 *                +-----------+                +---->  C
1706
                 *                            v                +---->  E
1707
                 *                   +------------------+      |       P
1708
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1709
                 *        R       |  +------------------+  |   |
1710
                 *        E     FALSE                     TRUE |
1711
                 *        J  <----+                        |   |
1712
                 *        E  <----+           +------------+   |
1713
                 *        C     FALSE         v                |
1714
                 *        T       |  +------------------+      |
1715
                 *                +--|  Al(x) >= Rl(x)  |------+
1716
                 *                   +------------------+
1717
                 *
1718
                 * SCMP_CMP_LT:
1719
                 *                   +------------------+
1720
                 *                +--|  Ah(x) >  Rh(x)  |------+
1721
                 *                |  +------------------+      |
1722
                 *              FALSE                         TRUE     R
1723
                 *                |                            |       E
1724
                 *                +-----------+                +---->  J
1725
                 *                            v                +---->  E
1726
                 *                   +------------------+      |       C
1727
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1728
                 *        A       |  +------------------+  |   |
1729
                 *        C     FALSE                     TRUE |
1730
                 *        C  <----+                        |   |
1731
                 *        E  <----+           +------------+   |
1732
                 *        P     FALSE         v                |
1733
                 *        T       |  +------------------+      |
1734
                 *                +--|  Al(x) >= Rl(x)  |------+
1735
                 *                   +------------------+
1736
                 *
1737
                 * SCMP_CMP_LE:
1738
                 *                   +------------------+
1739
                 *                +--|  Ah(x) >  Rh(x)  |------+
1740
                 *                |  +------------------+      |
1741
                 *              FALSE                         TRUE     R
1742
                 *                |                            |       E
1743
                 *                +-----------+                +---->  J
1744
                 *                            v                +---->  E
1745
                 *                   +------------------+      |       C
1746
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1747
                 *        A       |  +------------------+  |   |
1748
                 *        C     FALSE                     TRUE |
1749
                 *        C  <----+                        |   |
1750
                 *        E  <----+           +------------+   |
1751
                 *        P     FALSE         v                |
1752
                 *        T       |  +------------------+      |
1753
                 *                +--|  Al(x) >  Rl(x)  |------+
1754
                 *                   +------------------+
1755
                 *
1756
                 * SCMP_CMP_EQ:
1757
                 *                   +------------------+
1758
                 *                +--|  Ah(x) == Rh(x)  |--+
1759
                 *        R       |  +------------------+  |           A
1760
                 *        E     FALSE                     TRUE         C
1761
                 *        J  <----+                        |           C
1762
                 *        E  <----+           +------------+   +---->  E
1763
                 *        C     FALSE         v                |       P
1764
                 *        T       |  +------------------+      |       T
1765
                 *                +--|  Al(x) == Rl(x)  |------+
1766
                 *                   +------------------+
1767
                 *
1768
                 * SCMP_CMP_NE:
1769
                 *                   +------------------+
1770
                 *                +--|  Ah(x) == Rh(x)  |--+
1771
                 *        A       |  +------------------+  |           R
1772
                 *        C     FALSE                     TRUE         E
1773
                 *        C  <----+                        |           J
1774
                 *        E  <----+           +------------+   +---->  E
1775
                 *        P     FALSE         v                |       C
1776
                 *        T       |  +------------------+      |       T
1777
                 *                +--|  Al(x) == Rl(x)  |------+
1778
                 *                   +------------------+
1779
                 *
1780
                 */
1781

1782
                /* setup the level */
1783
                switch (chain[iter].op) {
183,014✔
1784
                case SCMP_CMP_GT:
7,112✔
1785
                case SCMP_CMP_GE:
1786
                case SCMP_CMP_LE:
1787
                case SCMP_CMP_LT:
1788
                        c_iter[2] = zmalloc(sizeof(*c_iter[2]));
7,112✔
1789
                        if (c_iter[2] == NULL) {
7,112✔
1790
                                free(c_iter[0]);
×
1791
                                free(c_iter[1]);
×
1792
                                goto gen_64_failure;
×
1793
                        }
1794

1795
                        c_iter[0]->arg = arg;
7,112✔
1796
                        c_iter[1]->arg = arg;
7,112✔
1797
                        c_iter[2]->arg = arg;
7,112✔
1798
                        c_iter[0]->arg_h_flg = true;
7,112✔
1799
                        c_iter[1]->arg_h_flg = true;
7,112✔
1800
                        c_iter[2]->arg_h_flg = false;
7,112✔
1801
                        c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
7,112✔
1802
                        c_iter[1]->arg_offset = arch_arg_offset_hi(arch, arg);
7,112✔
1803
                        c_iter[2]->arg_offset = arch_arg_offset_lo(arch, arg);
7,112✔
1804

1805
                        c_iter[0]->mask = D64_HI(mask);
7,112✔
1806
                        c_iter[1]->mask = D64_HI(mask);
7,112✔
1807
                        c_iter[2]->mask = D64_LO(mask);
7,112✔
1808
                        c_iter[0]->datum = D64_HI(datum);
7,112✔
1809
                        c_iter[1]->datum = D64_HI(datum);
7,112✔
1810
                        c_iter[2]->datum = D64_LO(datum);
7,112✔
1811
                        c_iter[0]->datum_full = datum;
7,112✔
1812
                        c_iter[1]->datum_full = datum;
7,112✔
1813
                        c_iter[2]->datum_full = datum;
7,112✔
1814

1815
                        _db_node_mask_fixup(c_iter[0]);
7,112✔
1816
                        _db_node_mask_fixup(c_iter[1]);
7,112✔
1817
                        _db_node_mask_fixup(c_iter[2]);
7,112✔
1818

1819
                        c_iter[0]->op = SCMP_CMP_GT;
7,112✔
1820
                        c_iter[1]->op = SCMP_CMP_EQ;
7,112✔
1821
                        switch (chain[iter].op) {
7,112✔
1822
                        case SCMP_CMP_GT:
774✔
1823
                        case SCMP_CMP_LE:
1824
                                c_iter[2]->op = SCMP_CMP_GT;
774✔
1825
                                break;
774✔
1826
                        case SCMP_CMP_GE:
6,338✔
1827
                        case SCMP_CMP_LT:
1828
                                c_iter[2]->op = SCMP_CMP_GE;
6,338✔
1829
                                break;
6,338✔
1830
                        default:
×
1831
                                /* we should never get here */
1832
                                goto gen_64_failure;
×
1833
                        }
1834
                        c_iter[0]->op_orig = chain[iter].op;
7,112✔
1835
                        c_iter[1]->op_orig = chain[iter].op;
7,112✔
1836
                        c_iter[2]->op_orig = chain[iter].op;
7,112✔
1837

1838
                        c_iter[0]->nxt_f = _db_node_get(c_iter[1]);
7,112✔
1839
                        c_iter[1]->nxt_t = _db_node_get(c_iter[2]);
7,112✔
1840
                        break;
7,112✔
1841
                case SCMP_CMP_EQ:
175,902✔
1842
                case SCMP_CMP_MASKED_EQ:
1843
                case SCMP_CMP_NE:
1844
                        c_iter[0]->arg = arg;
175,902✔
1845
                        c_iter[1]->arg = arg;
175,902✔
1846
                        c_iter[0]->arg_h_flg = true;
175,902✔
1847
                        c_iter[1]->arg_h_flg = false;
175,902✔
1848
                        c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
175,902✔
1849
                        c_iter[1]->arg_offset = arch_arg_offset_lo(arch, arg);
175,902✔
1850

1851
                        c_iter[0]->mask = D64_HI(mask);
175,902✔
1852
                        c_iter[1]->mask = D64_LO(mask);
175,902✔
1853
                        c_iter[0]->datum = D64_HI(datum);
175,902✔
1854
                        c_iter[1]->datum = D64_LO(datum);
175,902✔
1855
                        c_iter[0]->datum_full = datum;
175,902✔
1856
                        c_iter[1]->datum_full = datum;
175,902✔
1857

1858
                        _db_node_mask_fixup(c_iter[0]);
175,902✔
1859
                        _db_node_mask_fixup(c_iter[1]);
175,902✔
1860

1861
                        switch (chain[iter].op) {
175,902✔
1862
                        case SCMP_CMP_MASKED_EQ:
11,288✔
1863
                                c_iter[0]->op = SCMP_CMP_MASKED_EQ;
11,288✔
1864
                                c_iter[1]->op = SCMP_CMP_MASKED_EQ;
11,288✔
1865
                                break;
11,288✔
1866
                        default:
164,614✔
1867
                                c_iter[0]->op = SCMP_CMP_EQ;
164,614✔
1868
                                c_iter[1]->op = SCMP_CMP_EQ;
164,614✔
1869
                        }
1870
                        c_iter[0]->op_orig = chain[iter].op;
175,902✔
1871
                        c_iter[1]->op_orig = chain[iter].op;
175,902✔
1872

1873
                        c_iter[0]->nxt_t = _db_node_get(c_iter[1]);
175,902✔
1874
                        break;
175,902✔
1875
                default:
×
1876
                        /* we should never get here */
1877
                        goto gen_64_failure;
×
1878
                }
1879

1880
                /* link this level to the previous level */
1881
                if (c_prev[0] != NULL) {
183,014✔
1882
                        switch (op_prev) {
79,454✔
1883
                        case SCMP_CMP_GT:
1884
                        case SCMP_CMP_GE:
1885
                                c_prev[0]->nxt_t = _db_node_get(c_iter[0]);
116✔
1886
                                c_prev[2]->nxt_t = _db_node_get(c_iter[0]);
116✔
1887
                                break;
116✔
1888
                        case SCMP_CMP_EQ:
1889
                        case SCMP_CMP_MASKED_EQ:
1890
                                c_prev[1]->nxt_t = _db_node_get(c_iter[0]);
72,256✔
1891
                                break;
72,256✔
1892
                        case SCMP_CMP_LE:
1893
                        case SCMP_CMP_LT:
1894
                                c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
104✔
1895
                                c_prev[2]->nxt_f = _db_node_get(c_iter[0]);
104✔
1896
                                break;
104✔
1897
                        case SCMP_CMP_NE:
1898
                                c_prev[0]->nxt_f = _db_node_get(c_iter[0]);
6,978✔
1899
                                c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
6,978✔
1900
                                break;
6,978✔
1901
                        default:
×
1902
                                /* we should never get here */
1903
                                goto gen_64_failure;
×
1904
                        }
1905
                } else
1906
                        s_new->chains = _db_node_get(c_iter[0]);
103,560✔
1907

1908
                /* update the node count */
1909
                switch (chain[iter].op) {
183,014✔
1910
                case SCMP_CMP_NE:
175,902✔
1911
                case SCMP_CMP_EQ:
1912
                case SCMP_CMP_MASKED_EQ:
1913
                        s_new->node_cnt += 2;
175,902✔
1914
                        break;
175,902✔
1915
                default:
7,112✔
1916
                        s_new->node_cnt += 3;
7,112✔
1917
                }
1918

1919
                /* keep pointers to this level */
1920
                c_prev[0] = c_iter[0];
1921
                c_prev[1] = c_iter[1];
1922
                c_prev[2] = c_iter[2];
1923
                op_prev = chain[iter].op;
1924
        }
1925
        if (c_iter[0] != NULL) {
230,638✔
1926
                /* set the actions on the last layer */
1927
                switch (op_prev) {
103,560✔
1928
                case SCMP_CMP_GT:
908✔
1929
                case SCMP_CMP_GE:
1930
                        c_iter[0]->act_t_flg = true;
908✔
1931
                        c_iter[0]->act_t = rule->action;
908✔
1932
                        c_iter[2]->act_t_flg = true;
908✔
1933
                        c_iter[2]->act_t = rule->action;
908✔
1934
                        break;
908✔
1935
                case SCMP_CMP_LE:
5,984✔
1936
                case SCMP_CMP_LT:
1937
                        c_iter[1]->act_f_flg = true;
5,984✔
1938
                        c_iter[1]->act_f = rule->action;
5,984✔
1939
                        c_iter[2]->act_f_flg = true;
5,984✔
1940
                        c_iter[2]->act_f = rule->action;
5,984✔
1941
                        break;
5,984✔
1942
                case SCMP_CMP_EQ:
89,416✔
1943
                case SCMP_CMP_MASKED_EQ:
1944
                        c_iter[1]->act_t_flg = true;
89,416✔
1945
                        c_iter[1]->act_t = rule->action;
89,416✔
1946
                        break;
89,416✔
1947
                case SCMP_CMP_NE:
7,252✔
1948
                        c_iter[0]->act_f_flg = true;
7,252✔
1949
                        c_iter[0]->act_f = rule->action;
7,252✔
1950
                        c_iter[1]->act_f_flg = true;
7,252✔
1951
                        c_iter[1]->act_f = rule->action;
7,252✔
1952
                        break;
7,252✔
1953
                default:
×
1954
                        /* we should never get here */
1955
                        goto gen_64_failure;
×
1956
                }
1957
        } else
1958
                s_new->action = rule->action;
127,078✔
1959

1960
        return s_new;
1961

1962
gen_64_failure:
×
1963
        /* free the new chain and its syscall struct */
1964
        _db_tree_put(&s_new->chains);
×
1965
        free(s_new);
×
1966
        return NULL;
×
1967
}
1968

1969
/**
1970
 * Generate a new filter rule for a 32 bit system
1971
 * @param arch the architecture definition
1972
 * @param rule the new filter rule
1973
 *
1974
 * This function generates a new syscall filter for a 32 bit system. Returns
1975
 * zero on success, negative values on failure.
1976
 *
1977
 */
1978
static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch,
95,522✔
1979
                                           const struct db_api_rule_list *rule)
1980
{
1981
        unsigned int iter;
95,522✔
1982
        struct db_sys_list *s_new;
95,522✔
1983
        const struct db_api_arg *chain = rule->args;
95,522✔
1984
        struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL;
95,522✔
1985
        bool tf_flag;
95,522✔
1986

1987
        s_new = zmalloc(sizeof(*s_new));
95,522✔
1988
        if (s_new == NULL)
95,522✔
1989
                return NULL;
1990
        s_new->num = rule->syscall;
95,522✔
1991
        s_new->valid = true;
95,522✔
1992
        /* run through the argument chain */
1993
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
668,654✔
1994
                if (chain[iter].valid == 0)
573,132✔
1995
                        continue;
522,712✔
1996

1997
                /* skip generating instructions which are no-ops */
1998
                if (!_db_arg_cmp_need_lo(&chain[iter]))
50,420✔
1999
                        continue;
×
2000

2001
                c_iter = zmalloc(sizeof(*c_iter));
50,420✔
2002
                if (c_iter == NULL)
50,420✔
2003
                        goto gen_32_failure;
×
2004
                c_iter->arg = chain[iter].arg;
50,420✔
2005
                c_iter->arg_h_flg = false;
50,420✔
2006
                c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg);
50,420✔
2007
                c_iter->op = chain[iter].op;
50,420✔
2008
                c_iter->op_orig = chain[iter].op;
50,420✔
2009
                /* implicitly strips off the upper 32 bit */
2010
                c_iter->mask = chain[iter].mask;
50,420✔
2011
                c_iter->datum = chain[iter].datum;
50,420✔
2012
                c_iter->datum_full = chain[iter].datum;
50,420✔
2013

2014
                /* link in the new node and update the chain */
2015
                if (c_prev != NULL) {
50,420✔
2016
                        if (tf_flag)
60✔
2017
                                c_prev->nxt_t = _db_node_get(c_iter);
36✔
2018
                        else
2019
                                c_prev->nxt_f = _db_node_get(c_iter);
24✔
2020
                } else
2021
                        s_new->chains = _db_node_get(c_iter);
50,360✔
2022
                s_new->node_cnt++;
50,420✔
2023

2024
                /* rewrite the op to reduce the op/datum combos */
2025
                switch (c_iter->op) {
50,420✔
2026
                case SCMP_CMP_NE:
12✔
2027
                        c_iter->op = SCMP_CMP_EQ;
12✔
2028
                        tf_flag = false;
12✔
2029
                        break;
12✔
2030
                case SCMP_CMP_LT:
12✔
2031
                        c_iter->op = SCMP_CMP_GE;
12✔
2032
                        tf_flag = false;
12✔
2033
                        break;
12✔
2034
                case SCMP_CMP_LE:
12✔
2035
                        c_iter->op = SCMP_CMP_GT;
12✔
2036
                        tf_flag = false;
12✔
2037
                        break;
12✔
2038
                default:
2039
                        tf_flag = true;
2040
                }
2041

2042
                /* fixup the mask/datum */
2043
                _db_node_mask_fixup(c_iter);
50,420✔
2044

2045
                c_prev = c_iter;
50,420✔
2046
        }
2047
        if (c_iter != NULL) {
95,522✔
2048
                /* set the leaf node */
2049
                if (tf_flag) {
50,360✔
2050
                        c_iter->act_t_flg = true;
50,348✔
2051
                        c_iter->act_t = rule->action;
50,348✔
2052
                } else {
2053
                        c_iter->act_f_flg = true;
12✔
2054
                        c_iter->act_f = rule->action;
12✔
2055
                }
2056
        } else
2057
                s_new->action = rule->action;
45,162✔
2058

2059
        return s_new;
2060

2061
gen_32_failure:
×
2062
        /* free the new chain and its syscall struct */
2063
        _db_tree_put(&s_new->chains);
×
2064
        free(s_new);
×
2065
        return NULL;
×
2066
}
2067

2068
/**
2069
 * Add a new rule to the seccomp filter DB
2070
 * @param db the seccomp filter db
2071
 * @param rule the filter rule
2072
 *
2073
 * This function adds a new syscall filter to the seccomp filter DB, adding to
2074
 * the existing filters for the syscall, unless no argument specific filters
2075
 * are present (filtering only on the syscall).  When adding new chains, the
2076
 * shortest chain, or most inclusive filter match, will be entered into the
2077
 * filter DB. Returns zero on success, negative values on failure.
2078
 *
2079
 * It is important to note that in the case of failure the db may be corrupted,
2080
 * the caller must use the transaction mechanism if the db integrity is
2081
 * important.
2082
 *
2083
 */
2084
int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule)
326,160✔
2085
{
2086
        int rc = -ENOMEM;
326,160✔
2087
        struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
326,160✔
2088
        struct db_iter_state state;
326,160✔
2089
        bool rm_flag = false;
326,160✔
2090

2091
        assert(db != NULL);
326,160✔
2092

2093
        /* do all our possible memory allocation up front so we don't have to
2094
         * worry about failure once we get to the point where we start updating
2095
         * the filter db */
2096
        if (db->arch->size == ARCH_SIZE_64)
326,160✔
2097
                s_new = _db_rule_gen_64(db->arch, rule);
230,638✔
2098
        else if (db->arch->size == ARCH_SIZE_32)
95,522✔
2099
                s_new = _db_rule_gen_32(db->arch, rule);
95,522✔
2100
        else
2101
                return -EFAULT;
2102
        if (s_new == NULL)
326,160✔
2103
                return -ENOMEM;
2104

2105
        /* find a matching syscall/chain or insert a new one */
2106
        s_iter = db->syscalls;
326,160✔
2107
        while (s_iter != NULL && s_iter->num < rule->syscall) {
1,747,720✔
2108
                s_prev = s_iter;
1,421,560✔
2109
                s_iter = s_iter->next;
1,421,560✔
2110
        }
2111
        s_new->priority = _DB_PRI_MASK_CHAIN - s_new->node_cnt;
326,160✔
2112
add_reset:
328,448✔
2113
        if (s_iter == NULL || s_iter->num != rule->syscall) {
328,448✔
2114
                /* new syscall, add before s_iter */
2115
                if (s_prev != NULL) {
247,170✔
2116
                        s_new->next = s_prev->next;
201,536✔
2117
                        s_prev->next = s_new;
201,536✔
2118
                } else {
2119
                        s_new->next = db->syscalls;
45,634✔
2120
                        db->syscalls = s_new;
45,634✔
2121
                }
2122
                db->syscall_cnt++;
247,170✔
2123
                return 0;
247,170✔
2124
        } else if (s_iter->chains == NULL) {
81,278✔
2125
                if (rm_flag || !s_iter->valid) {
3,108✔
2126
                        /* we are here because our previous pass cleared the
2127
                         * entire syscall chain when searching for a subtree
2128
                         * match or the existing syscall entry is a phantom,
2129
                         * so either way add the new chain */
2130
                        s_iter->chains = s_new->chains;
2,328✔
2131
                        s_iter->action = s_new->action;
2,328✔
2132
                        s_iter->node_cnt = s_new->node_cnt;
2,328✔
2133
                        if (s_iter->valid)
2,328✔
2134
                                s_iter->priority = s_new->priority;
2,288✔
2135
                        s_iter->valid = true;
2,328✔
2136
                        free(s_new);
2,328✔
2137
                        rc = 0;
2,328✔
2138
                        goto add_priority_update;
2,328✔
2139
                } else {
2140
                        /* syscall exists without any chains - existing filter
2141
                         * is at least as large as the new entry so cleanup and
2142
                         * exit */
2143
                        _db_tree_put(&s_new->chains);
780✔
2144
                        free(s_new);
780✔
2145
                        goto add_free_ok;
780✔
2146
                }
2147
        } else if (s_iter->chains != NULL && s_new->chains == NULL) {
78,170✔
2148
                /* syscall exists with chains but the new filter has no chains
2149
                 * so we need to clear the existing chains and exit */
2150
                _db_tree_put(&s_iter->chains);
132✔
2151
                s_iter->chains = NULL;
132✔
2152
                s_iter->node_cnt = 0;
132✔
2153
                s_iter->action = rule->action;
132✔
2154

2155
                /* cleanup the new tree and return */
2156
                _db_tree_put(&s_new->chains);
132✔
2157
                free(s_new);
132✔
2158
                goto add_free_ok;
132✔
2159
        }
2160

2161
        /* prune any sub-trees that are no longer required */
2162
        memset(&state, 0, sizeof(state));
78,038✔
2163
        state.sx = s_iter;
78,038✔
2164
        state.action = rule->action;
78,038✔
2165
        rc = _db_tree_prune(&s_iter->chains, s_new->chains, &state);
78,038✔
2166
        if (rc > 0) {
78,038✔
2167
                /* we pruned at least some of the existing tree */
2168
                rm_flag = true;
2,288✔
2169
                s_iter->node_cnt -= rc;
2,288✔
2170
                if (s_iter->chains == NULL)
2,288✔
2171
                        /* we pruned the entire tree */
2172
                        goto add_reset;
2,288✔
2173
        } else if ((state.flags & _DB_IST_M_REDUNDANT) == _DB_IST_M_REDUNDANT) {
75,750✔
2174
                /* the existing tree is "shorter", drop the new one */
2175
                _db_tree_put(&s_new->chains);
4,644✔
2176
                free(s_new);
4,644✔
2177
                goto add_free_ok;
4,644✔
2178
        }
2179

2180
        /* add the new rule to the existing filter and cleanup */
2181
        memset(&state, 0, sizeof(state));
71,106✔
2182
        state.sx = s_iter;
71,106✔
2183
        rc = _db_tree_add(&s_iter->chains, s_new->chains, &state);
71,106✔
2184
        if (rc < 0)
71,106✔
2185
                goto add_failure;
34✔
2186
        s_iter->node_cnt += s_new->node_cnt;
71,072✔
2187
        s_iter->node_cnt -= _db_tree_put(&s_new->chains);
71,072✔
2188
        free(s_new);
71,072✔
2189

2190
add_free_ok:
2191
        rc = 0;
2192
add_priority_update:
78,956✔
2193
        /* update the priority */
2194
        if (s_iter != NULL) {
78,956✔
2195
                s_iter->priority &= (~_DB_PRI_MASK_CHAIN);
78,956✔
2196
                s_iter->priority |= (_DB_PRI_MASK_CHAIN - s_iter->node_cnt);
78,956✔
2197
        }
2198
        return rc;
78,956✔
2199

2200
add_failure:
34✔
2201
        /* NOTE: another reminder that we don't do any db error recovery here,
2202
         * use the transaction mechanism as previously mentioned */
2203
        _db_tree_put(&s_new->chains);
34✔
2204
        free(s_new);
34✔
2205
        return rc;
34✔
2206
}
2207

2208
/**
2209
 * Set the priority of a given syscall
2210
 * @param col the filter collection
2211
 * @param syscall the syscall number
2212
 * @param priority priority value, higher value == higher priority
2213
 *
2214
 * This function sets the priority of the given syscall; this value is used
2215
 * when generating the seccomp filter code such that higher priority syscalls
2216
 * will incur less filter code overhead than the lower priority syscalls in the
2217
 * filter.  Returns zero on success, negative values on failure.
2218
 *
2219
 */
2220
int db_col_syscall_priority(struct db_filter_col *col,
68✔
2221
                            int syscall, uint8_t priority)
2222
{
2223
        int rc = 0, rc_tmp;
68✔
2224
        unsigned int iter;
68✔
2225
        int sc_tmp;
68✔
2226
        struct db_filter *filter;
68✔
2227

2228
        for (iter = 0; iter < col->filter_cnt; iter++) {
150✔
2229
                filter = col->filters[iter];
82✔
2230
                sc_tmp = syscall;
82✔
2231

2232
                rc_tmp = arch_syscall_translate(filter->arch, &sc_tmp);
82✔
2233
                if (rc_tmp < 0)
82✔
2234
                        goto priority_failure;
×
2235

2236
                /* if this is a pseudo syscall then we need to rewrite the
2237
                 * syscall for some arch specific reason, don't forget the
2238
                 * special handling for syscall -1 */
2239
                if (sc_tmp < -1) {
82✔
2240
                        /* we set this as a strict op - we don't really care
2241
                         * since priorities are a "best effort" thing - as we
2242
                         * want to catch the -EDOM error and bail on this
2243
                         * architecture */
2244
                        rc_tmp = arch_syscall_rewrite(filter->arch, &sc_tmp);
7✔
2245
                        if (rc_tmp == -EDOM)
7✔
2246
                                continue;
3✔
2247
                        if (rc_tmp < 0)
4✔
2248
                                goto priority_failure;
×
2249
                }
2250

2251
                rc_tmp = _db_syscall_priority(filter, sc_tmp, priority);
79✔
2252

2253
priority_failure:
79✔
2254
                if (rc == 0 && rc_tmp < 0)
79✔
2255
                        rc = rc_tmp;
×
2256
        }
2257

2258
        if (rc == 0)
68✔
2259
                db_col_precompute_reset(col);
136✔
2260

2261
        return rc;
68✔
2262
}
2263

2264
/**
2265
 * Add a new rule to a single filter
2266
 * @param filter the filter
2267
 * @param rule the filter rule
2268
 *
2269
 * This is a helper function for db_col_rule_add() and similar functions, it
2270
 * isn't generally useful.  Returns zero on success, negative values on error.
2271
 *
2272
 */
2273
static int _db_col_rule_add(struct db_filter *filter,
302,059✔
2274
                            struct db_api_rule_list *rule)
2275
{
2276
        int rc;
302,059✔
2277
        struct db_api_rule_list *iter;
302,059✔
2278

2279
        /* add the rule to the filter */
2280
        rc = arch_filter_rule_add(filter, rule);
302,059✔
2281
        if (rc != 0)
302,059✔
2282
                return rc;
2283

2284
        /* insert the chain to the end of the rule list */
2285
        iter = rule;
2286
        while (iter->next)
301,660✔
2287
                iter = iter->next;
2288
        if (filter->rules != NULL) {
301,660✔
2289
                rule->prev = filter->rules->prev;
263,546✔
2290
                iter->next = filter->rules;
263,546✔
2291
                filter->rules->prev->next = rule;
263,546✔
2292
                filter->rules->prev = iter;
263,546✔
2293
        } else {
2294
                rule->prev = iter;
38,114✔
2295
                iter->next = rule;
38,114✔
2296
                filter->rules = rule;
38,114✔
2297
        }
2298

2299
        return 0;
2300
}
2301

2302
/**
2303
 * Add a new rule to the current filter
2304
 * @param col the filter collection
2305
 * @param strict the strict flag
2306
 * @param action the filter action
2307
 * @param syscall the syscall number
2308
 * @param arg_cnt the number of argument filters in the argument filter chain
2309
 * @param arg_array the argument filter chain, (uint, enum scmp_compare, ulong)
2310
 *
2311
 * This function adds a new argument/comparison/value to the seccomp filter for
2312
 * a syscall; multiple arguments can be specified and they will be chained
2313
 * together (essentially AND'd together) in the filter.  When the strict flag
2314
 * is true the function will fail if the exact rule can not be added to the
2315
 * filter, if the strict flag is false the function will not fail if the
2316
 * function needs to adjust the rule due to architecture specifics.  Returns
2317
 * zero on success, negative values on failure.
2318
 *
2319
 */
2320
int db_col_rule_add(struct db_filter_col *col,
53,701✔
2321
                    bool strict, uint32_t action, int syscall,
2322
                    unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
2323
{
2324
        int rc = 0, rc_tmp;
53,701✔
2325
        unsigned int iter;
53,701✔
2326
        unsigned int arg_num;
53,701✔
2327
        size_t chain_size;
53,701✔
2328
        struct db_api_arg *chain = NULL;
53,701✔
2329
        struct scmp_arg_cmp arg_data;
53,701✔
2330
        struct db_api_rule_list *rule;
53,701✔
2331
        struct db_filter *db;
53,701✔
2332

2333
        /* collect the arguments for the filter rule */
2334
        chain_size = sizeof(*chain) * ARG_COUNT_MAX;
53,701✔
2335
        chain = zmalloc(chain_size);
53,701✔
2336
        if (chain == NULL)
53,701✔
2337
                return -ENOMEM;
2338
        for (iter = 0; iter < arg_cnt; iter++) {
125,763✔
2339
                arg_data = arg_array[iter];
72,064✔
2340
                arg_num = arg_data.arg;
72,064✔
2341
                if (arg_num < ARG_COUNT_MAX && chain[arg_num].valid == 0) {
72,064✔
2342
                        chain[arg_num].valid = 1;
72,064✔
2343
                        chain[arg_num].arg = arg_num;
72,064✔
2344
                        chain[arg_num].op = arg_data.op;
72,064✔
2345
                        /* TODO: we should check datum/mask size against the
2346
                         *         arch definition, e.g. 64 bit datum on x86 */
2347
                        switch (chain[arg_num].op) {
72,064✔
2348
                        case SCMP_CMP_NE:
66,389✔
2349
                        case SCMP_CMP_LT:
2350
                        case SCMP_CMP_LE:
2351
                        case SCMP_CMP_EQ:
2352
                        case SCMP_CMP_GE:
2353
                        case SCMP_CMP_GT:
2354
                                chain[arg_num].mask = DATUM_MAX;
66,389✔
2355
                                chain[arg_num].datum = arg_data.datum_a;
66,389✔
2356
                                break;
66,389✔
2357
                        case SCMP_CMP_MASKED_EQ:
5,673✔
2358
                                chain[arg_num].mask = arg_data.datum_a;
5,673✔
2359
                                chain[arg_num].datum = arg_data.datum_b;
5,673✔
2360
                                break;
5,673✔
2361
                        default:
2✔
2362
                                rc = -EINVAL;
2✔
2363
                                goto add_return;
2✔
2364
                        }
2365
                } else {
2366
                        rc = -EINVAL;
×
2367
                        goto add_return;
×
2368
                }
2369
        }
2370

2371
        /* create a checkpoint */
2372
        rc = db_col_transaction_start(col, false);
53,699✔
2373
        if (rc != 0)
53,699✔
2374
                goto add_return;
×
2375

2376
        /* add the rule to the different filters in the collection */
2377
        for (iter = 0; iter < col->filter_cnt; iter++) {
186,121✔
2378
                db = col->filters[iter];
132,422✔
2379

2380
                /* create the rule */
2381
                rule = _db_rule_new(strict, action, syscall, chain);
132,422✔
2382
                if (rule == NULL) {
132,422✔
2383
                        rc_tmp = -ENOMEM;
×
2384
                        goto add_arch_fail;
×
2385
                }
2386

2387
                /* add the rule */
2388
                rc_tmp = _db_col_rule_add(db, rule);
132,422✔
2389
                if (rc_tmp != 0)
132,422✔
2390
                        free(rule);
399✔
2391

2392
add_arch_fail:
132,023✔
2393
                if (rc_tmp != 0 && rc == 0)
132,422✔
2394
                        rc = rc_tmp;
399✔
2395
        }
2396

2397
        /* commit the transaction or abort */
2398
        if (rc == 0)
53,699✔
2399
                db_col_transaction_commit(col, false);
53,300✔
2400
        else
2401
                db_col_transaction_abort(col, false);
399✔
2402

2403
add_return:
53,699✔
2404
        /* update the misc state */
2405
        if (rc == 0) {
53,701✔
2406
                if (action == SCMP_ACT_NOTIFY)
53,300✔
2407
                        col->notify_used = true;
×
2408
                db_col_precompute_reset(col);
107,001✔
2409
        }
2410
        if (chain != NULL)
53,701✔
2411
                free(chain);
53,701✔
2412
        return rc;
53,701✔
2413
}
2414

2415
/**
2416
 * Start a new seccomp filter transaction
2417
 * @param col the filter collection
2418
 * @param user true if initiated by a user
2419
 *
2420
 * This function starts a new seccomp filter transaction for the given filter
2421
 * collection.  Returns zero on success, negative values on failure.
2422
 *
2423
 */
2424
int db_col_transaction_start(struct db_filter_col *col, bool user)
57,065✔
2425
{
2426
        int rc;
57,065✔
2427
        unsigned int iter;
57,065✔
2428
        struct db_filter_snap *snap;
57,065✔
2429
        struct db_filter *filter_o, *filter_s;
57,065✔
2430
        struct db_api_rule_list *rule_o, *rule_s = NULL;
57,065✔
2431

2432
        /* check to see if a shadow snapshot exists */
2433
        if (col->snapshots && col->snapshots->shadow) {
57,065✔
2434
                /* we have a shadow!  this will be easy */
2435

2436
                /* NOTE: we don't bother to do any verification of the shadow
2437
                 *       because we start a new transaction every time we add
2438
                 *       a new rule to the filter(s); if this ever changes we
2439
                 *       will need to add a mechanism to verify that the shadow
2440
                 *       transaction is current/correct */
2441

2442
                col->snapshots->shadow = false;
46,443✔
2443
                col->snapshots->user = user;
46,443✔
2444
                return 0;
46,443✔
2445
        }
2446

2447
        /* allocate the snapshot */
2448
        snap = zmalloc(sizeof(*snap));
10,622✔
2449
        if (snap == NULL)
10,622✔
2450
                return -ENOMEM;
2451
        snap->filters = zmalloc(sizeof(struct db_filter *) * col->filter_cnt);
10,622✔
2452
        if (snap->filters == NULL) {
10,622✔
2453
                free(snap);
×
2454
                return -ENOMEM;
×
2455
        }
2456
        snap->filter_cnt = col->filter_cnt;
10,622✔
2457
        for (iter = 0; iter < snap->filter_cnt; iter++)
31,443✔
2458
                snap->filters[iter] = NULL;
20,821✔
2459
        snap->next = NULL;
10,622✔
2460

2461
        /* create a snapshot of the current filter state */
2462
        for (iter = 0; iter < col->filter_cnt; iter++) {
31,443✔
2463
                /* allocate a new filter */
2464
                filter_o = col->filters[iter];
20,821✔
2465
                filter_s = _db_init(filter_o->arch);
20,821✔
2466
                if (filter_s == NULL)
20,821✔
2467
                        goto trans_start_failure;
×
2468
                snap->filters[iter] = filter_s;
20,821✔
2469

2470
                /* create a filter snapshot from existing rules */
2471
                rule_o = filter_o->rules;
20,821✔
2472
                if (rule_o == NULL)
20,821✔
2473
                        continue;
17,379✔
2474
                do {
37,414✔
2475
                        /* duplicate the rule */
2476
                        rule_s = db_rule_dup(rule_o);
37,414✔
2477
                        if (rule_s == NULL)
37,414✔
2478
                                goto trans_start_failure;
×
2479

2480
                        /* add the rule */
2481
                        rc = _db_col_rule_add(filter_s, rule_s);
37,414✔
2482
                        if (rc != 0)
37,414✔
2483
                                goto trans_start_failure;
×
2484
                        rule_s = NULL;
37,414✔
2485

2486
                        /* next rule */
2487
                        rule_o = rule_o->next;
37,414✔
2488
                } while (rule_o != filter_o->rules);
37,414✔
2489
        }
2490

2491
        snap->user = user;
10,622✔
2492

2493
        /* add the snapshot to the list */
2494
        snap->next = col->snapshots;
10,622✔
2495
        col->snapshots = snap;
10,622✔
2496

2497
        return 0;
10,622✔
2498

2499
trans_start_failure:
×
2500
        if (rule_s != NULL)
×
2501
                free(rule_s);
×
2502
        _db_snap_release(snap);
×
2503
        return -ENOMEM;
×
2504
}
2505

2506
/**
2507
 * Abort the top most seccomp filter transaction
2508
 * @param col the filter collection
2509
 * @param user true if initiated by a user
2510
 *
2511
 * This function aborts the most recent seccomp filter transaction.
2512
 *
2513
 */
2514
void db_col_transaction_abort(struct db_filter_col *col, bool user)
1,274✔
2515
{
2516
        int iter;
1,274✔
2517
        unsigned int filter_cnt;
1,274✔
2518
        struct db_filter **filters;
1,274✔
2519
        struct db_filter_snap *snap;
1,274✔
2520

2521
        snap = col->snapshots;
1,274✔
2522
        if (snap == NULL)
1,274✔
2523
                return;
2524

2525
        /* replace the current filter with the last snapshot, skipping shadow
2526
         * snapshots are they are duplicates of the current snapshot */
2527
        if (snap->shadow) {
1,274✔
2528
                struct db_filter_snap *tmp = snap;
100✔
2529
                snap = snap->next;
100✔
2530
                _db_snap_release(tmp);
100✔
2531
        }
2532

2533
        if (snap->user != user)
1,274✔
2534
                return;
2535

2536
        col->snapshots = snap->next;
1,274✔
2537

2538
        filter_cnt = col->filter_cnt;
1,274✔
2539
        filters = col->filters;
1,274✔
2540
        col->filter_cnt = snap->filter_cnt;
1,274✔
2541
        col->filters = snap->filters;
1,274✔
2542
        free(snap);
1,274✔
2543

2544
        /* free the filter we swapped out */
2545
        for (iter = 0; iter < filter_cnt; iter++)
2,548✔
2546
                _db_release(filters[iter]);
2,548✔
2547
        free(filters);
1,274✔
2548

2549
        /* free any precompute */
2550
        db_col_precompute_reset(col);
2,548✔
2551
}
2552

2553
/**
2554
 * Commit the top most seccomp filter transaction
2555
 * @param col the filter collection
2556
 * @param user true if initiated by a user
2557
 *
2558
 * This function commits the most recent seccomp filter transaction and
2559
 * attempts to create a shadow transaction that is a duplicate of the current
2560
 * filter to speed up future transactions.
2561
 *
2562
 */
2563
void db_col_transaction_commit(struct db_filter_col *col, bool user)
55,791✔
2564
{
2565
        int rc;
55,791✔
2566
        unsigned int iter;
55,791✔
2567
        struct db_filter_snap *snap;
55,791✔
2568
        struct db_filter *filter_o, *filter_s;
55,791✔
2569
        struct db_api_rule_list *rule_o, *rule_s;
55,791✔
2570

2571
        snap = col->snapshots;
55,791✔
2572
        if (snap == NULL)
55,791✔
2573
                return;
2574

2575
        /* check for a shadow set by a higher transaction commit */
2576
        if (snap->shadow) {
55,791✔
2577
                /* leave the shadow intact, but drop the next snapshot */
2578
                if (snap->next) {
2,462✔
2579
                        struct db_filter_snap *tmp = snap->next;
2,462✔
2580
                        snap->next = tmp->next;
2,462✔
2581
                        _db_snap_release(tmp);
2,462✔
2582
                }
2583
                return;
2,462✔
2584
        }
2585

2586
        if (snap->user != user)
53,329✔
2587
                return;
2588

2589
        /* adjust the number of filters if needed */
2590
        if (col->filter_cnt > snap->filter_cnt) {
53,329✔
2591
                unsigned int tmp_i;
4✔
2592
                struct db_filter **tmp_f;
4✔
2593

2594
                /* add filters */
2595
                tmp_f = realloc(snap->filters,
4✔
2596
                                sizeof(struct db_filter *) * col->filter_cnt);
4✔
2597
                if (tmp_f == NULL)
4✔
2598
                        goto shadow_err;
×
2599
                snap->filters = tmp_f;
4✔
2600
                do {
4✔
2601
                        tmp_i = snap->filter_cnt;
4✔
2602
                        snap->filters[tmp_i] =
8✔
2603
                                _db_init(col->filters[tmp_i]->arch);
4✔
2604
                        if (snap->filters[tmp_i] == NULL)
4✔
2605
                                goto shadow_err;
×
2606
                        snap->filter_cnt++;
4✔
2607
                } while (snap->filter_cnt < col->filter_cnt);
4✔
2608
        } else if (col->filter_cnt < snap->filter_cnt) {
53,325✔
2609
                /* remove filters */
2610

2611
                /* NOTE: while we release the filters we no longer need, we
2612
                 *       don't bother to resize the filter array, we just
2613
                 *       adjust the filter counter, this *should* be harmless
2614
                 *       at the cost of a not reaping all the memory possible */
2615

2616
                do {
4✔
2617
                        _db_release(snap->filters[--snap->filter_cnt]);
4✔
2618
                } while (col->filter_cnt < snap->filter_cnt);
4✔
2619
        }
2620

2621
        /* loop through each filter and update the rules on the snapshot */
2622
        for (iter = 0; iter < col->filter_cnt; iter++) {
185,381✔
2623
                filter_o = col->filters[iter];
132,052✔
2624
                filter_s = snap->filters[iter];
132,052✔
2625

2626
                /* skip ahead to the new rule(s) */
2627
                rule_o = filter_o->rules;
132,052✔
2628
                rule_s = filter_s->rules;
132,052✔
2629
                if (rule_o == NULL)
132,052✔
2630
                        /* nothing to shadow */
2631
                        continue;
×
2632
                if (rule_s != NULL) {
132,052✔
2633
                        do {
1,122,190✔
2634
                                rule_o = rule_o->next;
1,122,190✔
2635
                                rule_s = rule_s->next;
1,122,190✔
2636
                        } while (rule_s != filter_s->rules);
1,122,190✔
2637

2638
                        /* did we actually add any rules? */
2639
                        if (rule_o == filter_o->rules)
114,716✔
2640
                                /* no, we are done in this case */
2641
                                continue;
4✔
2642
                }
2643

2644
                /* update the old snapshot to make it a shadow */
2645
                do {
132,223✔
2646
                        /* duplicate the rule */
2647
                        rule_s = db_rule_dup(rule_o);
132,223✔
2648
                        if (rule_s == NULL)
132,223✔
2649
                                goto shadow_err;
×
2650

2651
                        /* add the rule */
2652
                        rc = _db_col_rule_add(filter_s, rule_s);
132,223✔
2653
                        if (rc != 0) {
132,223✔
2654
                                free(rule_s);
×
2655
                                goto shadow_err;
×
2656
                        }
2657

2658
                        /* next rule */
2659
                        rule_o = rule_o->next;
132,223✔
2660
                } while (rule_o != filter_o->rules);
132,223✔
2661
        }
2662

2663
        /* success, mark the snapshot as a shadow and return */
2664
        snap->shadow = true;
53,329✔
2665
        return;
53,329✔
2666

2667
shadow_err:
×
2668
        /* we failed making a shadow, cleanup and return */
2669
        col->snapshots = snap->next;
×
2670
        _db_snap_release(snap);
×
2671
        return;
×
2672
}
2673

2674
/**
2675
 * Precompute the seccomp filters
2676
 * @param col the filter collection
2677
 *
2678
 * This function precomputes the seccomp filters before they are needed,
2679
 * returns zero on success, negative values on error.
2680
 *
2681
 */
2682
int db_col_precompute(struct db_filter_col *col)
7,852✔
2683
{
2684
        if (!col->prgm_bpf)
7,852✔
2685
                return gen_bpf_generate(col, &col->prgm_bpf);
7,838✔
2686
        return 0;
2687
}
2688

2689
/**
2690
 * Free any precomputed filter programs
2691
 * @param col the filter collection
2692
 *
2693
 * This function releases any precomputed filter programs.
2694
 */
2695
void db_col_precompute_reset(struct db_filter_col *col)
88,665✔
2696
{
2697
        if (!col->prgm_bpf)
88,665✔
2698
                return;
2699

2700
        gen_bpf_release(col->prgm_bpf);
309✔
2701
        col->prgm_bpf = NULL;
7,838✔
2702
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc