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

drakenclimber / libseccomp / 12716868856

10 Jan 2025 08:37PM UTC coverage: 87.592% (-0.08%) from 87.676%
12716868856

push

github

drakenclimber
bpf: pfc: Add maximum supported kernel version logic

Add support for an application to specify the maximum kernel version it
currently supports.  Any syscalls that have been added to a kernel
version newer than this specified version will return the unknown
action.  The unknown action defaults to returning ENOSYS, but it can be
overridden via the filter attribute SCMP_FLTATR_ACT_UNKNOWN.

When the maximum supported kernel version is enabled, libseccomp will
create a filter as follows:
	* Users explicitly declare rules for syscalls.  No changes here
	  from previous behavior
	* The default action provided via seccomp_init() will still be
	  used for all syscalls that existed as of the user-specified
	  supported kernel
	* Any syscalls that did not exist at the time of the
	  user-specified supported kernel will return the unknown
	  action.  By default libseccomp sets this to return ENOSYS, but
	  it can be overridden via the filter attribute
	  SCMP_FLTATR_ACT_UNKNOWN.

Below is a rough pseudo-code outline of a typical usage of this feature:
	seccomp_init()
	seccomp_add_rules()

	(optional but recommended) seccomp_attr_set( binary tree )
	seccomp_attr_set( max supported kernel version, e.g. SCMP_KV_6_5 )
	(optional) seccomp_attr_set( default unknown action )

	seccomp_load()
	seccomp_release()

Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>

8 of 65 new or added lines in 5 files covered. (12.31%)

96 existing lines in 4 files now uncovered.

2739 of 3127 relevant lines covered (87.59%)

288204.54 hits per line

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

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

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

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

31
#include <seccomp.h>
32

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

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

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

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

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

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

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

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

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

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

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

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

159
        return false;
160
}
161

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

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

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

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

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

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

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

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

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

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

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

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

260
        return cnt;
38,688✔
261
}
262

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

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

279
        return cnt;
1,132,390✔
280
}
281

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

297
        if (n == NULL)
1,638,190✔
298
                return 0;
299

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

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

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

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

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

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

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

342
        return cnt;
343
}
344

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

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

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

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

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

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

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

384
        return cnt;
385

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

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

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

406
        return cnt;
11,440✔
407
}
408

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

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

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

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

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

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

447
        return 0;
448
}
449

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

815
        return 0;
816
}
817

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

831
        if (db == NULL)
75,284✔
832
                return;
833

834
        /* free any filters */
835
        if (db->syscalls != NULL) {
75,284✔
836
                s_iter = db->syscalls;
837
                while (s_iter != NULL) {
242,413✔
838
                        db->syscalls = s_iter->next;
208,475✔
839
                        _db_tree_put(&s_iter->chains);
208,475✔
840
                        free(s_iter);
208,475✔
841
                        s_iter = db->syscalls;
208,475✔
842
                }
843
                db->syscalls = NULL;
33,938✔
844
        }
845
        db->syscall_cnt = 0;
75,284✔
846

847
        /* free any rules */
848
        if (db->rules != NULL) {
75,284✔
849
                /* split the loop first then loop and free */
850
                db->rules->prev->next = NULL;
34,640✔
851
                r_iter = db->rules;
34,640✔
852
                while (r_iter != NULL) {
297,556✔
853
                        db->rules = r_iter->next;
262,916✔
854
                        free(r_iter);
262,916✔
855
                        r_iter = db->rules;
262,916✔
856
                }
857
                db->rules = NULL;
34,640✔
858
        }
859
}
860

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

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

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

881
        return db;
37,642✔
882
}
883

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

897
        /* free and reset the DB */
898
        _db_reset(db);
15,761✔
899
        free(db);
32,348✔
900
}
901

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

914
        if (snap == NULL)
6,392✔
915
                return;
916

917
        if (snap->filter_cnt > 0) {
6,392✔
918
                for (iter = 0; iter < snap->filter_cnt; iter++) {
22,979✔
919
                        if (snap->filters[iter])
16,587✔
920
                                _db_release(snap->filters[iter]);
33,174✔
921
                }
922
                free(snap->filters);
6,392✔
923
        }
924
        free(snap);
6,392✔
925
}
926

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

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

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

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

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

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

980
        return 0;
981
}
982

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

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

1008
        return rule;
131,806✔
1009
}
1010

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

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

1031
        return dest;
419,992✔
1032
}
1033

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

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

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

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

1065
        /* set the default attribute values */
1066
        col->attr.act_default = def_action;
8,186✔
1067
        col->attr.act_badarch = SCMP_ACT_KILL;
8,186✔
1068
        col->attr.nnp_enable = 1;
8,186✔
1069
        col->attr.tsync_enable = 0;
8,186✔
1070
        col->attr.api_tskip = 0;
8,186✔
1071
        col->attr.log_enable = 0;
8,186✔
1072
        col->attr.spec_allow = 0;
8,186✔
1073
        col->attr.optimize = 1;
8,186✔
1074
        col->attr.api_sysrawrc = 0;
8,186✔
1075
        col->attr.wait_killable_recv = 0;
8,186✔
1076
        col->attr.act_unknown = SCMP_ACT_ERRNO(38); /* ENOSYS */
8,186✔
1077
        col->attr.kver = SCMP_KV_UNDEF;
8,186✔
1078

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

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

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

1105
        /* reset the precomputed programs */
1106
        db_col_precompute_reset(col);
16,372✔
1107

1108
        return 0;
1109
}
1110

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

1123
        col = zmalloc(sizeof(*col));
7,821✔
1124
        if (col == NULL)
7,821✔
1125
                return NULL;
1126

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

1133
        return col;
1134
}
1135

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

1149
        if (col == NULL)
7,821✔
1150
                return;
1151

1152
        /* set the state, just in case */
1153
        col->state = _DB_STA_FREED;
7,821✔
1154

1155
        /* free any snapshots */
1156
        while (col->snapshots != NULL) {
14,213✔
1157
                snap = col->snapshots;
6,392✔
1158
                col->snapshots = snap->next;
6,392✔
1159
                _db_snap_release(snap);
6,392✔
1160
        }
1161

1162
        /* free any filters */
1163
        for (iter = 0; iter < col->filter_cnt; iter++)
22,453✔
1164
                _db_release(col->filters[iter]);
29,264✔
1165
        col->filter_cnt = 0;
7,821✔
1166
        if (col->filters)
7,821✔
1167
                free(col->filters);
7,539✔
1168
        col->filters = NULL;
7,821✔
1169

1170
        /* free any precompute */
1171
        db_col_precompute_reset(col);
15,642✔
1172

1173
        /* free the collection */
1174
        free(col);
7,821✔
1175
}
1176

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

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

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

1213
        if (sys_chk_seccomp_action(action) == 1)
61,281✔
1214
                return 0;
61,277✔
1215
        return -EINVAL;
1216
}
1217

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

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

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

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

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

1261
        /* reset the precompute */
1262
        db_col_precompute_reset(col_dst);
52✔
1263

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

1268
        return 0;
26✔
1269
}
1270

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

1285
        for (iter = 0; iter < col->filter_cnt; iter++)
154,528✔
1286
                if (col->filters[iter]->arch->token == arch_token)
121,850✔
1287
                        return -EEXIST;
1288

1289
        return 0;
1290
}
1291

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

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

1349
        return rc;
15✔
1350
}
1351

1352
/**
1353
 * Get a filter attribute
1354
 * @param col the seccomp filter collection
1355
 * @param attr the filter attribute
1356
 *
1357
 * Returns the requested filter attribute value with zero on any error.
1358
 * Special care must be given with this function as error conditions can be
1359
 * hidden from the caller.
1360
 *
1361
 */
1362
uint32_t db_col_attr_read(const struct db_filter_col *col,
4✔
1363
                          enum scmp_filter_attr attr)
1364
{
1365
        uint32_t value = 0;
4✔
1366

1367
        db_col_attr_get(col, attr, &value);
4✔
1368
        return value;
4✔
1369
}
1370

1371
/**
1372
 * Set a filter attribute
1373
 * @param col the seccomp filter collection
1374
 * @param attr the filter attribute
1375
 * @param value the filter attribute value
1376
 *
1377
 * Set the requested filter attribute with the given value.  Returns zero on
1378
 * success, negative values on failure.
1379
 *
1380
 */
1381
int db_col_attr_set(struct db_filter_col *col,
529✔
1382
                    enum scmp_filter_attr attr, uint32_t value)
1383
{
1384
        int rc = 0, iter;
529✔
1385

1386
        switch (attr) {
529✔
1387
        case SCMP_FLTATR_ACT_DEFAULT:
1388
                /* read only */
1389
                return -EACCES;
1390
                break;
1✔
1391
        case SCMP_FLTATR_ACT_BADARCH:
1✔
1392
                if (db_col_action_valid(col, value) == 0)
1✔
1393
                        col->attr.act_badarch = value;
1✔
1394
                else
1395
                        return -EINVAL;
1396
                db_col_precompute_reset(col);
530✔
1397
                break;
1398
        case SCMP_FLTATR_CTL_NNP:
1✔
1399
                col->attr.nnp_enable = (value ? 1 : 0);
1✔
1400
                break;
1✔
1401
        case SCMP_FLTATR_CTL_TSYNC:
2✔
1402
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC);
2✔
1403
                if (rc == 1) {
2✔
1404
                        /* supported */
1405
                        rc = 0;
2✔
1406
                        /* kernel disallows TSYNC and NOTIFY in one filter
1407
                         * unless we have TSYNC_ESRCH */
1408
                        if (sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH) < 1 &&
2✔
1409
                            value && col->notify_used)
1✔
1410
                                return -EINVAL;
1411
                        col->attr.tsync_enable = (value ? 1 : 0);
2✔
1412
                } else if (rc == 0)
×
1413
                        /* unsupported */
1414
                        rc = -EOPNOTSUPP;
×
1415
                break;
1416
        case SCMP_FLTATR_API_TSKIP:
4✔
1417
                col->attr.api_tskip = (value ? 1 : 0);
4✔
1418
                db_col_precompute_reset(col);
533✔
1419
                break;
1420
        case SCMP_FLTATR_CTL_LOG:
2✔
1421
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_LOG);
2✔
1422
                if (rc == 1) {
2✔
1423
                        /* supported */
1424
                        rc = 0;
2✔
1425
                        col->attr.log_enable = (value ? 1 : 0);
2✔
1426
                } else if (rc == 0) {
×
1427
                        /* unsupported */
1428
                        rc = -EOPNOTSUPP;
×
1429
                }
1430
                break;
1431
        case SCMP_FLTATR_CTL_SSB:
2✔
1432
                rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW);
2✔
1433
                if (rc == 1) {
2✔
1434
                        /* supported */
1435
                        rc = 0;
2✔
1436
                        col->attr.spec_allow = (value ? 1 : 0);
2✔
1437
                } else if (rc == 0) {
×
1438
                        /* unsupported */
1439
                        rc = -EOPNOTSUPP;
×
1440
                }
1441
                break;
1442
        case SCMP_FLTATR_CTL_OPTIMIZE:
512✔
1443
                switch (value) {
512✔
1444
                case 1:
512✔
1445
                case 2:
1446
                        col->attr.optimize = value;
512✔
1447
                        break;
512✔
1448
                default:
1449
                        rc = -EOPNOTSUPP;
1450
                        break;
1451
                }
1452
                db_col_precompute_reset(col);
1,041✔
1453
                break;
1454
        case SCMP_FLTATR_API_SYSRAWRC:
2✔
1455
                col->attr.api_sysrawrc = (value ? 1 : 0);
2✔
1456
                break;
2✔
1457
        case SCMP_FLTATR_CTL_WAITKILL:
1✔
1458
                col->attr.wait_killable_recv = (value ? 1 : 0);
1✔
1459
                break;
1✔
1460
        case SCMP_FLTATR_ACT_UNKNOWN:
×
1461
                if (db_col_action_valid(col, value) == 0)
×
1462
                        col->attr.act_unknown = value;
×
1463
                else
1464
                        return -EINVAL;
1465
                db_col_precompute_reset(col);
×
NEW
1466
                rc = db_add_known_syscalls(col);
×
1467
                break;
×
1468
        case SCMP_FLTATR_CTL_KVER:
×
1469
                if (value <= SCMP_KV_UNDEF || value >=__SCMP_KV_MAX)
×
1470
                        return -EINVAL;
NEW
1471
                for (iter = 0; iter < col->filter_cnt; iter++) {
×
NEW
1472
                        if (col->filters[iter]->arch->token != SCMP_ARCH_X32 &&
×
NEW
1473
                            col->filters[iter]->arch->token != SCMP_ARCH_X86 &&
×
1474
                            col->filters[iter]->arch->token != SCMP_ARCH_X86_64)
1475
                                return -EOPNOTSUPP;
1476
                }
1477

1478
                col->attr.kver = value;
×
1479

1480
                /* Create a rule for every syscall that existed up to and
1481
                 * prior to attr.kver.  libseccomp capably handles duplicate
1482
                 * rules for a syscall, so if the user adds more rules after
1483
                 * setting this attribute, libseccomp will simply replace
1484
                 * the rules added by the unknown-syscall logic */
NEW
1485
                rc = db_add_known_syscalls(col);
×
UNCOV
1486
                break;
×
1487
        default:
1488
                rc = -EINVAL;
1489
                break;
1490
        }
1491

1492
        return rc;
1493
}
1494

1495
/**
1496
 * Add a new architecture filter to a filter collection
1497
 * @param col the seccomp filter collection
1498
 * @param arch the architecture
1499
 *
1500
 * This function adds a new architecture filter DB to an existing seccomp
1501
 * filter collection assuming there isn't a filter DB already present with the
1502
 * same architecture.  Returns zero on success, negative values on failure.
1503
 *
1504
 */
1505
int db_col_db_new(struct db_filter_col *col, const struct arch_def *arch)
12,105✔
1506
{
1507
        int rc;
12,105✔
1508
        struct db_filter *db;
12,105✔
1509

1510
        db = _db_init(arch);
12,105✔
1511
        if (db == NULL)
12,105✔
1512
                return -ENOMEM;
1513
        rc = db_col_db_add(col, db);
12,105✔
1514
        if (rc < 0)
12,105✔
UNCOV
1515
                _db_release(db);
×
1516
        else
1517
                db_col_precompute_reset(col);
24,210✔
1518

1519
        return rc;
1520
}
1521

1522
/**
1523
 * Add a new filter DB to a filter collection
1524
 * @param col the seccomp filter collection
1525
 * @param db the seccomp filter DB
1526
 *
1527
 * This function adds an existing seccomp filter DB to an existing seccomp
1528
 * filter collection assuming there isn't a filter DB already present with the
1529
 * same architecture.  Returns zero on success, negative values on failure.
1530
 *
1531
 */
1532
int db_col_db_add(struct db_filter_col *col, struct db_filter *db)
20,291✔
1533
{
1534
        struct db_filter **dbs;
20,291✔
1535

1536
        if (col->endian != 0 && col->endian != db->arch->endian)
20,291✔
1537
                return -EDOM;
1538

1539
        if (db_col_arch_exist(col, db->arch->token))
40,582✔
1540
                return -EEXIST;
1541

1542
        dbs = realloc(col->filters,
20,291✔
1543
                      sizeof(struct db_filter *) * (col->filter_cnt + 1));
20,291✔
1544
        if (dbs == NULL)
20,291✔
1545
                return -ENOMEM;
1546
        col->filters = dbs;
20,291✔
1547
        col->filter_cnt++;
20,291✔
1548
        col->filters[col->filter_cnt - 1] = db;
20,291✔
1549
        if (col->endian == 0)
20,291✔
1550
                col->endian = db->arch->endian;
10,096✔
1551

1552
        return 0;
1553
}
1554

1555
/**
1556
 * Remove a filter DB from a filter collection
1557
 * @param col the seccomp filter collection
1558
 * @param arch_token the architecture token
1559
 *
1560
 * This function removes an existing seccomp filter DB from an existing seccomp
1561
 * filter collection.  Returns zero on success, negative values on failure.
1562
 *
1563
 */
1564
int db_col_db_remove(struct db_filter_col *col, uint32_t arch_token)
5,294✔
1565
{
1566
        unsigned int iter;
5,294✔
1567
        unsigned int found;
5,294✔
1568
        struct db_filter **dbs;
5,294✔
1569

1570
        if ((col->filter_cnt <= 0) || (db_col_arch_exist(col, arch_token) == 0))
10,588✔
1571
                return -EINVAL;
1572

1573
        for (found = 0, iter = 0; iter < col->filter_cnt; iter++) {
29,200✔
1574
                if (found)
23,906✔
1575
                        col->filters[iter - 1] = col->filters[iter];
18,612✔
1576
                else if (col->filters[iter]->arch->token == arch_token) {
5,294✔
1577
                        _db_release(col->filters[iter]);
5,294✔
1578
                        found = 1;
5,294✔
1579
                }
1580
        }
1581
        col->filters[--col->filter_cnt] = NULL;
5,294✔
1582

1583
        if (col->filter_cnt > 0) {
5,294✔
1584
                /* NOTE: if we can't do the realloc it isn't fatal, we just
1585
                 *       have some extra space allocated */
1586
                dbs = realloc(col->filters,
3,102✔
1587
                              sizeof(struct db_filter *) * col->filter_cnt);
1588
                if (dbs != NULL)
3,102✔
1589
                        col->filters = dbs;
3,102✔
1590
        } else {
1591
                /* this was the last filter so free all the associated memory
1592
                 * and reset the endian token */
1593
                free(col->filters);
2,192✔
1594
                col->filters = NULL;
2,192✔
1595
                col->endian = 0;
2,192✔
1596
        }
1597

1598
        db_col_precompute_reset(col);
10,588✔
1599

1600
        return 0;
1601
}
1602

1603
/**
1604
 * Test if the argument filter can be skipped because it's a tautology
1605
 * @param arg argument filter
1606
 *
1607
 * If this argument filter applied to the lower 32 bit can be skipped this
1608
 * function returns false.
1609
 *
1610
 */
1611
static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg)
61,776✔
1612
{
1613
        if (arg->op == SCMP_CMP_MASKED_EQ && D64_LO(arg->mask) == 0)
12✔
1614
                return false;
68✔
1615

1616
        return true;
1617
}
1618

1619
/**
1620
 * Test if the argument filter can be skipped because it's a tautology
1621
 * @param arg argument filter
1622
 *
1623
 * If this argument filter applied to the upper 32 bit can be skipped this
1624
 * function returns false.
1625
 *
1626
 */
1627
static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg)
183,082✔
1628
{
1629
        if (arg->op == SCMP_CMP_MASKED_EQ && D64_HI(arg->mask) == 0)
11,356✔
1630
                return false;
11,356✔
1631

1632
        return true;
1633
}
1634

1635
/**
1636
 * Fixup the node based on the op/mask
1637
 * @param node the chain node
1638
 *
1639
 * Ensure the datum is masked as well.
1640
 *
1641
 */
1642
static void _db_node_mask_fixup(struct db_arg_chain_tree *node)
233,434✔
1643
{
1644
        node->datum &= node->mask;
233,434✔
1645
}
1646

1647
/**
1648
 * Generate a new filter rule for a 64 bit system
1649
 * @param arch the architecture definition
1650
 * @param rule the new filter rule
1651
 *
1652
 * This function generates a new syscall filter for a 64 bit system. Returns
1653
 * zero on success, negative values on failure.
1654
 *
1655
 */
1656
static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
191,906✔
1657
                                           const struct db_api_rule_list *rule)
1658
{
1659
        unsigned int iter;
191,906✔
1660
        struct db_sys_list *s_new;
191,906✔
1661
        const struct db_api_arg *chain = rule->args;
191,906✔
1662
        struct db_arg_chain_tree *c_iter[3] = { NULL, NULL, NULL };
191,906✔
1663
        struct db_arg_chain_tree *c_prev[3] = { NULL, NULL, NULL };
191,906✔
1664
        enum scmp_compare op_prev = _SCMP_CMP_MIN;
191,906✔
1665
        unsigned int arg;
191,906✔
1666
        scmp_datum_t mask;
191,906✔
1667
        scmp_datum_t datum;
191,906✔
1668

1669
        s_new = zmalloc(sizeof(*s_new));
191,906✔
1670
        if (s_new == NULL)
191,906✔
1671
                return NULL;
1672
        s_new->num = rule->syscall;
191,906✔
1673
        s_new->valid = true;
191,906✔
1674
        /* run through the argument chain */
1675
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
1,343,340✔
1676
                if (chain[iter].valid == 0)
1,151,440✔
1677
                        continue;
968,354✔
1678

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

1681
                /* skip generating instruction which are no-ops */
1682
                if (!_db_arg_cmp_need_hi(&chain[iter]) &&
183,082✔
1683
                    !_db_arg_cmp_need_lo(&chain[iter]))
11,356✔
1684
                        continue;
68✔
1685

1686
                c_iter[0] = zmalloc(sizeof(*c_iter[0]));
183,014✔
1687
                if (c_iter[0] == NULL)
183,014✔
UNCOV
1688
                        goto gen_64_failure;
×
1689
                c_iter[1] = zmalloc(sizeof(*c_iter[1]));
183,014✔
1690
                if (c_iter[1] == NULL) {
183,014✔
UNCOV
1691
                        free(c_iter[0]);
×
UNCOV
1692
                        goto gen_64_failure;
×
1693
                }
1694
                c_iter[2] = NULL;
183,014✔
1695

1696
                arg = chain[iter].arg;
183,014✔
1697
                mask = chain[iter].mask;
183,014✔
1698
                datum = chain[iter].datum;
183,014✔
1699

1700
                /* NOTE: with the idea that a picture is worth a thousand
1701
                 *       words, i'm presenting the following diagrams which
1702
                 *       show how we should compare 64-bit syscall arguments
1703
                 *       using 32-bit comparisons.
1704
                 *
1705
                 *       in the diagrams below "A(x)" is the syscall argument
1706
                 *       being evaluated and "R(x)" is the syscall argument
1707
                 *       value specified in the libseccomp rule.  the "ACCEPT"
1708
                 *       verdict indicates a rule match and processing should
1709
                 *       continue on to the rest of the rule, or the final rule
1710
                 *       action should be triggered.  the "REJECT" verdict
1711
                 *       indicates that the rule does not match and processing
1712
                 *       should continue to the next rule or the default
1713
                 *       action.
1714
                 *
1715
                 * SCMP_CMP_GT:
1716
                 *                   +------------------+
1717
                 *                +--|  Ah(x) >  Rh(x)  |------+
1718
                 *                |  +------------------+      |
1719
                 *              FALSE                         TRUE     A
1720
                 *                |                            |       C
1721
                 *                +-----------+                +---->  C
1722
                 *                            v                +---->  E
1723
                 *                   +------------------+      |       P
1724
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1725
                 *        R       |  +------------------+  |   |
1726
                 *        E     FALSE                     TRUE |
1727
                 *        J  <----+                        |   |
1728
                 *        E  <----+           +------------+   |
1729
                 *        C     FALSE         v                |
1730
                 *        T       |  +------------------+      |
1731
                 *                +--|  Al(x) >  Rl(x)  |------+
1732
                 *                   +------------------+
1733
                 *
1734
                 * SCMP_CMP_GE:
1735
                 *                   +------------------+
1736
                 *                +--|  Ah(x) >  Rh(x)  |------+
1737
                 *                |  +------------------+      |
1738
                 *              FALSE                         TRUE     A
1739
                 *                |                            |       C
1740
                 *                +-----------+                +---->  C
1741
                 *                            v                +---->  E
1742
                 *                   +------------------+      |       P
1743
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1744
                 *        R       |  +------------------+  |   |
1745
                 *        E     FALSE                     TRUE |
1746
                 *        J  <----+                        |   |
1747
                 *        E  <----+           +------------+   |
1748
                 *        C     FALSE         v                |
1749
                 *        T       |  +------------------+      |
1750
                 *                +--|  Al(x) >= Rl(x)  |------+
1751
                 *                   +------------------+
1752
                 *
1753
                 * SCMP_CMP_LT:
1754
                 *                   +------------------+
1755
                 *                +--|  Ah(x) >  Rh(x)  |------+
1756
                 *                |  +------------------+      |
1757
                 *              FALSE                         TRUE     R
1758
                 *                |                            |       E
1759
                 *                +-----------+                +---->  J
1760
                 *                            v                +---->  E
1761
                 *                   +------------------+      |       C
1762
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1763
                 *        A       |  +------------------+  |   |
1764
                 *        C     FALSE                     TRUE |
1765
                 *        C  <----+                        |   |
1766
                 *        E  <----+           +------------+   |
1767
                 *        P     FALSE         v                |
1768
                 *        T       |  +------------------+      |
1769
                 *                +--|  Al(x) >= Rl(x)  |------+
1770
                 *                   +------------------+
1771
                 *
1772
                 * SCMP_CMP_LE:
1773
                 *                   +------------------+
1774
                 *                +--|  Ah(x) >  Rh(x)  |------+
1775
                 *                |  +------------------+      |
1776
                 *              FALSE                         TRUE     R
1777
                 *                |                            |       E
1778
                 *                +-----------+                +---->  J
1779
                 *                            v                +---->  E
1780
                 *                   +------------------+      |       C
1781
                 *                +--|  Ah(x) == Rh(x)  |--+   |       T
1782
                 *        A       |  +------------------+  |   |
1783
                 *        C     FALSE                     TRUE |
1784
                 *        C  <----+                        |   |
1785
                 *        E  <----+           +------------+   |
1786
                 *        P     FALSE         v                |
1787
                 *        T       |  +------------------+      |
1788
                 *                +--|  Al(x) >  Rl(x)  |------+
1789
                 *                   +------------------+
1790
                 *
1791
                 * SCMP_CMP_EQ:
1792
                 *                   +------------------+
1793
                 *                +--|  Ah(x) == Rh(x)  |--+
1794
                 *        R       |  +------------------+  |           A
1795
                 *        E     FALSE                     TRUE         C
1796
                 *        J  <----+                        |           C
1797
                 *        E  <----+           +------------+   +---->  E
1798
                 *        C     FALSE         v                |       P
1799
                 *        T       |  +------------------+      |       T
1800
                 *                +--|  Al(x) == Rl(x)  |------+
1801
                 *                   +------------------+
1802
                 *
1803
                 * SCMP_CMP_NE:
1804
                 *                   +------------------+
1805
                 *                +--|  Ah(x) == Rh(x)  |--+
1806
                 *        A       |  +------------------+  |           R
1807
                 *        C     FALSE                     TRUE         E
1808
                 *        C  <----+                        |           J
1809
                 *        E  <----+           +------------+   +---->  E
1810
                 *        P     FALSE         v                |       C
1811
                 *        T       |  +------------------+      |       T
1812
                 *                +--|  Al(x) == Rl(x)  |------+
1813
                 *                   +------------------+
1814
                 *
1815
                 */
1816

1817
                /* setup the level */
1818
                switch (chain[iter].op) {
183,014✔
1819
                case SCMP_CMP_GT:
7,112✔
1820
                case SCMP_CMP_GE:
1821
                case SCMP_CMP_LE:
1822
                case SCMP_CMP_LT:
1823
                        c_iter[2] = zmalloc(sizeof(*c_iter[2]));
7,112✔
1824
                        if (c_iter[2] == NULL) {
7,112✔
UNCOV
1825
                                free(c_iter[0]);
×
UNCOV
1826
                                free(c_iter[1]);
×
UNCOV
1827
                                goto gen_64_failure;
×
1828
                        }
1829

1830
                        c_iter[0]->arg = arg;
7,112✔
1831
                        c_iter[1]->arg = arg;
7,112✔
1832
                        c_iter[2]->arg = arg;
7,112✔
1833
                        c_iter[0]->arg_h_flg = true;
7,112✔
1834
                        c_iter[1]->arg_h_flg = true;
7,112✔
1835
                        c_iter[2]->arg_h_flg = false;
7,112✔
1836
                        c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
7,112✔
1837
                        c_iter[1]->arg_offset = arch_arg_offset_hi(arch, arg);
7,112✔
1838
                        c_iter[2]->arg_offset = arch_arg_offset_lo(arch, arg);
7,112✔
1839

1840
                        c_iter[0]->mask = D64_HI(mask);
7,112✔
1841
                        c_iter[1]->mask = D64_HI(mask);
7,112✔
1842
                        c_iter[2]->mask = D64_LO(mask);
7,112✔
1843
                        c_iter[0]->datum = D64_HI(datum);
7,112✔
1844
                        c_iter[1]->datum = D64_HI(datum);
7,112✔
1845
                        c_iter[2]->datum = D64_LO(datum);
7,112✔
1846
                        c_iter[0]->datum_full = datum;
7,112✔
1847
                        c_iter[1]->datum_full = datum;
7,112✔
1848
                        c_iter[2]->datum_full = datum;
7,112✔
1849

1850
                        _db_node_mask_fixup(c_iter[0]);
7,112✔
1851
                        _db_node_mask_fixup(c_iter[1]);
7,112✔
1852
                        _db_node_mask_fixup(c_iter[2]);
7,112✔
1853

1854
                        c_iter[0]->op = SCMP_CMP_GT;
7,112✔
1855
                        c_iter[1]->op = SCMP_CMP_EQ;
7,112✔
1856
                        switch (chain[iter].op) {
7,112✔
1857
                        case SCMP_CMP_GT:
774✔
1858
                        case SCMP_CMP_LE:
1859
                                c_iter[2]->op = SCMP_CMP_GT;
774✔
1860
                                break;
774✔
1861
                        case SCMP_CMP_GE:
6,338✔
1862
                        case SCMP_CMP_LT:
1863
                                c_iter[2]->op = SCMP_CMP_GE;
6,338✔
1864
                                break;
6,338✔
UNCOV
1865
                        default:
×
1866
                                /* we should never get here */
UNCOV
1867
                                goto gen_64_failure;
×
1868
                        }
1869
                        c_iter[0]->op_orig = chain[iter].op;
7,112✔
1870
                        c_iter[1]->op_orig = chain[iter].op;
7,112✔
1871
                        c_iter[2]->op_orig = chain[iter].op;
7,112✔
1872

1873
                        c_iter[0]->nxt_f = _db_node_get(c_iter[1]);
7,112✔
1874
                        c_iter[1]->nxt_t = _db_node_get(c_iter[2]);
7,112✔
1875
                        break;
7,112✔
1876
                case SCMP_CMP_EQ:
175,902✔
1877
                case SCMP_CMP_MASKED_EQ:
1878
                case SCMP_CMP_NE:
1879
                        c_iter[0]->arg = arg;
175,902✔
1880
                        c_iter[1]->arg = arg;
175,902✔
1881
                        c_iter[0]->arg_h_flg = true;
175,902✔
1882
                        c_iter[1]->arg_h_flg = false;
175,902✔
1883
                        c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
175,902✔
1884
                        c_iter[1]->arg_offset = arch_arg_offset_lo(arch, arg);
175,902✔
1885

1886
                        c_iter[0]->mask = D64_HI(mask);
175,902✔
1887
                        c_iter[1]->mask = D64_LO(mask);
175,902✔
1888
                        c_iter[0]->datum = D64_HI(datum);
175,902✔
1889
                        c_iter[1]->datum = D64_LO(datum);
175,902✔
1890
                        c_iter[0]->datum_full = datum;
175,902✔
1891
                        c_iter[1]->datum_full = datum;
175,902✔
1892

1893
                        _db_node_mask_fixup(c_iter[0]);
175,902✔
1894
                        _db_node_mask_fixup(c_iter[1]);
175,902✔
1895

1896
                        switch (chain[iter].op) {
175,902✔
1897
                        case SCMP_CMP_MASKED_EQ:
11,288✔
1898
                                c_iter[0]->op = SCMP_CMP_MASKED_EQ;
11,288✔
1899
                                c_iter[1]->op = SCMP_CMP_MASKED_EQ;
11,288✔
1900
                                break;
11,288✔
1901
                        default:
164,614✔
1902
                                c_iter[0]->op = SCMP_CMP_EQ;
164,614✔
1903
                                c_iter[1]->op = SCMP_CMP_EQ;
164,614✔
1904
                        }
1905
                        c_iter[0]->op_orig = chain[iter].op;
175,902✔
1906
                        c_iter[1]->op_orig = chain[iter].op;
175,902✔
1907

1908
                        c_iter[0]->nxt_t = _db_node_get(c_iter[1]);
175,902✔
1909
                        break;
175,902✔
UNCOV
1910
                default:
×
1911
                        /* we should never get here */
UNCOV
1912
                        goto gen_64_failure;
×
1913
                }
1914

1915
                /* link this level to the previous level */
1916
                if (c_prev[0] != NULL) {
183,014✔
1917
                        switch (op_prev) {
79,454✔
1918
                        case SCMP_CMP_GT:
1919
                        case SCMP_CMP_GE:
1920
                                c_prev[0]->nxt_t = _db_node_get(c_iter[0]);
116✔
1921
                                c_prev[2]->nxt_t = _db_node_get(c_iter[0]);
116✔
1922
                                break;
116✔
1923
                        case SCMP_CMP_EQ:
1924
                        case SCMP_CMP_MASKED_EQ:
1925
                                c_prev[1]->nxt_t = _db_node_get(c_iter[0]);
72,256✔
1926
                                break;
72,256✔
1927
                        case SCMP_CMP_LE:
1928
                        case SCMP_CMP_LT:
1929
                                c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
104✔
1930
                                c_prev[2]->nxt_f = _db_node_get(c_iter[0]);
104✔
1931
                                break;
104✔
1932
                        case SCMP_CMP_NE:
1933
                                c_prev[0]->nxt_f = _db_node_get(c_iter[0]);
6,978✔
1934
                                c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
6,978✔
1935
                                break;
6,978✔
UNCOV
1936
                        default:
×
1937
                                /* we should never get here */
UNCOV
1938
                                goto gen_64_failure;
×
1939
                        }
1940
                } else
1941
                        s_new->chains = _db_node_get(c_iter[0]);
103,560✔
1942

1943
                /* update the node count */
1944
                switch (chain[iter].op) {
183,014✔
1945
                case SCMP_CMP_NE:
175,902✔
1946
                case SCMP_CMP_EQ:
1947
                case SCMP_CMP_MASKED_EQ:
1948
                        s_new->node_cnt += 2;
175,902✔
1949
                        break;
175,902✔
1950
                default:
7,112✔
1951
                        s_new->node_cnt += 3;
7,112✔
1952
                }
1953

1954
                /* keep pointers to this level */
1955
                c_prev[0] = c_iter[0];
1956
                c_prev[1] = c_iter[1];
1957
                c_prev[2] = c_iter[2];
1958
                op_prev = chain[iter].op;
1959
        }
1960
        if (c_iter[0] != NULL) {
191,906✔
1961
                /* set the actions on the last layer */
1962
                switch (op_prev) {
103,560✔
1963
                case SCMP_CMP_GT:
908✔
1964
                case SCMP_CMP_GE:
1965
                        c_iter[0]->act_t_flg = true;
908✔
1966
                        c_iter[0]->act_t = rule->action;
908✔
1967
                        c_iter[2]->act_t_flg = true;
908✔
1968
                        c_iter[2]->act_t = rule->action;
908✔
1969
                        break;
908✔
1970
                case SCMP_CMP_LE:
5,984✔
1971
                case SCMP_CMP_LT:
1972
                        c_iter[1]->act_f_flg = true;
5,984✔
1973
                        c_iter[1]->act_f = rule->action;
5,984✔
1974
                        c_iter[2]->act_f_flg = true;
5,984✔
1975
                        c_iter[2]->act_f = rule->action;
5,984✔
1976
                        break;
5,984✔
1977
                case SCMP_CMP_EQ:
89,416✔
1978
                case SCMP_CMP_MASKED_EQ:
1979
                        c_iter[1]->act_t_flg = true;
89,416✔
1980
                        c_iter[1]->act_t = rule->action;
89,416✔
1981
                        break;
89,416✔
1982
                case SCMP_CMP_NE:
7,252✔
1983
                        c_iter[0]->act_f_flg = true;
7,252✔
1984
                        c_iter[0]->act_f = rule->action;
7,252✔
1985
                        c_iter[1]->act_f_flg = true;
7,252✔
1986
                        c_iter[1]->act_f = rule->action;
7,252✔
1987
                        break;
7,252✔
UNCOV
1988
                default:
×
1989
                        /* we should never get here */
UNCOV
1990
                        goto gen_64_failure;
×
1991
                }
1992
        } else
1993
                s_new->action = rule->action;
88,346✔
1994

1995
        return s_new;
1996

UNCOV
1997
gen_64_failure:
×
1998
        /* free the new chain and its syscall struct */
UNCOV
1999
        _db_tree_put(&s_new->chains);
×
UNCOV
2000
        free(s_new);
×
UNCOV
2001
        return NULL;
×
2002
}
2003

2004
/**
2005
 * Generate a new filter rule for a 32 bit system
2006
 * @param arch the architecture definition
2007
 * @param rule the new filter rule
2008
 *
2009
 * This function generates a new syscall filter for a 32 bit system. Returns
2010
 * zero on success, negative values on failure.
2011
 *
2012
 */
2013
static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch,
95,510✔
2014
                                           const struct db_api_rule_list *rule)
2015
{
2016
        unsigned int iter;
95,510✔
2017
        struct db_sys_list *s_new;
95,510✔
2018
        const struct db_api_arg *chain = rule->args;
95,510✔
2019
        struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL;
95,510✔
2020
        bool tf_flag;
95,510✔
2021

2022
        s_new = zmalloc(sizeof(*s_new));
95,510✔
2023
        if (s_new == NULL)
95,510✔
2024
                return NULL;
2025
        s_new->num = rule->syscall;
95,510✔
2026
        s_new->valid = true;
95,510✔
2027
        /* run through the argument chain */
2028
        for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
668,570✔
2029
                if (chain[iter].valid == 0)
573,060✔
2030
                        continue;
522,640✔
2031

2032
                /* skip generating instructions which are no-ops */
2033
                if (!_db_arg_cmp_need_lo(&chain[iter]))
50,420✔
UNCOV
2034
                        continue;
×
2035

2036
                c_iter = zmalloc(sizeof(*c_iter));
50,420✔
2037
                if (c_iter == NULL)
50,420✔
UNCOV
2038
                        goto gen_32_failure;
×
2039
                c_iter->arg = chain[iter].arg;
50,420✔
2040
                c_iter->arg_h_flg = false;
50,420✔
2041
                c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg);
50,420✔
2042
                c_iter->op = chain[iter].op;
50,420✔
2043
                c_iter->op_orig = chain[iter].op;
50,420✔
2044
                /* implicitly strips off the upper 32 bit */
2045
                c_iter->mask = chain[iter].mask;
50,420✔
2046
                c_iter->datum = chain[iter].datum;
50,420✔
2047
                c_iter->datum_full = chain[iter].datum;
50,420✔
2048

2049
                /* link in the new node and update the chain */
2050
                if (c_prev != NULL) {
50,420✔
2051
                        if (tf_flag)
60✔
2052
                                c_prev->nxt_t = _db_node_get(c_iter);
36✔
2053
                        else
2054
                                c_prev->nxt_f = _db_node_get(c_iter);
24✔
2055
                } else
2056
                        s_new->chains = _db_node_get(c_iter);
50,360✔
2057
                s_new->node_cnt++;
50,420✔
2058

2059
                /* rewrite the op to reduce the op/datum combos */
2060
                switch (c_iter->op) {
50,420✔
2061
                case SCMP_CMP_NE:
12✔
2062
                        c_iter->op = SCMP_CMP_EQ;
12✔
2063
                        tf_flag = false;
12✔
2064
                        break;
12✔
2065
                case SCMP_CMP_LT:
12✔
2066
                        c_iter->op = SCMP_CMP_GE;
12✔
2067
                        tf_flag = false;
12✔
2068
                        break;
12✔
2069
                case SCMP_CMP_LE:
12✔
2070
                        c_iter->op = SCMP_CMP_GT;
12✔
2071
                        tf_flag = false;
12✔
2072
                        break;
12✔
2073
                default:
2074
                        tf_flag = true;
2075
                }
2076

2077
                /* fixup the mask/datum */
2078
                _db_node_mask_fixup(c_iter);
50,420✔
2079

2080
                c_prev = c_iter;
50,420✔
2081
        }
2082
        if (c_iter != NULL) {
95,510✔
2083
                /* set the leaf node */
2084
                if (tf_flag) {
50,360✔
2085
                        c_iter->act_t_flg = true;
50,348✔
2086
                        c_iter->act_t = rule->action;
50,348✔
2087
                } else {
2088
                        c_iter->act_f_flg = true;
12✔
2089
                        c_iter->act_f = rule->action;
12✔
2090
                }
2091
        } else
2092
                s_new->action = rule->action;
45,150✔
2093

2094
        return s_new;
2095

UNCOV
2096
gen_32_failure:
×
2097
        /* free the new chain and its syscall struct */
UNCOV
2098
        _db_tree_put(&s_new->chains);
×
UNCOV
2099
        free(s_new);
×
UNCOV
2100
        return NULL;
×
2101
}
2102

2103
/**
2104
 * Add a new rule to the seccomp filter DB
2105
 * @param db the seccomp filter db
2106
 * @param rule the filter rule
2107
 *
2108
 * This function adds a new syscall filter to the seccomp filter DB, adding to
2109
 * the existing filters for the syscall, unless no argument specific filters
2110
 * are present (filtering only on the syscall).  When adding new chains, the
2111
 * shortest chain, or most inclusive filter match, will be entered into the
2112
 * filter DB. Returns zero on success, negative values on failure.
2113
 *
2114
 * It is important to note that in the case of failure the db may be corrupted,
2115
 * the caller must use the transaction mechanism if the db integrity is
2116
 * important.
2117
 *
2118
 */
2119
int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule)
287,416✔
2120
{
2121
        int rc = -ENOMEM;
287,416✔
2122
        struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
287,416✔
2123
        struct db_iter_state state;
287,416✔
2124
        bool rm_flag = false;
287,416✔
2125

2126
        assert(db != NULL);
287,416✔
2127

2128
        /* do all our possible memory allocation up front so we don't have to
2129
         * worry about failure once we get to the point where we start updating
2130
         * the filter db */
2131
        if (db->arch->size == ARCH_SIZE_64)
287,416✔
2132
                s_new = _db_rule_gen_64(db->arch, rule);
191,906✔
2133
        else if (db->arch->size == ARCH_SIZE_32)
95,510✔
2134
                s_new = _db_rule_gen_32(db->arch, rule);
95,510✔
2135
        else
2136
                return -EFAULT;
2137
        if (s_new == NULL)
287,416✔
2138
                return -ENOMEM;
2139

2140
        /* find a matching syscall/chain or insert a new one */
2141
        s_iter = db->syscalls;
287,416✔
2142
        while (s_iter != NULL && s_iter->num < rule->syscall) {
1,476,240✔
2143
                s_prev = s_iter;
1,188,830✔
2144
                s_iter = s_iter->next;
1,188,830✔
2145
        }
2146
        s_new->priority = _DB_PRI_MASK_CHAIN - s_new->node_cnt;
287,416✔
2147
add_reset:
289,704✔
2148
        if (s_iter == NULL || s_iter->num != rule->syscall) {
289,704✔
2149
                /* new syscall, add before s_iter */
2150
                if (s_prev != NULL) {
208,426✔
2151
                        s_new->next = s_prev->next;
166,266✔
2152
                        s_prev->next = s_new;
166,266✔
2153
                } else {
2154
                        s_new->next = db->syscalls;
42,160✔
2155
                        db->syscalls = s_new;
42,160✔
2156
                }
2157
                db->syscall_cnt++;
208,426✔
2158
                return 0;
208,426✔
2159
        } else if (s_iter->chains == NULL) {
81,278✔
2160
                if (rm_flag || !s_iter->valid) {
3,108✔
2161
                        /* we are here because our previous pass cleared the
2162
                         * entire syscall chain when searching for a subtree
2163
                         * match or the existing syscall entry is a phantom,
2164
                         * so either way add the new chain */
2165
                        s_iter->chains = s_new->chains;
2,328✔
2166
                        s_iter->action = s_new->action;
2,328✔
2167
                        s_iter->node_cnt = s_new->node_cnt;
2,328✔
2168
                        if (s_iter->valid)
2,328✔
2169
                                s_iter->priority = s_new->priority;
2,288✔
2170
                        s_iter->valid = true;
2,328✔
2171
                        free(s_new);
2,328✔
2172
                        rc = 0;
2,328✔
2173
                        goto add_priority_update;
2,328✔
2174
                } else {
2175
                        /* syscall exists without any chains - existing filter
2176
                         * is at least as large as the new entry so cleanup and
2177
                         * exit */
2178
                        _db_tree_put(&s_new->chains);
780✔
2179
                        free(s_new);
780✔
2180
                        goto add_free_ok;
780✔
2181
                }
2182
        } else if (s_iter->chains != NULL && s_new->chains == NULL) {
78,170✔
2183
                /* syscall exists with chains but the new filter has no chains
2184
                 * so we need to clear the existing chains and exit */
2185
                _db_tree_put(&s_iter->chains);
132✔
2186
                s_iter->chains = NULL;
132✔
2187
                s_iter->node_cnt = 0;
132✔
2188
                s_iter->action = rule->action;
132✔
2189

2190
                /* cleanup the new tree and return */
2191
                _db_tree_put(&s_new->chains);
132✔
2192
                free(s_new);
132✔
2193
                goto add_free_ok;
132✔
2194
        }
2195

2196
        /* prune any sub-trees that are no longer required */
2197
        memset(&state, 0, sizeof(state));
78,038✔
2198
        state.sx = s_iter;
78,038✔
2199
        state.action = rule->action;
78,038✔
2200
        rc = _db_tree_prune(&s_iter->chains, s_new->chains, &state);
78,038✔
2201
        if (rc > 0) {
78,038✔
2202
                /* we pruned at least some of the existing tree */
2203
                rm_flag = true;
2,288✔
2204
                s_iter->node_cnt -= rc;
2,288✔
2205
                if (s_iter->chains == NULL)
2,288✔
2206
                        /* we pruned the entire tree */
2207
                        goto add_reset;
2,288✔
2208
        } else if ((state.flags & _DB_IST_M_REDUNDANT) == _DB_IST_M_REDUNDANT) {
75,750✔
2209
                /* the existing tree is "shorter", drop the new one */
2210
                _db_tree_put(&s_new->chains);
4,644✔
2211
                free(s_new);
4,644✔
2212
                goto add_free_ok;
4,644✔
2213
        }
2214

2215
        /* add the new rule to the existing filter and cleanup */
2216
        memset(&state, 0, sizeof(state));
71,106✔
2217
        state.sx = s_iter;
71,106✔
2218
        rc = _db_tree_add(&s_iter->chains, s_new->chains, &state);
71,106✔
2219
        if (rc < 0)
71,106✔
2220
                goto add_failure;
34✔
2221
        s_iter->node_cnt += s_new->node_cnt;
71,072✔
2222
        s_iter->node_cnt -= _db_tree_put(&s_new->chains);
71,072✔
2223
        free(s_new);
71,072✔
2224

2225
add_free_ok:
2226
        rc = 0;
2227
add_priority_update:
78,956✔
2228
        /* update the priority */
2229
        if (s_iter != NULL) {
78,956✔
2230
                s_iter->priority &= (~_DB_PRI_MASK_CHAIN);
78,956✔
2231
                s_iter->priority |= (_DB_PRI_MASK_CHAIN - s_iter->node_cnt);
78,956✔
2232
        }
2233
        return rc;
78,956✔
2234

2235
add_failure:
34✔
2236
        /* NOTE: another reminder that we don't do any db error recovery here,
2237
         * use the transaction mechanism as previously mentioned */
2238
        _db_tree_put(&s_new->chains);
34✔
2239
        free(s_new);
34✔
2240
        return rc;
34✔
2241
}
2242

2243
/**
2244
 * Set the priority of a given syscall
2245
 * @param col the filter collection
2246
 * @param syscall the syscall number
2247
 * @param priority priority value, higher value == higher priority
2248
 *
2249
 * This function sets the priority of the given syscall; this value is used
2250
 * when generating the seccomp filter code such that higher priority syscalls
2251
 * will incur less filter code overhead than the lower priority syscalls in the
2252
 * filter.  Returns zero on success, negative values on failure.
2253
 *
2254
 */
2255
int db_col_syscall_priority(struct db_filter_col *col,
68✔
2256
                            int syscall, uint8_t priority)
2257
{
2258
        int rc = 0, rc_tmp;
68✔
2259
        unsigned int iter;
68✔
2260
        int sc_tmp;
68✔
2261
        struct db_filter *filter;
68✔
2262

2263
        for (iter = 0; iter < col->filter_cnt; iter++) {
150✔
2264
                filter = col->filters[iter];
82✔
2265
                sc_tmp = syscall;
82✔
2266

2267
                rc_tmp = arch_syscall_translate(filter->arch, &sc_tmp);
82✔
2268
                if (rc_tmp < 0)
82✔
UNCOV
2269
                        goto priority_failure;
×
2270

2271
                /* if this is a pseudo syscall then we need to rewrite the
2272
                 * syscall for some arch specific reason, don't forget the
2273
                 * special handling for syscall -1 */
2274
                if (sc_tmp < -1) {
82✔
2275
                        /* we set this as a strict op - we don't really care
2276
                         * since priorities are a "best effort" thing - as we
2277
                         * want to catch the -EDOM error and bail on this
2278
                         * architecture */
2279
                        rc_tmp = arch_syscall_rewrite(filter->arch, &sc_tmp);
7✔
2280
                        if (rc_tmp == -EDOM)
7✔
2281
                                continue;
3✔
2282
                        if (rc_tmp < 0)
4✔
UNCOV
2283
                                goto priority_failure;
×
2284
                }
2285

2286
                rc_tmp = _db_syscall_priority(filter, sc_tmp, priority);
79✔
2287

2288
priority_failure:
79✔
2289
                if (rc == 0 && rc_tmp < 0)
79✔
UNCOV
2290
                        rc = rc_tmp;
×
2291
        }
2292

2293
        if (rc == 0)
68✔
2294
                db_col_precompute_reset(col);
136✔
2295

2296
        return rc;
68✔
2297
}
2298

2299
/**
2300
 * Add a new rule to a single filter
2301
 * @param filter the filter
2302
 * @param rule the filter rule
2303
 *
2304
 * This is a helper function for db_col_rule_add() and similar functions, it
2305
 * isn't generally useful.  Returns zero on success, negative values on error.
2306
 *
2307
 */
2308
static int _db_col_rule_add(struct db_filter *filter,
263,315✔
2309
                            struct db_api_rule_list *rule)
2310
{
2311
        int rc;
263,315✔
2312
        struct db_api_rule_list *iter;
263,315✔
2313

2314
        /* add the rule to the filter */
2315
        rc = arch_filter_rule_add(filter, rule);
263,315✔
2316
        if (rc != 0)
263,315✔
2317
                return rc;
2318

2319
        /* insert the chain to the end of the rule list */
2320
        iter = rule;
2321
        while (iter->next)
262,916✔
2322
                iter = iter->next;
2323
        if (filter->rules != NULL) {
262,916✔
2324
                rule->prev = filter->rules->prev;
228,276✔
2325
                iter->next = filter->rules;
228,276✔
2326
                filter->rules->prev->next = rule;
228,276✔
2327
                filter->rules->prev = iter;
228,276✔
2328
        } else {
2329
                rule->prev = iter;
34,640✔
2330
                iter->next = rule;
34,640✔
2331
                filter->rules = rule;
34,640✔
2332
        }
2333

2334
        return 0;
2335
}
2336

2337
/**
2338
 * Add a new rule to the current filter
2339
 * @param col the filter collection
2340
 * @param strict the strict flag
2341
 * @param action the filter action
2342
 * @param syscall the syscall number
2343
 * @param arg_cnt the number of argument filters in the argument filter chain
2344
 * @param arg_array the argument filter chain, (uint, enum scmp_compare, ulong)
2345
 *
2346
 * This function adds a new argument/comparison/value to the seccomp filter for
2347
 * a syscall; multiple arguments can be specified and they will be chained
2348
 * together (essentially AND'd together) in the filter.  When the strict flag
2349
 * is true the function will fail if the exact rule can not be added to the
2350
 * filter, if the strict flag is false the function will not fail if the
2351
 * function needs to adjust the rule due to architecture specifics.  Returns
2352
 * zero on success, negative values on failure.
2353
 *
2354
 */
2355
int db_col_rule_add(struct db_filter_col *col,
53,089✔
2356
                    bool strict, uint32_t action, int syscall,
2357
                    unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
2358
{
2359
        int rc = 0, rc_tmp;
53,089✔
2360
        unsigned int iter;
53,089✔
2361
        unsigned int arg_num;
53,089✔
2362
        size_t chain_size;
53,089✔
2363
        struct db_api_arg *chain = NULL;
53,089✔
2364
        struct scmp_arg_cmp arg_data;
53,089✔
2365
        struct db_api_rule_list *rule;
53,089✔
2366
        struct db_filter *db;
53,089✔
2367

2368
        /* collect the arguments for the filter rule */
2369
        chain_size = sizeof(*chain) * ARG_COUNT_MAX;
53,089✔
2370
        chain = zmalloc(chain_size);
53,089✔
2371
        if (chain == NULL)
53,089✔
2372
                return -ENOMEM;
2373
        for (iter = 0; iter < arg_cnt; iter++) {
125,151✔
2374
                arg_data = arg_array[iter];
72,064✔
2375
                arg_num = arg_data.arg;
72,064✔
2376
                if (arg_num < ARG_COUNT_MAX && chain[arg_num].valid == 0) {
72,064✔
2377
                        chain[arg_num].valid = 1;
72,064✔
2378
                        chain[arg_num].arg = arg_num;
72,064✔
2379
                        chain[arg_num].op = arg_data.op;
72,064✔
2380
                        /* TODO: we should check datum/mask size against the
2381
                         *         arch definition, e.g. 64 bit datum on x86 */
2382
                        switch (chain[arg_num].op) {
72,064✔
2383
                        case SCMP_CMP_NE:
66,389✔
2384
                        case SCMP_CMP_LT:
2385
                        case SCMP_CMP_LE:
2386
                        case SCMP_CMP_EQ:
2387
                        case SCMP_CMP_GE:
2388
                        case SCMP_CMP_GT:
2389
                                chain[arg_num].mask = DATUM_MAX;
66,389✔
2390
                                chain[arg_num].datum = arg_data.datum_a;
66,389✔
2391
                                break;
66,389✔
2392
                        case SCMP_CMP_MASKED_EQ:
5,673✔
2393
                                chain[arg_num].mask = arg_data.datum_a;
5,673✔
2394
                                chain[arg_num].datum = arg_data.datum_b;
5,673✔
2395
                                break;
5,673✔
2396
                        default:
2✔
2397
                                rc = -EINVAL;
2✔
2398
                                goto add_return;
2✔
2399
                        }
2400
                } else {
UNCOV
2401
                        rc = -EINVAL;
×
UNCOV
2402
                        goto add_return;
×
2403
                }
2404
        }
2405

2406
        /* create a checkpoint */
2407
        rc = db_col_transaction_start(col);
53,087✔
2408
        if (rc != 0)
53,087✔
2409
                goto add_return;
×
2410

2411
        /* add the rule to the different filters in the collection */
2412
        for (iter = 0; iter < col->filter_cnt; iter++) {
184,893✔
2413
                db = col->filters[iter];
131,806✔
2414

2415
                /* create the rule */
2416
                rule = _db_rule_new(strict, action, syscall, chain);
131,806✔
2417
                if (rule == NULL) {
131,806✔
UNCOV
2418
                        rc_tmp = -ENOMEM;
×
UNCOV
2419
                        goto add_arch_fail;
×
2420
                }
2421

2422
                /* add the rule */
2423
                rc_tmp = _db_col_rule_add(db, rule);
131,806✔
2424
                if (rc_tmp != 0)
131,806✔
2425
                        free(rule);
399✔
2426

2427
add_arch_fail:
131,407✔
2428
                if (rc_tmp != 0 && rc == 0)
131,806✔
2429
                        rc = rc_tmp;
399✔
2430
        }
2431

2432
        /* commit the transaction or abort */
2433
        if (rc == 0)
53,087✔
2434
                db_col_transaction_commit(col);
52,688✔
2435
        else
2436
                db_col_transaction_abort(col);
399✔
2437

2438
add_return:
53,087✔
2439
        /* update the misc state */
2440
        if (rc == 0) {
53,089✔
2441
                if (action == SCMP_ACT_NOTIFY)
52,688✔
UNCOV
2442
                        col->notify_used = true;
×
2443
                db_col_precompute_reset(col);
105,777✔
2444
        }
2445
        if (chain != NULL)
53,089✔
2446
                free(chain);
53,089✔
2447
        return rc;
53,089✔
2448
}
2449

2450
/**
2451
 * Start a new seccomp filter transaction
2452
 * @param col the filter collection
2453
 *
2454
 * This function starts a new seccomp filter transaction for the given filter
2455
 * collection.  Returns zero on success, negative values on failure.
2456
 *
2457
 */
2458
int db_col_transaction_start(struct db_filter_col *col)
53,087✔
2459
{
2460
        int rc;
53,087✔
2461
        unsigned int iter;
53,087✔
2462
        struct db_filter_snap *snap;
53,087✔
2463
        struct db_filter *filter_o, *filter_s;
53,087✔
2464
        struct db_api_rule_list *rule_o, *rule_s = NULL;
53,087✔
2465

2466
        /* check to see if a shadow snapshot exists */
2467
        if (col->snapshots && col->snapshots->shadow) {
53,087✔
2468
                /* we have a shadow!  this will be easy */
2469

2470
                /* NOTE: we don't bother to do any verification of the shadow
2471
                 *       because we start a new transaction every time we add
2472
                 *       a new rule to the filter(s); if this ever changes we
2473
                 *       will need to add a mechanism to verify that the shadow
2474
                 *       transaction is current/correct */
2475

2476
                col->snapshots->shadow = false;
45,931✔
2477
                return 0;
45,931✔
2478
        }
2479

2480
        /* allocate the snapshot */
2481
        snap = zmalloc(sizeof(*snap));
7,156✔
2482
        if (snap == NULL)
7,156✔
2483
                return -ENOMEM;
2484
        snap->filters = zmalloc(sizeof(struct db_filter *) * col->filter_cnt);
7,156✔
2485
        if (snap->filters == NULL) {
7,156✔
UNCOV
2486
                free(snap);
×
UNCOV
2487
                return -ENOMEM;
×
2488
        }
2489
        snap->filter_cnt = col->filter_cnt;
7,156✔
2490
        for (iter = 0; iter < snap->filter_cnt; iter++)
24,507✔
2491
                snap->filters[iter] = NULL;
17,351✔
2492
        snap->next = NULL;
7,156✔
2493

2494
        /* create a snapshot of the current filter state */
2495
        for (iter = 0; iter < col->filter_cnt; iter++) {
24,507✔
2496
                /* allocate a new filter */
2497
                filter_o = col->filters[iter];
17,351✔
2498
                filter_s = _db_init(filter_o->arch);
17,351✔
2499
                if (filter_s == NULL)
17,351✔
UNCOV
2500
                        goto trans_start_failure;
×
2501
                snap->filters[iter] = filter_s;
17,351✔
2502

2503
                /* create a filter snapshot from existing rules */
2504
                rule_o = filter_o->rules;
17,351✔
2505
                if (rule_o == NULL)
17,351✔
2506
                        continue;
17,317✔
2507
                do {
102✔
2508
                        /* duplicate the rule */
2509
                        rule_s = db_rule_dup(rule_o);
102✔
2510
                        if (rule_s == NULL)
102✔
UNCOV
2511
                                goto trans_start_failure;
×
2512

2513
                        /* add the rule */
2514
                        rc = _db_col_rule_add(filter_s, rule_s);
102✔
2515
                        if (rc != 0)
102✔
UNCOV
2516
                                goto trans_start_failure;
×
2517
                        rule_s = NULL;
102✔
2518

2519
                        /* next rule */
2520
                        rule_o = rule_o->next;
102✔
2521
                } while (rule_o != filter_o->rules);
102✔
2522
        }
2523

2524
        /* add the snapshot to the list */
2525
        snap->next = col->snapshots;
7,156✔
2526
        col->snapshots = snap;
7,156✔
2527

2528
        return 0;
7,156✔
2529

UNCOV
2530
trans_start_failure:
×
UNCOV
2531
        if (rule_s != NULL)
×
UNCOV
2532
                free(rule_s);
×
UNCOV
2533
        _db_snap_release(snap);
×
UNCOV
2534
        return -ENOMEM;
×
2535
}
2536

2537
/**
2538
 * Abort the top most seccomp filter transaction
2539
 * @param col the filter collection
2540
 *
2541
 * This function aborts the most recent seccomp filter transaction.
2542
 *
2543
 */
2544
void db_col_transaction_abort(struct db_filter_col *col)
399✔
2545
{
2546
        int iter;
399✔
2547
        unsigned int filter_cnt;
399✔
2548
        struct db_filter **filters;
399✔
2549
        struct db_filter_snap *snap;
399✔
2550

2551
        if (col->snapshots == NULL)
399✔
2552
                return;
2553

2554
        /* replace the current filter with the last snapshot */
2555
        snap = col->snapshots;
399✔
2556
        col->snapshots = snap->next;
399✔
2557
        filter_cnt = col->filter_cnt;
399✔
2558
        filters = col->filters;
399✔
2559
        col->filter_cnt = snap->filter_cnt;
399✔
2560
        col->filters = snap->filters;
399✔
2561
        free(snap);
399✔
2562

2563
        /* free the filter we swapped out */
2564
        for (iter = 0; iter < filter_cnt; iter++)
798✔
2565
                _db_release(filters[iter]);
798✔
2566
        free(filters);
399✔
2567

2568
        /* free any precompute */
2569
        db_col_precompute_reset(col);
798✔
2570
}
2571

2572
/**
2573
 * Commit the top most seccomp filter transaction
2574
 * @param col the filter collection
2575
 *
2576
 * This function commits the most recent seccomp filter transaction and
2577
 * attempts to create a shadow transaction that is a duplicate of the current
2578
 * filter to speed up future transactions.
2579
 *
2580
 */
2581
void db_col_transaction_commit(struct db_filter_col *col)
52,688✔
2582
{
2583
        int rc;
52,688✔
2584
        unsigned int iter;
52,688✔
2585
        struct db_filter_snap *snap;
52,688✔
2586
        struct db_filter *filter_o, *filter_s;
52,688✔
2587
        struct db_api_rule_list *rule_o, *rule_s;
52,688✔
2588

2589
        snap = col->snapshots;
52,688✔
2590
        if (snap == NULL)
52,688✔
2591
                return;
2592

2593
        /* check for a shadow set by a higher transaction commit */
2594
        if (snap->shadow) {
52,688✔
2595
                /* leave the shadow intact, but drop the next snapshot */
UNCOV
2596
                if (snap->next) {
×
UNCOV
2597
                        snap->next = snap->next->next;
×
UNCOV
2598
                        _db_snap_release(snap->next);
×
2599
                }
UNCOV
2600
                return;
×
2601
        }
2602

2603
        /* adjust the number of filters if needed */
2604
        if (col->filter_cnt > snap->filter_cnt) {
52,688✔
2605
                unsigned int tmp_i;
×
2606
                struct db_filter **tmp_f;
×
2607

2608
                /* add filters */
UNCOV
2609
                tmp_f = realloc(snap->filters,
×
UNCOV
2610
                                sizeof(struct db_filter *) * col->filter_cnt);
×
UNCOV
2611
                if (tmp_f == NULL)
×
UNCOV
2612
                        goto shadow_err;
×
2613
                snap->filters = tmp_f;
×
2614
                do {
×
UNCOV
2615
                        tmp_i = snap->filter_cnt;
×
UNCOV
2616
                        snap->filters[tmp_i] =
×
2617
                                _db_init(col->filters[tmp_i]->arch);
×
2618
                        if (snap->filters[tmp_i] == NULL)
×
2619
                                goto shadow_err;
×
2620
                        snap->filter_cnt++;
×
2621
                } while (snap->filter_cnt < col->filter_cnt);
×
2622
        } else if (col->filter_cnt < snap->filter_cnt) {
52,688✔
2623
                /* remove filters */
2624

2625
                /* NOTE: while we release the filters we no longer need, we
2626
                 *       don't bother to resize the filter array, we just
2627
                 *       adjust the filter counter, this *should* be harmless
2628
                 *       at the cost of a not reaping all the memory possible */
2629

UNCOV
2630
                do {
×
UNCOV
2631
                        _db_release(snap->filters[snap->filter_cnt--]);
×
UNCOV
2632
                } while (snap->filter_cnt > col->filter_cnt);
×
2633
        }
2634

2635
        /* loop through each filter and update the rules on the snapshot */
2636
        for (iter = 0; iter < col->filter_cnt; iter++) {
184,095✔
2637
                filter_o = col->filters[iter];
131,407✔
2638
                filter_s = snap->filters[iter];
131,407✔
2639

2640
                /* skip ahead to the new rule(s) */
2641
                rule_o = filter_o->rules;
131,407✔
2642
                rule_s = filter_s->rules;
131,407✔
2643
                if (rule_o == NULL)
131,407✔
2644
                        /* nothing to shadow */
UNCOV
2645
                        continue;
×
2646
                if (rule_s != NULL) {
131,407✔
2647
                        do {
1,116,570✔
2648
                                rule_o = rule_o->next;
1,116,570✔
2649
                                rule_s = rule_s->next;
1,116,570✔
2650
                        } while (rule_s != filter_s->rules);
1,116,570✔
2651

2652
                        /* did we actually add any rules? */
2653
                        if (rule_o == filter_o->rules)
114,104✔
2654
                                /* no, we are done in this case */
UNCOV
2655
                                continue;
×
2656
                }
2657

2658
                /* update the old snapshot to make it a shadow */
2659
                do {
131,407✔
2660
                        /* duplicate the rule */
2661
                        rule_s = db_rule_dup(rule_o);
131,407✔
2662
                        if (rule_s == NULL)
131,407✔
2663
                                goto shadow_err;
×
2664

2665
                        /* add the rule */
2666
                        rc = _db_col_rule_add(filter_s, rule_s);
131,407✔
2667
                        if (rc != 0) {
131,407✔
UNCOV
2668
                                free(rule_s);
×
UNCOV
2669
                                goto shadow_err;
×
2670
                        }
2671

2672
                        /* next rule */
2673
                        rule_o = rule_o->next;
131,407✔
2674
                } while (rule_o != filter_o->rules);
131,407✔
2675
        }
2676

2677
        /* success, mark the snapshot as a shadow and return */
2678
        snap->shadow = true;
52,688✔
2679
        return;
52,688✔
2680

UNCOV
2681
shadow_err:
×
2682
        /* we failed making a shadow, cleanup and return */
UNCOV
2683
        col->snapshots = snap->next;
×
UNCOV
2684
        _db_snap_release(snap);
×
UNCOV
2685
        return;
×
2686
}
2687

2688
/**
2689
 * Precompute the seccomp filters
2690
 * @param col the filter collection
2691
 *
2692
 * This function precomputes the seccomp filters before they are needed,
2693
 * returns zero on success, negative values on error.
2694
 *
2695
 */
2696
int db_col_precompute(struct db_filter_col *col)
7,823✔
2697
{
2698
        if (!col->prgm_bpf)
7,823✔
2699
                return gen_bpf_generate(col, &col->prgm_bpf);
7,809✔
2700
        return 0;
2701
}
2702

2703
/**
2704
 * Free any precomputed filter programs
2705
 * @param col the filter collection
2706
 *
2707
 * This function releases any precomputed filter programs.
2708
 */
2709
void db_col_precompute_reset(struct db_filter_col *col)
87,104✔
2710
{
2711
        if (!col->prgm_bpf)
87,104✔
2712
                return;
2713

2714
        gen_bpf_release(col->prgm_bpf);
309✔
2715
        col->prgm_bpf = NULL;
7,809✔
2716
}
2717

NEW
2718
int db_add_known_syscalls(struct db_filter_col *col)
×
2719
{
NEW
2720
        struct db_api_rule_list *rule = NULL;
×
NEW
2721
        ssize_t chain_size;
×
NEW
2722
        struct db_api_arg *chain = NULL;
×
NEW
2723
        int rc, iter, syscall_num;
×
NEW
2724
        bool added;
×
2725

NEW
2726
        chain_size = sizeof(*chain) * ARG_COUNT_MAX;
×
NEW
2727
        chain = zmalloc(chain_size);
×
NEW
2728
        if (chain == NULL)
×
2729
                return -ENOMEM;
2730

2731
        /* create a checkpoint */
NEW
2732
        rc = db_col_transaction_start(col);
×
NEW
2733
        if (rc != 0)
×
NEW
2734
                goto add_failure;
×
2735

NEW
2736
        for (syscall_num = 0; syscall_num < MAX_SYSCALL_NUM; syscall_num++) {
×
NEW
2737
                for (iter = 0; iter < col->filter_cnt; iter++) {
×
NEW
2738
                        rule = _db_rule_new(1, col->attr.act_default,
×
2739
                                            syscall_num, chain);
NEW
2740
                        if (rule == NULL) {
×
NEW
2741
                                rc = -ENOMEM;
×
NEW
2742
                                goto add_failure;
×
2743
                        }
2744

NEW
2745
                        rc = arch_add_kver_rule(col->filters[iter], rule,
×
NEW
2746
                                                col->attr.kver, &added);
×
NEW
2747
                        if (added == false)
×
NEW
2748
                                free(rule);
×
NEW
2749
                        if (rc != 0)
×
NEW
2750
                                goto add_failure;
×
2751
                }
2752
        }
2753

NEW
2754
add_failure:
×
2755
        /* commit the transaction or abort */
NEW
2756
        if (rc == 0)
×
NEW
2757
                db_col_transaction_commit(col);
×
2758
        else
NEW
2759
                db_col_transaction_abort(col);
×
2760

NEW
2761
        if (chain != NULL)
×
NEW
2762
                free(chain);
×
2763

NEW
2764
        return 0;
×
2765
}
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