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

drakenclimber / libseccomp / 6279355589

21 Sep 2023 04:53PM UTC coverage: 89.542% (-0.3%) from 89.89%
6279355589

push

github

pcmoore
api: add transaction support to the libseccomp API

While libseccomp has internally has transaction support for some time
now, it hasn't been accessible to callers through the libseccomp API.
This patch adds a transaction API as well as supporting documentation
and a new unit regression test.

  int seccomp_transaction_start(const scmp_filter_ctx ctx)
  int seccomp_transaction_commit(const scmp_filter_ctx ctx)
  void seccomp_transaction_reject(const scmp_filter_ctx ctx)

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

30 of 30 new or added lines in 2 files covered. (100.0%)

2697 of 3012 relevant lines covered (89.54%)

299730.21 hits per line

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

90.55
/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,139,490✔
272
{
273
        unsigned int cnt;
1,139,490✔
274

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

279
        return cnt;
1,139,490✔
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,645,280✔
292
{
293
        unsigned int cnt = 0;
1,645,280✔
294
        struct db_arg_chain_tree *n = *node;
1,645,280✔
295
        struct db_arg_chain_tree *lvl_p, *lvl_n, *nxt_t, *nxt_f;
1,645,280✔
296

297
        if (n == NULL)
1,645,280✔
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)
77,304✔
827
{
828
        struct db_sys_list *s_iter;
77,304✔
829
        struct db_api_rule_list *r_iter;
77,304✔
830

831
        if (db == NULL)
77,304✔
832
                return;
833

834
        /* free any filters */
835
        if (db->syscalls != NULL) {
77,304✔
836
                s_iter = db->syscalls;
837
                while (s_iter != NULL) {
250,491✔
838
                        db->syscalls = s_iter->next;
215,573✔
839
                        _db_tree_put(&s_iter->chains);
215,573✔
840
                        free(s_iter);
215,573✔
841
                        s_iter = db->syscalls;
215,573✔
842
                }
843
                db->syscalls = NULL;
34,918✔
844
        }
845
        db->syscall_cnt = 0;
77,304✔
846

847
        /* free any rules */
848
        if (db->rules != NULL) {
77,304✔
849
                /* split the loop first then loop and free */
850
                db->rules->prev->next = NULL;
35,659✔
851
                r_iter = db->rules;
35,659✔
852
                while (r_iter != NULL) {
305,712✔
853
                        db->rules = r_iter->next;
270,053✔
854
                        free(r_iter);
270,053✔
855
                        r_iter = db->rules;
270,053✔
856
                }
857
                db->rules = NULL;
35,659✔
858
        }
859
}
860

861
/**
862
 * Intitalize 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)
38,652✔
870
{
871
        struct db_filter *db;
38,652✔
872

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

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

881
        return db;
38,652✔
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)
38,652✔
893
{
894
        if (db == NULL)
38,652✔
895
                return;
896

897
        /* free and reset the DB */
898
        _db_reset(db);
16,310✔
899
        free(db);
33,639✔
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)
7,134✔
911
{
912
        unsigned int iter;
7,134✔
913

914
        if (snap == NULL)
7,134✔
915
                return;
916

917
        if (snap->filter_cnt > 0) {
7,134✔
918
                for (iter = 0; iter < snap->filter_cnt; iter++) {
24,463✔
919
                        if (snap->filters[iter])
17,329✔
920
                                _db_release(snap->filters[iter]);
34,658✔
921
                }
922
                free(snap->filters);
7,134✔
923
        }
924
        free(snap);
7,134✔
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,029✔
995
                                             uint32_t action, int syscall,
996
                                             struct db_api_arg *chain)
997
{
998
        struct db_api_rule_list *rule;
132,029✔
999

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

1008
        return rule;
132,029✔
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)
434,071✔
1021
{
1022
        struct db_api_rule_list *dest;
434,071✔
1023

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

1031
        return dest;
434,071✔
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,201✔
1046
{
1047
        unsigned int iter;
8,201✔
1048
        struct db_filter *db;
8,201✔
1049
        struct db_filter_snap *snap;
8,201✔
1050

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

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

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

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

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

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

1093
        /* reset the transactions */
1094
        while (col->snapshots) {
8,566✔
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,402✔
1105

1106
        return 0;
1107
}
1108

1109
/**
1110
 * Intitalize 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,836✔
1118
{
1119
        struct db_filter_col *col;
7,836✔
1120

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

1125
        /* reset the DB to a known state */
1126
        if (db_col_reset(col, def_action) < 0) {
7,836✔
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,836✔
1143
{
1144
        unsigned int iter;
7,836✔
1145
        struct db_filter_snap *snap;
7,836✔
1146

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

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

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

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

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

1171
        /* free the collection */
1172
        free(col);
7,836✔
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)
63,735✔
1184
{
1185
        if (col != NULL && col->state == _DB_STA_VALID && col->filter_cnt > 0)
63,735✔
1186
                return 0;
63,727✔
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,519✔
1199
{
1200
        if (col != NULL) {
61,519✔
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,317✔
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,519✔
1212
                return 0;
61,515✔
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)
37,990✔
1280
{
1281
        unsigned int iter;
37,990✔
1282

1283
        for (iter = 0; iter < col->filter_cnt; iter++)
153,983✔
1284
                if (col->filters[iter]->arch->token == arch_token)
121,288✔
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,106✔
1471
{
1472
        int rc;
12,106✔
1473
        struct db_filter *db;
12,106✔
1474

1475
        db = _db_init(arch);
12,106✔
1476
        if (db == NULL)
12,106✔
1477
                return -ENOMEM;
1478
        rc = db_col_db_add(col, db);
12,106✔
1479
        if (rc < 0)
12,106✔
1480
                _db_release(db);
×
1481
        else
1482
                db_col_precompute_reset(col);
24,212✔
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,307✔
1498
{
1499
        struct db_filter **dbs;
20,307✔
1500

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

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

1507
        dbs = realloc(col->filters,
20,307✔
1508
                      sizeof(struct db_filter *) * (col->filter_cnt + 1));
20,307✔
1509
        if (dbs == NULL)
20,307✔
1510
                return -ENOMEM;
1511
        col->filters = dbs;
20,307✔
1512
        col->filter_cnt++;
20,307✔
1513
        col->filters[col->filter_cnt - 1] = db;
20,307✔
1514
        if (col->endian == 0)
20,307✔
1515
                col->endian = db->arch->endian;
10,112✔
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,013✔
1530
{
1531
        unsigned int iter;
5,013✔
1532
        unsigned int found;
5,013✔
1533
        struct db_filter **dbs;
5,013✔
1534

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

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

1548
        if (col->filter_cnt > 0) {
5,013✔
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,102✔
1552
                              sizeof(struct db_filter *) * col->filter_cnt);
1553
                if (dbs != NULL)
3,102✔
1554
                        col->filters = dbs;
3,102✔
1555
        } else {
1556
                /* this was the last filter so free all the associated memory
1557
                 * and reset the endian token */
1558
                free(col->filters);
1,911✔
1559
                col->filters = NULL;
1,911✔
1560
                col->endian = 0;
1,911✔
1561
        }
1562

1563
        db_col_precompute_reset(col);
10,026✔
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,
199,004✔
1622
                                           const struct db_api_rule_list *rule)
1623
{
1624
        unsigned int iter;
199,004✔
1625
        struct db_sys_list *s_new;
199,004✔
1626
        const struct db_api_arg *chain = rule->args;
199,004✔
1627
        struct db_arg_chain_tree *c_iter[3] = { NULL, NULL, NULL };
199,004✔
1628
        struct db_arg_chain_tree *c_prev[3] = { NULL, NULL, NULL };
199,004✔
1629
        enum scmp_compare op_prev = _SCMP_CMP_MIN;
199,004✔
1630
        unsigned int arg;
199,004✔
1631
        scmp_datum_t mask;
199,004✔
1632
        scmp_datum_t datum;
199,004✔
1633

1634
        s_new = zmalloc(sizeof(*s_new));
199,004✔
1635
        if (s_new == NULL)
199,004✔
1636
                return NULL;
1637
        s_new->num = rule->syscall;
199,004✔
1638
        s_new->valid = true;
199,004✔
1639
        /* run through the argument chain */
1640
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
1,393,030✔
1641
                if (chain[iter].valid == 0)
1,194,020✔
1642
                        continue;
1,010,940✔
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) {
199,004✔
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;
95,444✔
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,510✔
1979
                                           const struct db_api_rule_list *rule)
1980
{
1981
        unsigned int iter;
95,510✔
1982
        struct db_sys_list *s_new;
95,510✔
1983
        const struct db_api_arg *chain = rule->args;
95,510✔
1984
        struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL;
95,510✔
1985
        bool tf_flag;
95,510✔
1986

1987
        s_new = zmalloc(sizeof(*s_new));
95,510✔
1988
        if (s_new == NULL)
95,510✔
1989
                return NULL;
1990
        s_new->num = rule->syscall;
95,510✔
1991
        s_new->valid = true;
95,510✔
1992
        /* run through the argument chain */
1993
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
668,570✔
1994
                if (chain[iter].valid == 0)
573,060✔
1995
                        continue;
522,640✔
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,510✔
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,150✔
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)
294,514✔
2085
{
2086
        int rc = -ENOMEM;
294,514✔
2087
        struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
294,514✔
2088
        struct db_iter_state state;
294,514✔
2089
        bool rm_flag = false;
294,514✔
2090

2091
        assert(db != NULL);
294,514✔
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)
294,514✔
2097
                s_new = _db_rule_gen_64(db->arch, rule);
199,004✔
2098
        else if (db->arch->size == ARCH_SIZE_32)
95,510✔
2099
                s_new = _db_rule_gen_32(db->arch, rule);
95,510✔
2100
        else
2101
                return -EFAULT;
2102
        if (s_new == NULL)
294,514✔
2103
                return -ENOMEM;
2104

2105
        /* find a matching syscall/chain or insert a new one */
2106
        s_iter = db->syscalls;
294,514✔
2107
        while (s_iter != NULL && s_iter->num < rule->syscall) {
1,503,470✔
2108
                s_prev = s_iter;
1,208,960✔
2109
                s_iter = s_iter->next;
1,208,960✔
2110
        }
2111
        s_new->priority = _DB_PRI_MASK_CHAIN - s_new->node_cnt;
294,514✔
2112
add_reset:
296,802✔
2113
        if (s_iter == NULL || s_iter->num != rule->syscall) {
296,802✔
2114
                /* new syscall, add before s_iter */
2115
                if (s_prev != NULL) {
215,524✔
2116
                        s_new->next = s_prev->next;
172,384✔
2117
                        s_prev->next = s_new;
172,384✔
2118
                } else {
2119
                        s_new->next = db->syscalls;
43,140✔
2120
                        db->syscalls = s_new;
43,140✔
2121
                }
2122
                db->syscall_cnt++;
215,524✔
2123
                return 0;
215,524✔
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,
270,466✔
2274
                            struct db_api_rule_list *rule)
2275
{
2276
        int rc;
270,466✔
2277
        struct db_api_rule_list *iter;
270,466✔
2278

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

2284
        /* insert the chain to the end of the rule list */
2285
        iter = rule;
2286
        while (iter->next)
270,053✔
2287
                iter = iter->next;
2288
        if (filter->rules != NULL) {
270,053✔
2289
                rule->prev = filter->rules->prev;
234,394✔
2290
                iter->next = filter->rules;
234,394✔
2291
                filter->rules->prev->next = rule;
234,394✔
2292
                filter->rules->prev = iter;
234,394✔
2293
        } else {
2294
                rule->prev = iter;
35,659✔
2295
                iter->next = rule;
35,659✔
2296
                filter->rules = rule;
35,659✔
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,312✔
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,312✔
2325
        unsigned int iter;
53,312✔
2326
        unsigned int arg_num;
53,312✔
2327
        size_t chain_size;
53,312✔
2328
        struct db_api_arg *chain = NULL;
53,312✔
2329
        struct scmp_arg_cmp arg_data;
53,312✔
2330
        struct db_api_rule_list *rule;
53,312✔
2331
        struct db_filter *db;
53,312✔
2332

2333
        /* collect the arguments for the filter rule */
2334
        chain_size = sizeof(*chain) * ARG_COUNT_MAX;
53,312✔
2335
        chain = zmalloc(chain_size);
53,312✔
2336
        if (chain == NULL)
53,312✔
2337
                return -ENOMEM;
2338
        for (iter = 0; iter < arg_cnt; iter++) {
125,374✔
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,310✔
2373
        if (rc != 0)
53,310✔
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++) {
185,339✔
2378
                db = col->filters[iter];
132,029✔
2379

2380
                /* create the rule */
2381
                rule = _db_rule_new(strict, action, syscall, chain);
132,029✔
2382
                if (rule == NULL) {
132,029✔
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,029✔
2389
                if (rc_tmp != 0)
132,029✔
2390
                        free(rule);
413✔
2391

2392
add_arch_fail:
131,616✔
2393
                if (rc_tmp != 0 && rc == 0)
132,029✔
2394
                        rc = rc_tmp;
413✔
2395
        }
2396

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

2403
add_return:
53,310✔
2404
        /* update the misc state */
2405
        if (rc == 0) {
53,312✔
2406
                if (action == SCMP_ACT_NOTIFY)
52,897✔
2407
                        col->notify_used = true;
×
2408
                db_col_precompute_reset(col);
106,209✔
2409
        }
2410
        if (chain != NULL)
53,312✔
2411
                free(chain);
53,312✔
2412
        return rc;
53,312✔
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)
54,262✔
2425
{
2426
        int rc;
54,262✔
2427
        unsigned int iter;
54,262✔
2428
        struct db_filter_snap *snap;
54,262✔
2429
        struct db_filter *filter_o, *filter_s;
54,262✔
2430
        struct db_api_rule_list *rule_o, *rule_s = NULL;
54,262✔
2431

2432
        /* check to see if a shadow snapshot exists */
2433
        if (col->snapshots && col->snapshots->shadow) {
54,262✔
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,112✔
2443
                col->snapshots->user = user;
46,112✔
2444
                return 0;
46,112✔
2445
        }
2446

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

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

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

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

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

2491
        snap->user = user;
8,150✔
2492

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

2497
        return 0;
8,150✔
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)
651✔
2515
{
2516
        int iter;
651✔
2517
        unsigned int filter_cnt;
651✔
2518
        struct db_filter **filters;
651✔
2519
        struct db_filter_snap *snap;
651✔
2520

2521
        snap = col->snapshots;
651✔
2522
        if (snap == NULL)
651✔
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) {
651✔
2528
                struct db_filter_snap *tmp = snap;
28✔
2529
                snap = snap->next;
28✔
2530
                _db_snap_release(tmp);
28✔
2531
        }
2532

2533
        if (snap->user != user)
651✔
2534
                return;
2535

2536
        col->snapshots = snap->next;
651✔
2537

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

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

2549
        /* free any precompute */
2550
        db_col_precompute_reset(col);
1,302✔
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)
53,611✔
2564
{
2565
        int rc;
53,611✔
2566
        unsigned int iter;
53,611✔
2567
        struct db_filter_snap *snap;
53,611✔
2568
        struct db_filter *filter_o, *filter_s;
53,611✔
2569
        struct db_api_rule_list *rule_o, *rule_s;
53,611✔
2570

2571
        snap = col->snapshots;
53,611✔
2572
        if (snap == NULL)
53,611✔
2573
                return;
2574
        if (snap->user != user)
53,611✔
2575
                return;
2576

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

2588
        /* adjust the number of filters if needed */
2589
        if (col->filter_cnt > snap->filter_cnt) {
52,911✔
2590
                unsigned int tmp_i;
×
2591
                struct db_filter **tmp_f;
×
2592

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

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

2615
                do {
×
2616
                        _db_release(snap->filters[snap->filter_cnt--]);
×
2617
                } while (snap->filter_cnt > col->filter_cnt);
×
2618
        }
2619

2620
        /* loop through each filter and update the rules on the snapshot */
2621
        for (iter = 0; iter < col->filter_cnt; iter++) {
184,541✔
2622
                filter_o = col->filters[iter];
131,630✔
2623
                filter_s = snap->filters[iter];
131,630✔
2624

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

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

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

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

2657
                        /* next rule */
2658
                        rule_o = rule_o->next;
131,630✔
2659
                } while (rule_o != filter_o->rules);
131,630✔
2660
        }
2661

2662
        /* success, mark the snapshot as a shadow and return */
2663
        snap->shadow = true;
52,911✔
2664
        return;
52,911✔
2665

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

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

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

2699
        gen_bpf_release(col->prgm_bpf);
309✔
2700
        col->prgm_bpf = NULL;
7,824✔
2701
}
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