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

drakenclimber / libseccomp / 7064690886

01 Dec 2023 08:07PM UTC coverage: 89.523% (-0.5%) from 89.973%
7064690886

push

github

drakenclimber
Revert "tests: Fix wrong syscall-error in test 29"

Revert commit a707ec98cc2c ("tests: Fix wrong syscall-error in test
29").  The reverted commit properly set the syscall number to -10001 in
a call to seccomp_rule_add_exact(), but due to previously unknown
shortcomings in the test, this led to failures.  Revert to the previous
version of test 29.

Note that this reverted version of test 29 is not functioning as
originally designed, but it is largely benign and the functional test
will pass on all architectures.  (It's generating a BPF filter that will
allow all syscalls on x86.)

Fixes: a707ec98cc2c ("tests: Fix wrong syscall-error in test 29")
Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>

2572 of 2873 relevant lines covered (89.52%)

272267.7 hits per line

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

90.16
/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
 * Author: Paul Moore <paul@paul-moore.com>
7
 */
8

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

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

30
#include <seccomp.h>
31

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

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

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

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

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

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

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

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

127
        a_arg = __db_chain_arg_priority(a);
382,898✔
128
        b_arg = __db_chain_arg_priority(b);
382,898✔
129
        if (a_arg < b_arg)
382,898✔
130
                return true;
131
        else if (a_arg > b_arg)
370,840✔
132
                return false;
133

134
        a_op = __db_chain_op_priority(a->op_orig);
357,344✔
135
        b_op = __db_chain_op_priority(b->op_orig);
357,344✔
136
        if (a_op < b_op)
357,344✔
137
                return true;
138
        else if (a_op > b_op)
356,684✔
139
                return false;
140

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

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

158
        return false;
159
}
160

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

175
        a_arg = __db_chain_arg_priority(a);
558,344✔
176
        b_arg = __db_chain_arg_priority(b);
558,344✔
177

178
        return ((a_arg == b_arg) && (a->op == b->op) &&
532,790✔
179
                (a->datum == b->datum) && (a->mask == b->mask));
1,067,510✔
180
}
181

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

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

207
/**
208
 * Get a node reference
209
 * @param node pointer to a node
210
 *
211
 * This function gets a reference to an individual node.  Returns a pointer
212
 * to the node.
213
 *
214
 */
215
static struct db_arg_chain_tree *_db_node_get(struct db_arg_chain_tree *node)
541,634✔
216
{
217
        if (node != NULL)
541,634✔
218
                node->refcnt++;
95,922✔
219
        return node;
492,380✔
220
}
221

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

238
        while (n->lvl_prv)
100,626✔
239
                n = n->lvl_prv;
8,448✔
240
        start = n;
177,030✔
241

242
        while (n != NULL) {
177,030✔
243
                links = 0;
145,644✔
244
                if (n->lvl_prv)
145,644✔
245
                        links++;
53,466✔
246
                if (n->lvl_nxt)
145,644✔
247
                        links++;
106,932✔
248

249
                if (n->refcnt > links)
145,644✔
250
                        return cnt;
251

252
                n = n->lvl_nxt;
84,852✔
253
        }
254

255
        n = start;
31,386✔
256
        while (n != NULL)
84,852✔
257
                cnt += _db_node_put(&n);
53,466✔
258

259
        return cnt;
31,386✔
260
}
261

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

274
        cnt = _db_node_put(tree);
965,540✔
275
        if (*tree)
965,540✔
276
                cnt += _db_level_clean(*tree);
92,178✔
277

278
        return cnt;
965,540✔
279
}
280

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

296
        if (n == NULL)
1,428,060✔
297
                return 0;
298

299
        if (--(n->refcnt) == 0) {
830,060✔
300
                lvl_p = n->lvl_prv;
375,990✔
301
                lvl_n = n->lvl_nxt;
375,990✔
302
                nxt_t = n->nxt_t;
375,990✔
303
                nxt_f = n->nxt_f;
375,990✔
304

305
                /* split the current level */
306
                /* NOTE: we still hold a ref for both lvl_p and lvl_n */
307
                if (lvl_p)
375,990✔
308
                        lvl_p->lvl_nxt = NULL;
×
309
                if (lvl_n)
375,990✔
310
                        lvl_n->lvl_prv = NULL;
53,466✔
311

312
                /* drop refcnts on the current level */
313
                if (lvl_p)
375,990✔
314
                        cnt += _db_node_put(&lvl_p);
×
315
                if (lvl_n)
375,990✔
316
                        cnt += _db_node_put(&lvl_n);
53,466✔
317

318
                /* re-link current level if it still exists */
319
                if (lvl_p)
375,990✔
320
                        lvl_p->lvl_nxt = _db_node_get(lvl_n);
×
321
                if (lvl_n)
375,990✔
322
                        lvl_n->lvl_prv = _db_node_get(lvl_p);
22,080✔
323

324
                /* update caller's pointer */
325
                if (lvl_p)
375,990✔
326
                        *node = lvl_p;
×
327
                else if (lvl_n)
375,990✔
328
                        *node = lvl_n;
22,080✔
329
                else
330
                        *node = NULL;
353,910✔
331

332
                /* drop the next level(s) */
333
                cnt += _db_tree_put(&nxt_t);
375,990✔
334
                cnt += _db_tree_put(&nxt_f);
375,990✔
335

336
                /* cleanup and accounting */
337
                free(n);
375,990✔
338
                cnt++;
375,990✔
339
        }
340

341
        return cnt;
342
}
343

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

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

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

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

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

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

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

383
        return cnt;
384

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

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

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

405
        return cnt;
11,440✔
406
}
407

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

423
        if (tree == NULL)
7,727,190✔
424
                return 0;
425

426
        c_iter = tree;
427
        while (c_iter->lvl_prv != NULL)
3,343,880✔
428
                c_iter = c_iter->lvl_prv;
429

430
        do {
3,791,310✔
431
                if (c_iter->act_t_flg && c_iter->act_t != action)
3,791,310✔
432
                        return -EEXIST;
433
                if (c_iter->act_f_flg && c_iter->act_f != action)
3,780,860✔
434
                        return -EEXIST;
435

436
                rc = _db_tree_act_check(c_iter->nxt_t, action);
3,780,710✔
437
                if (rc < 0)
3,780,710✔
438
                        return rc;
16,220✔
439
                rc = _db_tree_act_check(c_iter->nxt_f, action);
3,764,490✔
440
                if (rc < 0)
3,764,490✔
441
                        return rc;
156✔
442

443
                c_iter = c_iter->lvl_nxt;
3,764,330✔
444
        } while (c_iter != NULL);
3,764,330✔
445

446
        return 0;
447
}
448

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

688
        do {
142,422✔
689
                if (_db_chain_eq(x_iter, n_iter)) {
142,422✔
690
                        if (n_iter->act_t_flg) {
75,112✔
691
                                if (!x_iter->act_t_flg) {
1,066✔
692
                                        /* new node has a true action */
693

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

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

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

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

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

784
                        return 0;
74,704✔
785
                } else if (!_db_chain_lt(x_iter, n_iter)) {
67,310✔
786
                        /* try to move along the current level */
787
                        if (x_iter->lvl_nxt == NULL) {
18,056✔
788
                                /* add to the end of this level */
789
                                n_iter->lvl_prv = _db_node_get(x_iter);
4,212✔
790
                                x_iter->lvl_nxt = _db_node_get(n_iter);
4,212✔
791
                                return 0;
4,212✔
792
                        } else
793
                                /* next */
794
                                x_iter = x_iter->lvl_nxt;
13,844✔
795
                } else {
796
                        /* add before the existing node on this level*/
797
                        if (x_iter->lvl_prv != NULL) {
49,254✔
798
                                x_iter->lvl_prv->lvl_nxt = _db_node_get(n_iter);
2,586✔
799
                                n_iter->lvl_prv = x_iter->lvl_prv;
2,586✔
800
                                x_iter->lvl_prv = _db_node_get(n_iter);
2,586✔
801
                                n_iter->lvl_nxt = x_iter;
2,586✔
802
                        } else {
803
                                x_iter->lvl_prv = _db_node_get(n_iter);
46,668✔
804
                                n_iter->lvl_nxt = _db_node_get(x_iter);
46,668✔
805
                        }
806
                        if (*existing == x_iter) {
49,254✔
807
                                *existing = _db_node_get(n_iter);
46,668✔
808
                                _db_node_put(&x_iter);
46,668✔
809
                        }
810
                        return 0;
49,254✔
811
                }
812
        } while (x_iter);
13,844✔
813

814
        return 0;
815
}
816

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

830
        if (db == NULL)
60,280✔
831
                return;
832

833
        /* free any filters */
834
        if (db->syscalls != NULL) {
60,280✔
835
                s_iter = db->syscalls;
836
                while (s_iter != NULL) {
176,920✔
837
                        db->syscalls = s_iter->next;
150,248✔
838
                        _db_tree_put(&s_iter->chains);
150,248✔
839
                        free(s_iter);
150,248✔
840
                        s_iter = db->syscalls;
150,248✔
841
                }
842
                db->syscalls = NULL;
26,672✔
843
        }
844
        db->syscall_cnt = 0;
60,280✔
845

846
        /* free any rules */
847
        if (db->rules != NULL) {
60,280✔
848
                /* split the loop first then loop and free */
849
                db->rules->prev->next = NULL;
27,413✔
850
                r_iter = db->rules;
27,413✔
851
                while (r_iter != NULL) {
227,284✔
852
                        db->rules = r_iter->next;
199,871✔
853
                        free(r_iter);
199,871✔
854
                        r_iter = db->rules;
199,871✔
855
                }
856
                db->rules = NULL;
27,413✔
857
        }
858
}
859

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

872
        db = zmalloc(sizeof(*db));
30,140✔
873
        if (db == NULL)
30,140✔
874
                return NULL;
875

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

880
        return db;
30,140✔
881
}
882

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

896
        /* free and reset the DB */
897
        _db_reset(db);
13,167✔
898
        free(db);
26,121✔
899
}
900

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

913
        if (snap == NULL)
6,173✔
914
                return;
915

916
        if (snap->filter_cnt > 0) {
6,173✔
917
                for (iter = 0; iter < snap->filter_cnt; iter++) {
19,127✔
918
                        if (snap->filters[iter])
12,954✔
919
                                _db_release(snap->filters[iter]);
25,908✔
920
                }
921
                free(snap->filters);
6,173✔
922
        }
923
        free(snap);
6,173✔
924
}
925

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

945
        assert(db != NULL);
78✔
946

947
        s_iter = db->syscalls;
78✔
948
        while (s_iter != NULL && s_iter->num < syscall) {
182✔
949
                s_prev = s_iter;
104✔
950
                s_iter = s_iter->next;
104✔
951
        }
952

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

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

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

979
        return 0;
980
}
981

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

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

1007
        return rule;
100,291✔
1008
}
1009

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

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

1030
        return dest;
316,821✔
1031
}
1032

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

1050
        if (col == NULL)
7,968✔
1051
                return -EINVAL;
1052

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

1061
        /* set the endianess to undefined */
1062
        col->endian = 0;
7,968✔
1063

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

1075
        /* set the state */
1076
        col->state = _DB_STA_VALID;
7,968✔
1077
        if (def_action == SCMP_ACT_NOTIFY)
7,968✔
1078
                col->notify_used = true;
×
1079
        else
1080
                col->notify_used = false;
7,968✔
1081

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

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

1101
        return 0;
1102
}
1103

1104
/**
1105
 * Intitalize a seccomp filter collection
1106
 * @param def_action the default filter action
1107
 *
1108
 * This function initializes a seccomp filter collection and readies it for
1109
 * use.  Returns a pointer to the collection on success, NULL on failure.
1110
 *
1111
 */
1112
struct db_filter_col *db_col_init(uint32_t def_action)
7,603✔
1113
{
1114
        struct db_filter_col *col;
7,603✔
1115

1116
        col = zmalloc(sizeof(*col));
7,603✔
1117
        if (col == NULL)
7,603✔
1118
                return NULL;
1119

1120
        /* reset the DB to a known state */
1121
        if (db_col_reset(col, def_action) < 0)
7,603✔
1122
                goto init_failure;
×
1123

1124
        return col;
1125

1126
init_failure:
×
1127
        db_col_release(col);
×
1128
        return NULL;
×
1129
}
1130

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

1144
        if (col == NULL)
7,603✔
1145
                return;
1146

1147
        /* set the state, just in case */
1148
        col->state = _DB_STA_FREED;
7,603✔
1149

1150
        /* free any snapshots */
1151
        while (col->snapshots != NULL) {
13,776✔
1152
                snap = col->snapshots;
6,173✔
1153
                col->snapshots = snap->next;
6,173✔
1154
                _db_snap_release(snap);
6,173✔
1155
        }
1156

1157
        /* free any filters */
1158
        for (iter = 0; iter < col->filter_cnt; iter++)
19,627✔
1159
                _db_release(col->filters[iter]);
24,048✔
1160
        col->filter_cnt = 0;
7,603✔
1161
        if (col->filters)
7,603✔
1162
                free(col->filters);
7,367✔
1163
        col->filters = NULL;
7,603✔
1164

1165
        /* free the collection */
1166
        free(col);
7,603✔
1167
}
1168

1169
/**
1170
 * Validate a filter collection
1171
 * @param col the seccomp filter collection
1172
 *
1173
 * This function validates a seccomp filter collection.  Returns zero if the
1174
 * collection is valid, negative values on failure.
1175
 *
1176
 */
1177
int db_col_valid(struct db_filter_col *col)
58,725✔
1178
{
1179
        if (col != NULL && col->state == _DB_STA_VALID && col->filter_cnt > 0)
58,725✔
1180
                return 0;
58,718✔
1181
        return -EINVAL;
1182
}
1183

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

1198
                /* kernel disallows TSYNC and NOTIFY in one filter unless we
1199
                 * have the TSYNC_ESRCH flag */
1200
                if (sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH) < 1 &&
50,529✔
1201
                    col->attr.tsync_enable && action == SCMP_ACT_NOTIFY)
2,158✔
1202
                        return -EINVAL;
1203
        }
1204

1205
        if (sys_chk_seccomp_action(action) == 1)
58,498✔
1206
                return 0;
58,494✔
1207
        return -EINVAL;
1208
}
1209

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

1225
        /* verify that the endianess is a match */
1226
        if (col_dst->endian != col_src->endian)
26✔
1227
                return -EDOM;
1228

1229
        /* make sure we don't have any arch/filter collisions */
1230
        for (iter_a = 0; iter_a < col_dst->filter_cnt; iter_a++) {
52✔
1231
                for (iter_b = 0; iter_b < col_src->filter_cnt; iter_b++) {
52✔
1232
                        if (col_dst->filters[iter_a]->arch->token ==
26✔
1233
                            col_src->filters[iter_b]->arch->token)
26✔
1234
                                return -EEXIST;
1235
                }
1236
        }
1237

1238
        /* expand the destination */
1239
        dbs = realloc(col_dst->filters,
26✔
1240
                      sizeof(struct db_filter *) *
1241
                      (col_dst->filter_cnt + col_src->filter_cnt));
26✔
1242
        if (dbs == NULL)
26✔
1243
                return -ENOMEM;
1244
        col_dst->filters = dbs;
26✔
1245

1246
        /* transfer the architecture filters */
1247
        for (iter_a = col_dst->filter_cnt, iter_b = 0;
26✔
1248
             iter_b < col_src->filter_cnt; iter_a++, iter_b++) {
52✔
1249
                col_dst->filters[iter_a] = col_src->filters[iter_b];
26✔
1250
                col_dst->filter_cnt++;
26✔
1251
        }
1252

1253
        /* free the source */
1254
        col_src->filter_cnt = 0;
26✔
1255
        db_col_release(col_src);
26✔
1256

1257
        return 0;
26✔
1258
}
1259

1260
/**
1261
 * Check to see if an architecture filter exists in the filter collection
1262
 * @param col the seccomp filter collection
1263
 * @param arch_token the architecture token
1264
 *
1265
 * Iterate through the given filter collection checking to see if a filter
1266
 * exists for the specified architecture.  Returns -EEXIST if a filter is found,
1267
 * zero if a matching filter does not exist.
1268
 *
1269
 */
1270
int db_col_arch_exist(struct db_filter_col *col, uint32_t arch_token)
29,339✔
1271
{
1272
        unsigned int iter;
29,339✔
1273

1274
        for (iter = 0; iter < col->filter_cnt; iter++)
94,836✔
1275
                if (col->filters[iter]->arch->token == arch_token)
69,752✔
1276
                        return -EEXIST;
1277

1278
        return 0;
1279
}
1280

1281
/**
1282
 * Get a filter attribute
1283
 * @param col the seccomp filter collection
1284
 * @param attr the filter attribute
1285
 * @param value the filter attribute value
1286
 *
1287
 * Get the requested filter attribute and provide it via @value.  Returns zero
1288
 * on success, negative values on failure.
1289
 *
1290
 */
1291
int db_col_attr_get(const struct db_filter_col *col,
14✔
1292
                    enum scmp_filter_attr attr, uint32_t *value)
1293
{
1294
        int rc = 0;
14✔
1295

1296
        switch (attr) {
14✔
1297
        case SCMP_FLTATR_ACT_DEFAULT:
1✔
1298
                *value = col->attr.act_default;
1✔
1299
                break;
1✔
1300
        case SCMP_FLTATR_ACT_BADARCH:
1✔
1301
                *value = col->attr.act_badarch;
1✔
1302
                break;
1✔
1303
        case SCMP_FLTATR_CTL_NNP:
1✔
1304
                *value = col->attr.nnp_enable;
1✔
1305
                break;
1✔
1306
        case SCMP_FLTATR_CTL_TSYNC:
1✔
1307
                *value = col->attr.tsync_enable;
1✔
1308
                break;
1✔
1309
        case SCMP_FLTATR_API_TSKIP:
1✔
1310
                *value = col->attr.api_tskip;
1✔
1311
                break;
1✔
1312
        case SCMP_FLTATR_CTL_LOG:
1✔
1313
                *value = col->attr.log_enable;
1✔
1314
                break;
1✔
1315
        case SCMP_FLTATR_CTL_SSB:
1✔
1316
                *value = col->attr.spec_allow;
1✔
1317
                break;
1✔
1318
        case SCMP_FLTATR_CTL_OPTIMIZE:
1✔
1319
                *value = col->attr.optimize;
1✔
1320
                break;
1✔
1321
        case SCMP_FLTATR_API_SYSRAWRC:
5✔
1322
                *value = col->attr.api_sysrawrc;
5✔
1323
                break;
5✔
1324
        default:
1325
                rc = -EINVAL;
1326
                break;
1327
        }
1328

1329
        return rc;
14✔
1330
}
1331

1332
/**
1333
 * Get a filter attribute
1334
 * @param col the seccomp filter collection
1335
 * @param attr the filter attribute
1336
 *
1337
 * Returns the requested filter attribute value with zero on any error.
1338
 * Special care must be given with this function as error conditions can be
1339
 * hidden from the caller.
1340
 *
1341
 */
1342
uint32_t db_col_attr_read(const struct db_filter_col *col,
4✔
1343
                          enum scmp_filter_attr attr)
1344
{
1345
        uint32_t value = 0;
4✔
1346

1347
        db_col_attr_get(col, attr, &value);
4✔
1348
        return value;
4✔
1349
}
1350

1351
/**
1352
 * Set a filter attribute
1353
 * @param col the seccomp filter collection
1354
 * @param attr the filter attribute
1355
 * @param value the filter attribute value
1356
 *
1357
 * Set the requested filter attribute with the given value.  Returns zero on
1358
 * success, negative values on failure.
1359
 *
1360
 */
1361
int db_col_attr_set(struct db_filter_col *col,
489✔
1362
                    enum scmp_filter_attr attr, uint32_t value)
1363
{
1364
        int rc = 0;
489✔
1365

1366
        switch (attr) {
489✔
1367
        case SCMP_FLTATR_ACT_DEFAULT:
1368
                /* read only */
1369
                return -EACCES;
1370
                break;
1✔
1371
        case SCMP_FLTATR_ACT_BADARCH:
1✔
1372
                if (db_col_action_valid(col, value) == 0)
1✔
1373
                        col->attr.act_badarch = value;
1✔
1374
                else
1375
                        return -EINVAL;
1376
                break;
1✔
1377
        case SCMP_FLTATR_CTL_NNP:
1✔
1378
                col->attr.nnp_enable = (value ? 1 : 0);
1✔
1379
                break;
1✔
1380
        case SCMP_FLTATR_CTL_TSYNC:
2✔
1381
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC);
2✔
1382
                if (rc == 1) {
2✔
1383
                        /* supported */
1384
                        rc = 0;
2✔
1385
                        /* kernel disallows TSYNC and NOTIFY in one filter
1386
                         * unless we have TSYNC_ESRCH */
1387
                        if (sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH) < 1 &&
2✔
1388
                            value && col->notify_used)
1✔
1389
                                return -EINVAL;
1390
                        col->attr.tsync_enable = (value ? 1 : 0);
2✔
1391
                } else if (rc == 0)
×
1392
                        /* unsupported */
1393
                        rc = -EOPNOTSUPP;
×
1394
                break;
1395
        case SCMP_FLTATR_API_TSKIP:
4✔
1396
                col->attr.api_tskip = (value ? 1 : 0);
4✔
1397
                break;
4✔
1398
        case SCMP_FLTATR_CTL_LOG:
2✔
1399
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_LOG);
2✔
1400
                if (rc == 1) {
2✔
1401
                        /* supported */
1402
                        rc = 0;
2✔
1403
                        col->attr.log_enable = (value ? 1 : 0);
2✔
1404
                } else if (rc == 0) {
×
1405
                        /* unsupported */
1406
                        rc = -EOPNOTSUPP;
×
1407
                }
1408
                break;
1409
        case SCMP_FLTATR_CTL_SSB:
2✔
1410
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW);
2✔
1411
                if (rc == 1) {
2✔
1412
                        /* supported */
1413
                        rc = 0;
2✔
1414
                        col->attr.spec_allow = (value ? 1 : 0);
2✔
1415
                } else if (rc == 0) {
×
1416
                        /* unsupported */
1417
                        rc = -EOPNOTSUPP;
×
1418
                }
1419
                break;
1420
        case SCMP_FLTATR_CTL_OPTIMIZE:
473✔
1421
                switch (value) {
473✔
1422
                case 1:
473✔
1423
                case 2:
1424
                        col->attr.optimize = value;
473✔
1425
                        break;
473✔
1426
                default:
1427
                        rc = -EOPNOTSUPP;
1428
                        break;
1429
                }
1430
                break;
1431
        case SCMP_FLTATR_API_SYSRAWRC:
2✔
1432
                col->attr.api_sysrawrc = (value ? 1 : 0);
2✔
1433
                break;
2✔
1434
        default:
1435
                rc = -EINVAL;
1436
                break;
1437
        }
1438

1439
        return rc;
1440
}
1441

1442
/**
1443
 * Add a new architecture filter to a filter collection
1444
 * @param col the seccomp filter collection
1445
 * @param arch the architecture
1446
 *
1447
 * This function adds a new architecture filter DB to an existing seccomp
1448
 * filter collection assuming there isn't a filter DB already present with the
1449
 * same architecture.  Returns zero on success, negative values on failure.
1450
 *
1451
 */
1452
int db_col_db_new(struct db_filter_col *col, const struct arch_def *arch)
8,440✔
1453
{
1454
        int rc;
8,440✔
1455
        struct db_filter *db;
8,440✔
1456

1457
        db = _db_init(arch);
8,440✔
1458
        if (db == NULL)
8,440✔
1459
                return -ENOMEM;
1460
        rc = db_col_db_add(col, db);
8,440✔
1461
        if (rc < 0)
8,440✔
1462
                _db_release(db);
×
1463

1464
        return rc;
1465
}
1466

1467
/**
1468
 * Add a new filter DB to a filter collection
1469
 * @param col the seccomp filter collection
1470
 * @param db the seccomp filter DB
1471
 *
1472
 * This function adds an existing seccomp filter DB to an existing seccomp
1473
 * filter collection assuming there isn't a filter DB already present with the
1474
 * same architecture.  Returns zero on success, negative values on failure.
1475
 *
1476
 */
1477
int db_col_db_add(struct db_filter_col *col, struct db_filter *db)
16,408✔
1478
{
1479
        struct db_filter **dbs;
16,408✔
1480

1481
        if (col->endian != 0 && col->endian != db->arch->endian)
16,408✔
1482
                return -EDOM;
1483

1484
        if (db_col_arch_exist(col, db->arch->token))
32,816✔
1485
                return -EEXIST;
1486

1487
        dbs = realloc(col->filters,
16,408✔
1488
                      sizeof(struct db_filter *) * (col->filter_cnt + 1));
16,408✔
1489
        if (dbs == NULL)
16,408✔
1490
                return -ENOMEM;
1491
        col->filters = dbs;
16,408✔
1492
        col->filter_cnt++;
16,408✔
1493
        col->filters[col->filter_cnt - 1] = db;
16,408✔
1494
        if (col->endian == 0)
16,408✔
1495
                col->endian = db->arch->endian;
9,627✔
1496

1497
        return 0;
1498
}
1499

1500
/**
1501
 * Remove a filter DB from a filter collection
1502
 * @param col the seccomp filter collection
1503
 * @param arch_token the architecture token
1504
 *
1505
 * This function removes an existing seccomp filter DB from an existing seccomp
1506
 * filter collection.  Returns zero on success, negative values on failure.
1507
 *
1508
 */
1509
int db_col_db_remove(struct db_filter_col *col, uint32_t arch_token)
4,019✔
1510
{
1511
        unsigned int iter;
4,019✔
1512
        unsigned int found;
4,019✔
1513
        struct db_filter **dbs;
4,019✔
1514

1515
        if ((col->filter_cnt <= 0) || (db_col_arch_exist(col, arch_token) == 0))
8,038✔
1516
                return -EINVAL;
1517

1518
        for (found = 0, iter = 0; iter < col->filter_cnt; iter++) {
18,658✔
1519
                if (found)
14,639✔
1520
                        col->filters[iter - 1] = col->filters[iter];
10,620✔
1521
                else if (col->filters[iter]->arch->token == arch_token) {
4,019✔
1522
                        _db_release(col->filters[iter]);
4,019✔
1523
                        found = 1;
4,019✔
1524
                }
1525
        }
1526
        col->filters[--col->filter_cnt] = NULL;
4,019✔
1527

1528
        if (col->filter_cnt > 0) {
4,019✔
1529
                /* NOTE: if we can't do the realloc it isn't fatal, we just
1530
                 *       have some extra space allocated */
1531
                dbs = realloc(col->filters,
2,124✔
1532
                              sizeof(struct db_filter *) * col->filter_cnt);
1533
                if (dbs != NULL)
2,124✔
1534
                        col->filters = dbs;
2,124✔
1535
        } else {
1536
                /* this was the last filter so free all the associated memory
1537
                 * and reset the endian token */
1538
                free(col->filters);
1,895✔
1539
                col->filters = NULL;
1,895✔
1540
                col->endian = 0;
1,895✔
1541
        }
1542

1543
        return 0;
1544
}
1545

1546
/**
1547
 * Test if the argument filter can be skipped because it's a tautology
1548
 * @param arg argument filter
1549
 *
1550
 * If this argument filter applied to the lower 32 bit can be skipped this
1551
 * function returns false.
1552
 *
1553
 */
1554
static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg)
43,372✔
1555
{
1556
        if (arg->op == SCMP_CMP_MASKED_EQ && D64_LO(arg->mask) == 0)
10✔
1557
                return false;
68✔
1558

1559
        return true;
1560
}
1561

1562
/**
1563
 * Test if the argument filter can be skipped because it's a tautology
1564
 * @param arg argument filter
1565
 *
1566
 * If this argument filter applied to the upper 32 bit can be skipped this
1567
 * function returns false.
1568
 *
1569
 */
1570
static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg)
168,502✔
1571
{
1572
        if (arg->op == SCMP_CMP_MASKED_EQ && D64_HI(arg->mask) == 0)
11,354✔
1573
                return false;
11,354✔
1574

1575
        return true;
1576
}
1577

1578
/**
1579
 * Fixup the node based on the op/mask
1580
 * @param node the chain node
1581
 *
1582
 * Ensure the datum is masked as well.
1583
 *
1584
 */
1585
static void _db_node_mask_fixup(struct db_arg_chain_tree *node)
200,452✔
1586
{
1587
        node->datum &= node->mask;
200,452✔
1588
}
1589

1590
/**
1591
 * Generate a new filter rule for a 64 bit system
1592
 * @param arch the architecture definition
1593
 * @param rule the new filter rule
1594
 *
1595
 * This function generates a new syscall filter for a 64 bit system. Returns
1596
 * zero on success, negative values on failure.
1597
 *
1598
 */
1599
static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
153,984✔
1600
                                           const struct db_api_rule_list *rule)
1601
{
1602
        unsigned int iter;
153,984✔
1603
        struct db_sys_list *s_new;
153,984✔
1604
        const struct db_api_arg *chain = rule->args;
153,984✔
1605
        struct db_arg_chain_tree *c_iter[3] = { NULL, NULL, NULL };
153,984✔
1606
        struct db_arg_chain_tree *c_prev[3] = { NULL, NULL, NULL };
153,984✔
1607
        enum scmp_compare op_prev = _SCMP_CMP_MIN;
153,984✔
1608
        unsigned int arg;
153,984✔
1609
        scmp_datum_t mask;
153,984✔
1610
        scmp_datum_t datum;
153,984✔
1611

1612
        s_new = zmalloc(sizeof(*s_new));
153,984✔
1613
        if (s_new == NULL)
153,984✔
1614
                return NULL;
1615
        s_new->num = rule->syscall;
153,984✔
1616
        s_new->valid = true;
153,984✔
1617
        /* run through the argument chain */
1618
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
1,077,890✔
1619
                if (chain[iter].valid == 0)
923,904✔
1620
                        continue;
755,402✔
1621

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

1624
                /* skip generating instruction which are no-ops */
1625
                if (!_db_arg_cmp_need_hi(&chain[iter]) &&
168,502✔
1626
                    !_db_arg_cmp_need_lo(&chain[iter]))
11,354✔
1627
                        continue;
68✔
1628

1629
                c_iter[0] = zmalloc(sizeof(*c_iter[0]));
168,434✔
1630
                if (c_iter[0] == NULL)
168,434✔
1631
                        goto gen_64_failure;
×
1632
                c_iter[1] = zmalloc(sizeof(*c_iter[1]));
168,434✔
1633
                if (c_iter[1] == NULL) {
168,434✔
1634
                        free(c_iter[0]);
×
1635
                        goto gen_64_failure;
×
1636
                }
1637
                c_iter[2] = NULL;
168,434✔
1638

1639
                arg = chain[iter].arg;
168,434✔
1640
                mask = chain[iter].mask;
168,434✔
1641
                datum = chain[iter].datum;
168,434✔
1642

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

1760
                /* setup the level */
1761
                switch (chain[iter].op) {
168,434✔
1762
                case SCMP_CMP_GT:
7,104✔
1763
                case SCMP_CMP_GE:
1764
                case SCMP_CMP_LE:
1765
                case SCMP_CMP_LT:
1766
                        c_iter[2] = zmalloc(sizeof(*c_iter[2]));
7,104✔
1767
                        if (c_iter[2] == NULL) {
7,104✔
1768
                                free(c_iter[0]);
×
1769
                                free(c_iter[1]);
×
1770
                                goto gen_64_failure;
×
1771
                        }
1772

1773
                        c_iter[0]->arg = arg;
7,104✔
1774
                        c_iter[1]->arg = arg;
7,104✔
1775
                        c_iter[2]->arg = arg;
7,104✔
1776
                        c_iter[0]->arg_h_flg = true;
7,104✔
1777
                        c_iter[1]->arg_h_flg = true;
7,104✔
1778
                        c_iter[2]->arg_h_flg = false;
7,104✔
1779
                        c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
7,104✔
1780
                        c_iter[1]->arg_offset = arch_arg_offset_hi(arch, arg);
7,104✔
1781
                        c_iter[2]->arg_offset = arch_arg_offset_lo(arch, arg);
7,104✔
1782

1783
                        c_iter[0]->mask = D64_HI(mask);
7,104✔
1784
                        c_iter[1]->mask = D64_HI(mask);
7,104✔
1785
                        c_iter[2]->mask = D64_LO(mask);
7,104✔
1786
                        c_iter[0]->datum = D64_HI(datum);
7,104✔
1787
                        c_iter[1]->datum = D64_HI(datum);
7,104✔
1788
                        c_iter[2]->datum = D64_LO(datum);
7,104✔
1789
                        c_iter[0]->datum_full = datum;
7,104✔
1790
                        c_iter[1]->datum_full = datum;
7,104✔
1791
                        c_iter[2]->datum_full = datum;
7,104✔
1792

1793
                        _db_node_mask_fixup(c_iter[0]);
7,104✔
1794
                        _db_node_mask_fixup(c_iter[1]);
7,104✔
1795
                        _db_node_mask_fixup(c_iter[2]);
7,104✔
1796

1797
                        c_iter[0]->op = SCMP_CMP_GT;
7,104✔
1798
                        c_iter[1]->op = SCMP_CMP_EQ;
7,104✔
1799
                        switch (chain[iter].op) {
7,104✔
1800
                        case SCMP_CMP_GT:
770✔
1801
                        case SCMP_CMP_LE:
1802
                                c_iter[2]->op = SCMP_CMP_GT;
770✔
1803
                                break;
770✔
1804
                        case SCMP_CMP_GE:
6,334✔
1805
                        case SCMP_CMP_LT:
1806
                                c_iter[2]->op = SCMP_CMP_GE;
6,334✔
1807
                                break;
6,334✔
1808
                        default:
×
1809
                                /* we should never get here */
1810
                                goto gen_64_failure;
×
1811
                        }
1812
                        c_iter[0]->op_orig = chain[iter].op;
7,104✔
1813
                        c_iter[1]->op_orig = chain[iter].op;
7,104✔
1814
                        c_iter[2]->op_orig = chain[iter].op;
7,104✔
1815

1816
                        c_iter[0]->nxt_f = _db_node_get(c_iter[1]);
7,104✔
1817
                        c_iter[1]->nxt_t = _db_node_get(c_iter[2]);
7,104✔
1818
                        break;
7,104✔
1819
                case SCMP_CMP_EQ:
161,330✔
1820
                case SCMP_CMP_MASKED_EQ:
1821
                case SCMP_CMP_NE:
1822
                        c_iter[0]->arg = arg;
161,330✔
1823
                        c_iter[1]->arg = arg;
161,330✔
1824
                        c_iter[0]->arg_h_flg = true;
161,330✔
1825
                        c_iter[1]->arg_h_flg = false;
161,330✔
1826
                        c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
161,330✔
1827
                        c_iter[1]->arg_offset = arch_arg_offset_lo(arch, arg);
161,330✔
1828

1829
                        c_iter[0]->mask = D64_HI(mask);
161,330✔
1830
                        c_iter[1]->mask = D64_LO(mask);
161,330✔
1831
                        c_iter[0]->datum = D64_HI(datum);
161,330✔
1832
                        c_iter[1]->datum = D64_LO(datum);
161,330✔
1833
                        c_iter[0]->datum_full = datum;
161,330✔
1834
                        c_iter[1]->datum_full = datum;
161,330✔
1835

1836
                        _db_node_mask_fixup(c_iter[0]);
161,330✔
1837
                        _db_node_mask_fixup(c_iter[1]);
161,330✔
1838

1839
                        switch (chain[iter].op) {
161,330✔
1840
                        case SCMP_CMP_MASKED_EQ:
11,286✔
1841
                                c_iter[0]->op = SCMP_CMP_MASKED_EQ;
11,286✔
1842
                                c_iter[1]->op = SCMP_CMP_MASKED_EQ;
11,286✔
1843
                                break;
11,286✔
1844
                        default:
150,044✔
1845
                                c_iter[0]->op = SCMP_CMP_EQ;
150,044✔
1846
                                c_iter[1]->op = SCMP_CMP_EQ;
150,044✔
1847
                        }
1848
                        c_iter[0]->op_orig = chain[iter].op;
161,330✔
1849
                        c_iter[1]->op_orig = chain[iter].op;
161,330✔
1850

1851
                        c_iter[0]->nxt_t = _db_node_get(c_iter[1]);
161,330✔
1852
                        break;
161,330✔
1853
                default:
×
1854
                        /* we should never get here */
1855
                        goto gen_64_failure;
×
1856
                }
1857

1858
                /* link this level to the previous level */
1859
                if (c_prev[0] != NULL) {
168,434✔
1860
                        switch (op_prev) {
77,232✔
1861
                        case SCMP_CMP_GT:
1862
                        case SCMP_CMP_GE:
1863
                                c_prev[0]->nxt_t = _db_node_get(c_iter[0]);
112✔
1864
                                c_prev[2]->nxt_t = _db_node_get(c_iter[0]);
112✔
1865
                                break;
112✔
1866
                        case SCMP_CMP_EQ:
1867
                        case SCMP_CMP_MASKED_EQ:
1868
                                c_prev[1]->nxt_t = _db_node_get(c_iter[0]);
70,042✔
1869
                                break;
70,042✔
1870
                        case SCMP_CMP_LE:
1871
                        case SCMP_CMP_LT:
1872
                                c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
102✔
1873
                                c_prev[2]->nxt_f = _db_node_get(c_iter[0]);
102✔
1874
                                break;
102✔
1875
                        case SCMP_CMP_NE:
1876
                                c_prev[0]->nxt_f = _db_node_get(c_iter[0]);
6,976✔
1877
                                c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
6,976✔
1878
                                break;
6,976✔
1879
                        default:
×
1880
                                /* we should never get here */
1881
                                goto gen_64_failure;
×
1882
                        }
1883
                } else
1884
                        s_new->chains = _db_node_get(c_iter[0]);
91,202✔
1885

1886
                /* update the node count */
1887
                switch (chain[iter].op) {
168,434✔
1888
                case SCMP_CMP_NE:
161,330✔
1889
                case SCMP_CMP_EQ:
1890
                case SCMP_CMP_MASKED_EQ:
1891
                        s_new->node_cnt += 2;
161,330✔
1892
                        break;
161,330✔
1893
                default:
7,104✔
1894
                        s_new->node_cnt += 3;
7,104✔
1895
                }
1896

1897
                /* keep pointers to this level */
1898
                c_prev[0] = c_iter[0];
1899
                c_prev[1] = c_iter[1];
1900
                c_prev[2] = c_iter[2];
1901
                op_prev = chain[iter].op;
1902
        }
1903
        if (c_iter[0] != NULL) {
153,984✔
1904
                /* set the actions on the last layer */
1905
                switch (op_prev) {
91,202✔
1906
                case SCMP_CMP_GT:
908✔
1907
                case SCMP_CMP_GE:
1908
                        c_iter[0]->act_t_flg = true;
908✔
1909
                        c_iter[0]->act_t = rule->action;
908✔
1910
                        c_iter[2]->act_t_flg = true;
908✔
1911
                        c_iter[2]->act_t = rule->action;
908✔
1912
                        break;
908✔
1913
                case SCMP_CMP_LE:
5,982✔
1914
                case SCMP_CMP_LT:
1915
                        c_iter[1]->act_f_flg = true;
5,982✔
1916
                        c_iter[1]->act_f = rule->action;
5,982✔
1917
                        c_iter[2]->act_f_flg = true;
5,982✔
1918
                        c_iter[2]->act_f = rule->action;
5,982✔
1919
                        break;
5,982✔
1920
                case SCMP_CMP_EQ:
77,060✔
1921
                case SCMP_CMP_MASKED_EQ:
1922
                        c_iter[1]->act_t_flg = true;
77,060✔
1923
                        c_iter[1]->act_t = rule->action;
77,060✔
1924
                        break;
77,060✔
1925
                case SCMP_CMP_NE:
7,252✔
1926
                        c_iter[0]->act_f_flg = true;
7,252✔
1927
                        c_iter[0]->act_f = rule->action;
7,252✔
1928
                        c_iter[1]->act_f_flg = true;
7,252✔
1929
                        c_iter[1]->act_f = rule->action;
7,252✔
1930
                        break;
7,252✔
1931
                default:
×
1932
                        /* we should never get here */
1933
                        goto gen_64_failure;
×
1934
                }
1935
        } else
1936
                s_new->action = rule->action;
62,782✔
1937

1938
        return s_new;
1939

1940
gen_64_failure:
×
1941
        /* free the new chain and its syscall struct */
1942
        _db_tree_put(&s_new->chains);
×
1943
        free(s_new);
×
1944
        return NULL;
×
1945
}
1946

1947
/**
1948
 * Generate a new filter rule for a 32 bit system
1949
 * @param arch the architecture definition
1950
 * @param rule the new filter rule
1951
 *
1952
 * This function generates a new syscall filter for a 32 bit system. Returns
1953
 * zero on success, negative values on failure.
1954
 *
1955
 */
1956
static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch,
61,724✔
1957
                                           const struct db_api_rule_list *rule)
1958
{
1959
        unsigned int iter;
61,724✔
1960
        struct db_sys_list *s_new;
61,724✔
1961
        const struct db_api_arg *chain = rule->args;
61,724✔
1962
        struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL;
61,724✔
1963
        bool tf_flag;
61,724✔
1964

1965
        s_new = zmalloc(sizeof(*s_new));
61,724✔
1966
        if (s_new == NULL)
61,724✔
1967
                return NULL;
1968
        s_new->num = rule->syscall;
61,724✔
1969
        s_new->valid = true;
61,724✔
1970
        /* run through the argument chain */
1971
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
432,068✔
1972
                if (chain[iter].valid == 0)
370,344✔
1973
                        continue;
338,326✔
1974

1975
                /* skip generating instructions which are no-ops */
1976
                if (!_db_arg_cmp_need_lo(&chain[iter]))
32,018✔
1977
                        continue;
×
1978

1979
                c_iter = zmalloc(sizeof(*c_iter));
32,018✔
1980
                if (c_iter == NULL)
32,018✔
1981
                        goto gen_32_failure;
×
1982
                c_iter->arg = chain[iter].arg;
32,018✔
1983
                c_iter->arg_h_flg = false;
32,018✔
1984
                c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg);
32,018✔
1985
                c_iter->op = chain[iter].op;
32,018✔
1986
                c_iter->op_orig = chain[iter].op;
32,018✔
1987
                /* implicitly strips off the upper 32 bit */
1988
                c_iter->mask = chain[iter].mask;
32,018✔
1989
                c_iter->datum = chain[iter].datum;
32,018✔
1990
                c_iter->datum_full = chain[iter].datum;
32,018✔
1991

1992
                /* link in the new node and update the chain */
1993
                if (c_prev != NULL) {
32,018✔
1994
                        if (tf_flag)
50✔
1995
                                c_prev->nxt_t = _db_node_get(c_iter);
30✔
1996
                        else
1997
                                c_prev->nxt_f = _db_node_get(c_iter);
20✔
1998
                } else
1999
                        s_new->chains = _db_node_get(c_iter);
31,968✔
2000
                s_new->node_cnt++;
32,018✔
2001

2002
                /* rewrite the op to reduce the op/datum combos */
2003
                switch (c_iter->op) {
32,018✔
2004
                case SCMP_CMP_NE:
10✔
2005
                        c_iter->op = SCMP_CMP_EQ;
10✔
2006
                        tf_flag = false;
10✔
2007
                        break;
10✔
2008
                case SCMP_CMP_LT:
10✔
2009
                        c_iter->op = SCMP_CMP_GE;
10✔
2010
                        tf_flag = false;
10✔
2011
                        break;
10✔
2012
                case SCMP_CMP_LE:
10✔
2013
                        c_iter->op = SCMP_CMP_GT;
10✔
2014
                        tf_flag = false;
10✔
2015
                        break;
10✔
2016
                default:
2017
                        tf_flag = true;
2018
                }
2019

2020
                /* fixup the mask/datum */
2021
                _db_node_mask_fixup(c_iter);
32,018✔
2022

2023
                c_prev = c_iter;
32,018✔
2024
        }
2025
        if (c_iter != NULL) {
61,724✔
2026
                /* set the leaf node */
2027
                if (tf_flag) {
31,968✔
2028
                        c_iter->act_t_flg = true;
31,958✔
2029
                        c_iter->act_t = rule->action;
31,958✔
2030
                } else {
2031
                        c_iter->act_f_flg = true;
10✔
2032
                        c_iter->act_f = rule->action;
10✔
2033
                }
2034
        } else
2035
                s_new->action = rule->action;
29,756✔
2036

2037
        return s_new;
2038

2039
gen_32_failure:
×
2040
        /* free the new chain and its syscall struct */
2041
        _db_tree_put(&s_new->chains);
×
2042
        free(s_new);
×
2043
        return NULL;
×
2044
}
2045

2046
/**
2047
 * Add a new rule to the seccomp filter DB
2048
 * @param db the seccomp filter db
2049
 * @param rule the filter rule
2050
 *
2051
 * This function adds a new syscall filter to the seccomp filter DB, adding to
2052
 * the existing filters for the syscall, unless no argument specific filters
2053
 * are present (filtering only on the syscall).  When adding new chains, the
2054
 * shortest chain, or most inclusive filter match, will be entered into the
2055
 * filter DB. Returns zero on success, negative values on failure.
2056
 *
2057
 * It is important to note that in the case of failure the db may be corrupted,
2058
 * the caller must use the transaction mechanism if the db integrity is
2059
 * important.
2060
 *
2061
 */
2062
int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule)
215,708✔
2063
{
2064
        int rc = -ENOMEM;
215,708✔
2065
        struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
215,708✔
2066
        struct db_iter_state state;
215,708✔
2067
        bool rm_flag = false;
215,708✔
2068

2069
        assert(db != NULL);
215,708✔
2070

2071
        /* do all our possible memory allocation up front so we don't have to
2072
         * worry about failure once we get to the point where we start updating
2073
         * the filter db */
2074
        if (db->arch->size == ARCH_SIZE_64)
215,708✔
2075
                s_new = _db_rule_gen_64(db->arch, rule);
153,984✔
2076
        else if (db->arch->size == ARCH_SIZE_32)
61,724✔
2077
                s_new = _db_rule_gen_32(db->arch, rule);
61,724✔
2078
        else
2079
                return -EFAULT;
2080
        if (s_new == NULL)
215,708✔
2081
                return -ENOMEM;
2082

2083
        /* find a matching syscall/chain or insert a new one */
2084
        s_iter = db->syscalls;
215,708✔
2085
        while (s_iter != NULL && s_iter->num < rule->syscall) {
1,071,700✔
2086
                s_prev = s_iter;
855,990✔
2087
                s_iter = s_iter->next;
855,990✔
2088
        }
2089
        s_new->priority = _DB_PRI_MASK_CHAIN - s_new->node_cnt;
215,708✔
2090
add_reset:
217,996✔
2091
        if (s_iter == NULL || s_iter->num != rule->syscall) {
217,996✔
2092
                /* new syscall, add before s_iter */
2093
                if (s_prev != NULL) {
150,200✔
2094
                        s_new->next = s_prev->next;
118,766✔
2095
                        s_prev->next = s_new;
118,766✔
2096
                } else {
2097
                        s_new->next = db->syscalls;
31,434✔
2098
                        db->syscalls = s_new;
31,434✔
2099
                }
2100
                db->syscall_cnt++;
150,200✔
2101
                return 0;
150,200✔
2102
        } else if (s_iter->chains == NULL) {
67,796✔
2103
                if (rm_flag || !s_iter->valid) {
3,108✔
2104
                        /* we are here because our previous pass cleared the
2105
                         * entire syscall chain when searching for a subtree
2106
                         * match or the existing syscall entry is a phantom,
2107
                         * so either way add the new chain */
2108
                        s_iter->chains = s_new->chains;
2,328✔
2109
                        s_iter->action = s_new->action;
2,328✔
2110
                        s_iter->node_cnt = s_new->node_cnt;
2,328✔
2111
                        if (s_iter->valid)
2,328✔
2112
                                s_iter->priority = s_new->priority;
2,288✔
2113
                        s_iter->valid = true;
2,328✔
2114
                        free(s_new);
2,328✔
2115
                        rc = 0;
2,328✔
2116
                        goto add_priority_update;
2,328✔
2117
                } else {
2118
                        /* syscall exists without any chains - existing filter
2119
                         * is at least as large as the new entry so cleanup and
2120
                         * exit */
2121
                        _db_tree_put(&s_new->chains);
780✔
2122
                        free(s_new);
780✔
2123
                        goto add_free_ok;
780✔
2124
                }
2125
        } else if (s_iter->chains != NULL && s_new->chains == NULL) {
64,688✔
2126
                /* syscall exists with chains but the new filter has no chains
2127
                 * so we need to clear the existing chains and exit */
2128
                _db_tree_put(&s_iter->chains);
132✔
2129
                s_iter->chains = NULL;
132✔
2130
                s_iter->node_cnt = 0;
132✔
2131
                s_iter->action = rule->action;
132✔
2132

2133
                /* cleanup the new tree and return */
2134
                _db_tree_put(&s_new->chains);
132✔
2135
                free(s_new);
132✔
2136
                goto add_free_ok;
132✔
2137
        }
2138

2139
        /* prune any sub-trees that are no longer required */
2140
        memset(&state, 0, sizeof(state));
64,556✔
2141
        state.sx = s_iter;
64,556✔
2142
        state.action = rule->action;
64,556✔
2143
        rc = _db_tree_prune(&s_iter->chains, s_new->chains, &state);
64,556✔
2144
        if (rc > 0) {
64,556✔
2145
                /* we pruned at least some of the existing tree */
2146
                rm_flag = true;
2,288✔
2147
                s_iter->node_cnt -= rc;
2,288✔
2148
                if (s_iter->chains == NULL)
2,288✔
2149
                        /* we pruned the entire tree */
2150
                        goto add_reset;
2,288✔
2151
        } else if ((state.flags & _DB_IST_M_REDUNDANT) == _DB_IST_M_REDUNDANT) {
62,268✔
2152
                /* the existing tree is "shorter", drop the new one */
2153
                _db_tree_put(&s_new->chains);
4,644✔
2154
                free(s_new);
4,644✔
2155
                goto add_free_ok;
4,644✔
2156
        }
2157

2158
        /* add the new rule to the existing filter and cleanup */
2159
        memset(&state, 0, sizeof(state));
57,624✔
2160
        state.sx = s_iter;
57,624✔
2161
        rc = _db_tree_add(&s_iter->chains, s_new->chains, &state);
57,624✔
2162
        if (rc < 0)
57,624✔
2163
                goto add_failure;
34✔
2164
        s_iter->node_cnt += s_new->node_cnt;
57,590✔
2165
        s_iter->node_cnt -= _db_tree_put(&s_new->chains);
57,590✔
2166
        free(s_new);
57,590✔
2167

2168
add_free_ok:
2169
        rc = 0;
2170
add_priority_update:
65,474✔
2171
        /* update the priority */
2172
        if (s_iter != NULL) {
65,474✔
2173
                s_iter->priority &= (~_DB_PRI_MASK_CHAIN);
65,474✔
2174
                s_iter->priority |= (_DB_PRI_MASK_CHAIN - s_iter->node_cnt);
65,474✔
2175
        }
2176
        return rc;
65,474✔
2177

2178
add_failure:
34✔
2179
        /* NOTE: another reminder that we don't do any db error recovery here,
2180
         * use the transaction mechanism as previously mentioned */
2181
        _db_tree_put(&s_new->chains);
34✔
2182
        free(s_new);
34✔
2183
        return rc;
34✔
2184
}
2185

2186
/**
2187
 * Set the priority of a given syscall
2188
 * @param col the filter collection
2189
 * @param syscall the syscall number
2190
 * @param priority priority value, higher value == higher priority
2191
 *
2192
 * This function sets the priority of the given syscall; this value is used
2193
 * when generating the seccomp filter code such that higher priority syscalls
2194
 * will incur less filter code overhead than the lower priority syscalls in the
2195
 * filter.  Returns zero on success, negative values on failure.
2196
 *
2197
 */
2198
int db_col_syscall_priority(struct db_filter_col *col,
68✔
2199
                            int syscall, uint8_t priority)
2200
{
2201
        int rc = 0, rc_tmp;
68✔
2202
        unsigned int iter;
68✔
2203
        int sc_tmp;
68✔
2204
        struct db_filter *filter;
68✔
2205

2206
        for (iter = 0; iter < col->filter_cnt; iter++) {
148✔
2207
                filter = col->filters[iter];
80✔
2208
                sc_tmp = syscall;
80✔
2209

2210
                rc_tmp = arch_syscall_translate(filter->arch, &sc_tmp);
80✔
2211
                if (rc_tmp < 0)
80✔
2212
                        goto priority_failure;
×
2213

2214
                /* if this is a pseudo syscall then we need to rewrite the
2215
                 * syscall for some arch specific reason, don't forget the
2216
                 * special handling for syscall -1 */
2217
                if (sc_tmp < -1) {
80✔
2218
                        /* we set this as a strict op - we don't really care
2219
                         * since priorities are a "best effort" thing - as we
2220
                         * want to catch the -EDOM error and bail on this
2221
                         * architecture */
2222
                        rc_tmp = arch_syscall_rewrite(filter->arch, &sc_tmp);
6✔
2223
                        if (rc_tmp == -EDOM)
6✔
2224
                                continue;
2✔
2225
                        if (rc_tmp < 0)
4✔
2226
                                goto priority_failure;
×
2227
                }
2228

2229
                rc_tmp = _db_syscall_priority(filter, sc_tmp, priority);
78✔
2230

2231
priority_failure:
78✔
2232
                if (rc == 0 && rc_tmp < 0)
78✔
2233
                        rc = rc_tmp;
×
2234
        }
2235

2236
        return rc;
68✔
2237
}
2238

2239
/**
2240
 * Add a new rule to a single filter
2241
 * @param filter the filter
2242
 * @param rule the filter rule
2243
 *
2244
 * This is a helper function for db_col_rule_add() and similar functions, it
2245
 * isn't generally useful.  Returns zero on success, negative values on error.
2246
 *
2247
 */
2248
static int _db_col_rule_add(struct db_filter *filter,
200,284✔
2249
                            struct db_api_rule_list *rule)
2250
{
2251
        int rc;
200,284✔
2252
        struct db_api_rule_list *iter;
200,284✔
2253

2254
        /* add the rule to the filter */
2255
        rc = arch_filter_rule_add(filter, rule);
200,284✔
2256
        if (rc != 0)
200,284✔
2257
                return rc;
2258

2259
        /* insert the chain to the end of the rule list */
2260
        iter = rule;
2261
        while (iter->next)
199,871✔
2262
                iter = iter->next;
2263
        if (filter->rules != NULL) {
199,871✔
2264
                rule->prev = filter->rules->prev;
172,458✔
2265
                iter->next = filter->rules;
172,458✔
2266
                filter->rules->prev->next = rule;
172,458✔
2267
                filter->rules->prev = iter;
172,458✔
2268
        } else {
2269
                rule->prev = iter;
27,413✔
2270
                iter->next = rule;
27,413✔
2271
                filter->rules = rule;
27,413✔
2272
        }
2273

2274
        return 0;
2275
}
2276

2277
/**
2278
 * Add a new rule to the current filter
2279
 * @param col the filter collection
2280
 * @param strict the strict flag
2281
 * @param action the filter action
2282
 * @param syscall the syscall number
2283
 * @param arg_cnt the number of argument filters in the argument filter chain
2284
 * @param arg_array the argument filter chain, (uint, enum scmp_compare, ulong)
2285
 *
2286
 * This function adds a new argument/comparison/value to the seccomp filter for
2287
 * a syscall; multiple arguments can be specified and they will be chained
2288
 * together (essentially AND'd together) in the filter.  When the strict flag
2289
 * is true the function will fail if the exact rule can not be added to the
2290
 * filter, if the strict flag is false the function will not fail if the
2291
 * function needs to adjust the rule due to architecture specifics.  Returns
2292
 * zero on success, negative values on failure.
2293
 *
2294
 */
2295
int db_col_rule_add(struct db_filter_col *col,
50,524✔
2296
                    bool strict, uint32_t action, int syscall,
2297
                    unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
2298
{
2299
        int rc = 0, rc_tmp;
50,524✔
2300
        unsigned int iter;
50,524✔
2301
        unsigned int arg_num;
50,524✔
2302
        size_t chain_size;
50,524✔
2303
        struct db_api_arg *chain = NULL;
50,524✔
2304
        struct scmp_arg_cmp arg_data;
50,524✔
2305
        struct db_api_rule_list *rule;
50,524✔
2306
        struct db_filter *db;
50,524✔
2307

2308
        /* collect the arguments for the filter rule */
2309
        chain_size = sizeof(*chain) * ARG_COUNT_MAX;
50,524✔
2310
        chain = zmalloc(chain_size);
50,524✔
2311
        if (chain == NULL)
50,524✔
2312
                return -ENOMEM;
2313
        for (iter = 0; iter < arg_cnt; iter++) {
121,731✔
2314
                arg_data = arg_array[iter];
71,209✔
2315
                arg_num = arg_data.arg;
71,209✔
2316
                if (arg_num < ARG_COUNT_MAX && chain[arg_num].valid == 0) {
71,209✔
2317
                        chain[arg_num].valid = 1;
71,209✔
2318
                        chain[arg_num].arg = arg_num;
71,209✔
2319
                        chain[arg_num].op = arg_data.op;
71,209✔
2320
                        /* TODO: we should check datum/mask size against the
2321
                         *         arch definition, e.g. 64 bit datum on x86 */
2322
                        switch (chain[arg_num].op) {
71,209✔
2323
                        case SCMP_CMP_NE:
65,534✔
2324
                        case SCMP_CMP_LT:
2325
                        case SCMP_CMP_LE:
2326
                        case SCMP_CMP_EQ:
2327
                        case SCMP_CMP_GE:
2328
                        case SCMP_CMP_GT:
2329
                                chain[arg_num].mask = DATUM_MAX;
65,534✔
2330
                                chain[arg_num].datum = arg_data.datum_a;
65,534✔
2331
                                break;
65,534✔
2332
                        case SCMP_CMP_MASKED_EQ:
5,673✔
2333
                                chain[arg_num].mask = arg_data.datum_a;
5,673✔
2334
                                chain[arg_num].datum = arg_data.datum_b;
5,673✔
2335
                                break;
5,673✔
2336
                        default:
2✔
2337
                                rc = -EINVAL;
2✔
2338
                                goto add_return;
2✔
2339
                        }
2340
                } else {
2341
                        rc = -EINVAL;
×
2342
                        goto add_return;
×
2343
                }
2344
        }
2345

2346
        /* create a checkpoint */
2347
        rc = db_col_transaction_start(col);
50,522✔
2348
        if (rc != 0)
50,522✔
2349
                goto add_return;
×
2350

2351
        /* add the rule to the different filters in the collection */
2352
        for (iter = 0; iter < col->filter_cnt; iter++) {
150,813✔
2353
                db = col->filters[iter];
100,291✔
2354

2355
                /* create the rule */
2356
                rule = _db_rule_new(strict, action, syscall, chain);
100,291✔
2357
                if (rule == NULL) {
100,291✔
2358
                        rc_tmp = -ENOMEM;
×
2359
                        goto add_arch_fail;
×
2360
                }
2361

2362
                /* add the rule */
2363
                rc_tmp = _db_col_rule_add(db, rule);
100,291✔
2364
                if (rc_tmp != 0)
100,291✔
2365
                        free(rule);
413✔
2366

2367
add_arch_fail:
99,878✔
2368
                if (rc_tmp != 0 && rc == 0)
100,291✔
2369
                        rc = rc_tmp;
413✔
2370
        }
2371

2372
        /* commit the transaction or abort */
2373
        if (rc == 0)
50,522✔
2374
                db_col_transaction_commit(col);
50,109✔
2375
        else
2376
                db_col_transaction_abort(col);
413✔
2377

2378
add_return:
50,524✔
2379
        /* update the misc state */
2380
        if (rc == 0 && action == SCMP_ACT_NOTIFY)
50,524✔
2381
                col->notify_used = true;
×
2382
        if (chain != NULL)
50,524✔
2383
                free(chain);
50,524✔
2384
        return rc;
50,524✔
2385
}
2386

2387
/**
2388
 * Start a new seccomp filter transaction
2389
 * @param col the filter collection
2390
 *
2391
 * This function starts a new seccomp filter transaction for the given filter
2392
 * collection.  Returns zero on success, negative values on failure.
2393
 *
2394
 */
2395
int db_col_transaction_start(struct db_filter_col *col)
50,522✔
2396
{
2397
        int rc;
50,522✔
2398
        unsigned int iter;
50,522✔
2399
        struct db_filter_snap *snap;
50,522✔
2400
        struct db_filter *filter_o, *filter_s;
50,522✔
2401
        struct db_api_rule_list *rule_o, *rule_s = NULL;
50,522✔
2402

2403
        /* check to see if a shadow snapshot exists */
2404
        if (col->snapshots && col->snapshots->shadow) {
50,522✔
2405
                /* we have a shadow!  this will be easy */
2406

2407
                /* NOTE: we don't bother to do any verification of the shadow
2408
                 *       because we start a new transaction every time we add
2409
                 *       a new rule to the filter(s); if this ever changes we
2410
                 *       will need to add a mechanism to verify that the shadow
2411
                 *       transaction is current/correct */
2412

2413
                col->snapshots->shadow = false;
43,571✔
2414
                return 0;
43,571✔
2415
        }
2416

2417
        /* allocate the snapshot */
2418
        snap = zmalloc(sizeof(*snap));
6,951✔
2419
        if (snap == NULL)
6,951✔
2420
                return -ENOMEM;
2421
        snap->filters = zmalloc(sizeof(struct db_filter *) * col->filter_cnt);
6,951✔
2422
        if (snap->filters == NULL) {
6,951✔
2423
                free(snap);
×
2424
                return -ENOMEM;
×
2425
        }
2426
        snap->filter_cnt = col->filter_cnt;
6,951✔
2427
        for (iter = 0; iter < snap->filter_cnt; iter++)
20,683✔
2428
                snap->filters[iter] = NULL;
13,732✔
2429
        snap->next = NULL;
6,951✔
2430

2431
        /* create a snapshot of the current filter state */
2432
        for (iter = 0; iter < col->filter_cnt; iter++) {
20,683✔
2433
                /* allocate a new filter */
2434
                filter_o = col->filters[iter];
13,732✔
2435
                filter_s = _db_init(filter_o->arch);
13,732✔
2436
                if (filter_s == NULL)
13,732✔
2437
                        goto trans_start_failure;
×
2438
                snap->filters[iter] = filter_s;
13,732✔
2439

2440
                /* create a filter snapshot from existing rules */
2441
                rule_o = filter_o->rules;
13,732✔
2442
                if (rule_o == NULL)
13,732✔
2443
                        continue;
13,685✔
2444
                do {
115✔
2445
                        /* duplicate the rule */
2446
                        rule_s = db_rule_dup(rule_o);
115✔
2447
                        if (rule_s == NULL)
115✔
2448
                                goto trans_start_failure;
×
2449

2450
                        /* add the rule */
2451
                        rc = _db_col_rule_add(filter_s, rule_s);
115✔
2452
                        if (rc != 0)
115✔
2453
                                goto trans_start_failure;
×
2454
                        rule_s = NULL;
115✔
2455

2456
                        /* next rule */
2457
                        rule_o = rule_o->next;
115✔
2458
                } while (rule_o != filter_o->rules);
115✔
2459
        }
2460

2461
        /* add the snapshot to the list */
2462
        snap->next = col->snapshots;
6,951✔
2463
        col->snapshots = snap;
6,951✔
2464

2465
        return 0;
6,951✔
2466

2467
trans_start_failure:
×
2468
        if (rule_s != NULL)
×
2469
                free(rule_s);
×
2470
        _db_snap_release(snap);
×
2471
        return -ENOMEM;
×
2472
}
2473

2474
/**
2475
 * Abort the top most seccomp filter transaction
2476
 * @param col the filter collection
2477
 *
2478
 * This function aborts the most recent seccomp filter transaction.
2479
 *
2480
 */
2481
void db_col_transaction_abort(struct db_filter_col *col)
413✔
2482
{
2483
        int iter;
413✔
2484
        unsigned int filter_cnt;
413✔
2485
        struct db_filter **filters;
413✔
2486
        struct db_filter_snap *snap;
413✔
2487

2488
        if (col->snapshots == NULL)
413✔
2489
                return;
2490

2491
        /* replace the current filter with the last snapshot */
2492
        snap = col->snapshots;
413✔
2493
        col->snapshots = snap->next;
413✔
2494
        filter_cnt = col->filter_cnt;
413✔
2495
        filters = col->filters;
413✔
2496
        col->filter_cnt = snap->filter_cnt;
413✔
2497
        col->filters = snap->filters;
413✔
2498
        free(snap);
413✔
2499

2500
        /* free the filter we swapped out */
2501
        for (iter = 0; iter < filter_cnt; iter++)
826✔
2502
                _db_release(filters[iter]);
826✔
2503
        free(filters);
413✔
2504
}
2505

2506
/**
2507
 * Commit the top most seccomp filter transaction
2508
 * @param col the filter collection
2509
 *
2510
 * This function commits the most recent seccomp filter transaction and
2511
 * attempts to create a shadow transaction that is a duplicate of the current
2512
 * filter to speed up future transactions.
2513
 *
2514
 */
2515
void db_col_transaction_commit(struct db_filter_col *col)
50,109✔
2516
{
2517
        int rc;
50,109✔
2518
        unsigned int iter;
50,109✔
2519
        struct db_filter_snap *snap;
50,109✔
2520
        struct db_filter *filter_o, *filter_s;
50,109✔
2521
        struct db_api_rule_list *rule_o, *rule_s;
50,109✔
2522

2523
        snap = col->snapshots;
50,109✔
2524
        if (snap == NULL)
50,109✔
2525
                return;
2526

2527
        /* check for a shadow set by a higher transaction commit */
2528
        if (snap->shadow) {
50,109✔
2529
                /* leave the shadow intact, but drop the next snapshot */
2530
                if (snap->next) {
×
2531
                        snap->next = snap->next->next;
×
2532
                        _db_snap_release(snap->next);
×
2533
                }
2534
                return;
×
2535
        }
2536

2537
        /* adjust the number of filters if needed */
2538
        if (col->filter_cnt > snap->filter_cnt) {
50,109✔
2539
                unsigned int tmp_i;
×
2540
                struct db_filter **tmp_f;
×
2541

2542
                /* add filters */
2543
                tmp_f = realloc(snap->filters,
×
2544
                                sizeof(struct db_filter *) * col->filter_cnt);
×
2545
                if (tmp_f == NULL)
×
2546
                        goto shadow_err;
×
2547
                snap->filters = tmp_f;
×
2548
                do {
×
2549
                        tmp_i = snap->filter_cnt;
×
2550
                        snap->filters[tmp_i] =
×
2551
                                _db_init(col->filters[tmp_i]->arch);
×
2552
                        if (snap->filters[tmp_i] == NULL)
×
2553
                                goto shadow_err;
×
2554
                        snap->filter_cnt++;
×
2555
                } while (snap->filter_cnt < col->filter_cnt);
×
2556
        } else if (col->filter_cnt < snap->filter_cnt) {
50,109✔
2557
                /* remove filters */
2558

2559
                /* NOTE: while we release the filters we no longer need, we
2560
                 *       don't bother to resize the filter array, we just
2561
                 *       adjust the filter counter, this *should* be harmless
2562
                 *       at the cost of a not reaping all the memory possible */
2563

2564
                do {
×
2565
                        _db_release(snap->filters[snap->filter_cnt--]);
×
2566
                } while (snap->filter_cnt > col->filter_cnt);
×
2567
        }
2568

2569
        /* loop through each filter and update the rules on the snapshot */
2570
        for (iter = 0; iter < col->filter_cnt; iter++) {
149,987✔
2571
                filter_o = col->filters[iter];
99,878✔
2572
                filter_s = snap->filters[iter];
99,878✔
2573

2574
                /* skip ahead to the new rule(s) */
2575
                rule_o = filter_o->rules;
99,878✔
2576
                rule_s = filter_s->rules;
99,878✔
2577
                if (rule_o == NULL)
99,878✔
2578
                        /* nothing to shadow */
2579
                        continue;
×
2580
                if (rule_s != NULL) {
99,878✔
2581
                        do {
875,601✔
2582
                                rule_o = rule_o->next;
875,601✔
2583
                                rule_s = rule_s->next;
875,601✔
2584
                        } while (rule_s != filter_s->rules);
875,601✔
2585

2586
                        /* did we actually add any rules? */
2587
                        if (rule_o == filter_o->rules)
86,195✔
2588
                                /* no, we are done in this case */
2589
                                continue;
×
2590
                }
2591

2592
                /* update the old snapshot to make it a shadow */
2593
                do {
99,878✔
2594
                        /* duplicate the rule */
2595
                        rule_s = db_rule_dup(rule_o);
99,878✔
2596
                        if (rule_s == NULL)
99,878✔
2597
                                goto shadow_err;
×
2598

2599
                        /* add the rule */
2600
                        rc = _db_col_rule_add(filter_s, rule_s);
99,878✔
2601
                        if (rc != 0) {
99,878✔
2602
                                free(rule_s);
×
2603
                                goto shadow_err;
×
2604
                        }
2605

2606
                        /* next rule */
2607
                        rule_o = rule_o->next;
99,878✔
2608
                } while (rule_o != filter_o->rules);
99,878✔
2609
        }
2610

2611
        /* success, mark the snapshot as a shadow and return */
2612
        snap->shadow = true;
50,109✔
2613
        return;
50,109✔
2614

2615
shadow_err:
×
2616
        /* we failed making a shadow, cleanup and return */
2617
        col->snapshots = snap->next;
×
2618
        _db_snap_release(snap);
×
2619
        return;
×
2620
}
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