Bundle libseccomp 2.3.1
[linux-seccomp.git] / libseccomp / src / db.c
CommitLineData
8befd5cc
MG
1/**
2 * Enhanced Seccomp Filter DB
3 *
4 * Copyright (c) 2012,2016 Red Hat <pmoore@redhat.com>
5 * Author: Paul Moore <paul@paul-moore.com>
6 */
7
8/*
9 * This library is free software; you can redistribute it and/or modify it
10 * under the terms of version 2.1 of the GNU Lesser General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This library is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this library; if not, see <http://www.gnu.org/licenses>.
20 */
21
22#include <assert.h>
23#include <errno.h>
24#include <inttypes.h>
25#include <stdlib.h>
26#include <string.h>
27#include <stdarg.h>
28
29#include <seccomp.h>
30
31#include "arch.h"
32#include "db.h"
33#include "system.h"
34
35/* state values */
36#define _DB_STA_VALID 0xA1B2C3D4
37#define _DB_STA_FREED 0x1A2B3C4D
38
39/* the priority field is fairly simple - without any user hints, or in the case
40 * of a hint "tie", we give higher priority to syscalls with less chain nodes
41 * (filter is easier to evaluate) */
42#define _DB_PRI_MASK_CHAIN 0x0000FFFF
43#define _DB_PRI_MASK_USER 0x00FF0000
44#define _DB_PRI_USER(x) (((x) << 16) & _DB_PRI_MASK_USER)
45
46/* private structure for tracking the state of the sub-tree "pruning" */
47struct db_prune_state {
48 bool prefix_exist;
49 bool prefix_new;
50 bool matched;
51};
52
53static unsigned int _db_tree_free(struct db_arg_chain_tree *tree);
54
55/**
56 * Do not call this function directly, use _db_tree_free() instead
57 */
58static unsigned int __db_tree_free(struct db_arg_chain_tree *tree)
59{
60 int cnt;
61
62 if (tree == NULL || --(tree->refcnt) > 0)
63 return 0;
64
65 /* we assume the caller has ensured that 'tree->lvl_prv == NULL' */
66 cnt = __db_tree_free(tree->lvl_nxt);
67 cnt += _db_tree_free(tree->nxt_t);
68 cnt += _db_tree_free(tree->nxt_f);
69
70 free(tree);
71 return cnt + 1;
72}
73
74/**
75 * Free a syscall filter argument chain tree
76 * @param tree the argument chain list
77 *
78 * This function frees a tree and returns the number of nodes freed.
79 *
80 */
81static unsigned int _db_tree_free(struct db_arg_chain_tree *tree)
82{
83 struct db_arg_chain_tree *iter;
84
85 if (tree == NULL)
86 return 0;
87
88 iter = tree;
89 while (iter->lvl_prv != NULL)
90 iter = iter->lvl_prv;
91
92 return __db_tree_free(iter);
93}
94
95/**
96 * Remove a node from an argument chain tree
97 * @param tree the pointer to the tree
98 * @param node the node to remove
99 *
100 * This function searches the tree looking for the node and removes it once
101 * found. Returns the number of nodes freed.
102 *
103 */
104static unsigned int _db_tree_remove(struct db_arg_chain_tree **tree,
105 struct db_arg_chain_tree *node)
106{
107 int cnt = 0;
108 struct db_arg_chain_tree *c_iter;
109
110 if (tree == NULL || *tree == NULL || node == NULL)
111 return 0;
112
113 c_iter = *tree;
114 while (c_iter->lvl_prv != NULL)
115 c_iter = c_iter->lvl_prv;
116
117 do {
118 if (c_iter == node || db_chain_zombie(c_iter)) {
119 /* remove from the tree */
120 if (c_iter == *tree) {
121 if (c_iter->lvl_prv != NULL)
122 *tree = c_iter->lvl_prv;
123 else
124 *tree = c_iter->lvl_nxt;
125 }
126 if (c_iter->lvl_prv != NULL)
127 c_iter->lvl_prv->lvl_nxt = c_iter->lvl_nxt;
128 if (c_iter->lvl_nxt != NULL)
129 c_iter->lvl_nxt->lvl_prv = c_iter->lvl_prv;
130
131 /* free and return */
132 c_iter->lvl_prv = NULL;
133 c_iter->lvl_nxt = NULL;
134 cnt += _db_tree_free(c_iter);
135 return cnt;
136 }
137
138 /* check the true/false sub-trees */
139 cnt += _db_tree_remove(&(c_iter->nxt_t), node);
140 cnt += _db_tree_remove(&(c_iter->nxt_f), node);
141
142 c_iter = c_iter->lvl_nxt;
143 } while (c_iter != NULL);
144
145 return cnt;
146}
147
148/**
149 * Traverse a tree checking the action values
150 * @param tree the pointer to the tree
151 * @param action the action
152 *
153 * Traverse the tree inspecting each action to see if it matches the given
154 * action. Returns zero if all actions match the given action, negative values
155 * on failure.
156 *
157 */
158static int _db_tree_act_check(struct db_arg_chain_tree *tree, uint32_t action)
159{
160 int rc;
161 struct db_arg_chain_tree *c_iter;
162
163 if (tree == NULL)
164 return 0;
165
166 c_iter = tree;
167 while (c_iter->lvl_prv != NULL)
168 c_iter = c_iter->lvl_prv;
169
170 do {
171 if (c_iter->act_t_flg && c_iter->act_t != action)
172 return -EEXIST;
173 if (c_iter->act_f_flg && c_iter->act_f != action)
174 return -EEXIST;
175
176 rc = _db_tree_act_check(c_iter->nxt_t, action);
177 if (rc < 0)
178 return rc;
179 rc = _db_tree_act_check(c_iter->nxt_f, action);
180 if (rc < 0)
181 return rc;
182
183 c_iter = c_iter->lvl_nxt;
184 } while (c_iter != NULL);
185
186 return 0;
187}
188
189/**
190 * Checks for a sub-tree match in an existing tree and prunes the tree
191 * @param prev the head of the existing tree or sub-tree
192 * @param existing the starting point into the existing tree
193 * @param new pointer to the new tree
194 * @param state pointer to the pruning state
195 *
196 * This function searches the existing and new trees trying to prune each to
197 * eliminate redundancy. Returns the number of nodes removed from the tree on
198 * success, zero if no changes were made, and negative values if the new tree
199 * should be discarded.
200 *
201 */
202static int _db_tree_sub_prune(struct db_arg_chain_tree **prev,
203 struct db_arg_chain_tree *existing,
204 struct db_arg_chain_tree *new,
205 struct db_prune_state *state)
206{
207 int rc = 0;
208 int rc_tmp;
209 struct db_arg_chain_tree *ec_iter;
210 struct db_arg_chain_tree *ec_iter_tmp;
211 struct db_arg_chain_tree *c_iter;
212 struct db_prune_state state_new;
213
214 if (!state || !existing || !new)
215 return 0;
216
217 ec_iter = existing;
218 c_iter = new;
219 do {
220 if (db_chain_eq(ec_iter, c_iter)) {
221 /* equal */
222
223 if (db_chain_leaf(c_iter)) {
224 /* leaf */
225 if (db_chain_eq_result(ec_iter, c_iter)) {
226 /* identical results */
227 if (prev != NULL)
228 return _db_tree_remove(prev,
229 ec_iter);
230 else
231 return -1;
232 }
233 if (c_iter->act_t_flg && ec_iter->nxt_t) {
234 /* new is shorter (true) */
235 if (prev == NULL)
236 return -1;
237 rc += _db_tree_remove(&(ec_iter->nxt_t),
238 ec_iter->nxt_t);
239 ec_iter->act_t = c_iter->act_t;
240 ec_iter->act_t_flg = true;
241 }
242 if (c_iter->act_f_flg && ec_iter->nxt_f) {
243 /* new is shorter (false) */
244 if (prev == NULL)
245 return -1;
246 rc += _db_tree_remove(&(ec_iter->nxt_f),
247 ec_iter->nxt_f);
248 ec_iter->act_f = c_iter->act_f;
249 ec_iter->act_f_flg = true;
250 }
251
252 return rc;
253 }
254
255 if (c_iter->nxt_t && ec_iter->act_t_flg)
256 /* existing is shorter (true) */
257 return -1;
258 if (c_iter->nxt_f && ec_iter->act_f_flg)
259 /* existing is shorter (false) */
260 return -1;
261
262 if (c_iter->nxt_t) {
263 state_new = *state;
264 state_new.matched = true;
265 rc_tmp = _db_tree_sub_prune((prev ?
266 &ec_iter : NULL),
267 ec_iter->nxt_t,
268 c_iter->nxt_t,
269 &state_new);
270 rc += (rc_tmp > 0 ? rc_tmp : 0);
271 if (state->prefix_new && rc_tmp < 0)
272 return (rc > 0 ? rc : rc_tmp);
273 }
274 if (c_iter->nxt_f) {
275 state_new = *state;
276 state_new.matched = true;
277 rc_tmp = _db_tree_sub_prune((prev ?
278 &ec_iter : NULL),
279 ec_iter->nxt_f,
280 c_iter->nxt_f,
281 &state_new);
282 rc += (rc_tmp > 0 ? rc_tmp : 0);
283 if (state->prefix_new && rc_tmp < 0)
284 return (rc > 0 ? rc : rc_tmp);
285 }
286 } else if (db_chain_lt(ec_iter, c_iter)) {
287 /* less than */
288 if (state->matched || state->prefix_new)
289 goto next;
290 state_new = *state;
291 state_new.prefix_exist = true;
292
293 if (ec_iter->nxt_t) {
294 rc_tmp = _db_tree_sub_prune((prev ?
295 &ec_iter : NULL),
296 ec_iter->nxt_t,
297 c_iter,
298 &state_new);
299 rc += (rc_tmp > 0 ? rc_tmp : 0);
300 }
301 if (ec_iter->nxt_f) {
302 rc_tmp = _db_tree_sub_prune((prev ?
303 &ec_iter : NULL),
304 ec_iter->nxt_f,
305 c_iter,
306 &state_new);
307 rc += (rc_tmp > 0 ? rc_tmp : 0);
308 }
309 } else if (db_chain_gt(ec_iter, c_iter)) {
310 /* greater than */
311 if (state->matched || state->prefix_exist)
312 goto next;
313 state_new = *state;
314 state_new.prefix_new = true;
315
316 if (c_iter->nxt_t) {
317 rc_tmp = _db_tree_sub_prune(NULL,
318 ec_iter,
319 c_iter->nxt_t,
320 &state_new);
321 rc += (rc_tmp > 0 ? rc_tmp : 0);
322 if (rc_tmp < 0)
323 return (rc > 0 ? rc : rc_tmp);
324 }
325 if (c_iter->nxt_f) {
326 rc_tmp = _db_tree_sub_prune(NULL,
327 ec_iter,
328 c_iter->nxt_f,
329 &state_new);
330 rc += (rc_tmp > 0 ? rc_tmp : 0);
331 if (rc_tmp < 0)
332 return (rc > 0 ? rc : rc_tmp);
333 }
334 }
335
336next:
337 /* re-check current node and advance to the next node */
338 if (db_chain_zombie(ec_iter)) {
339 ec_iter_tmp = ec_iter->lvl_nxt;
340 rc += _db_tree_remove(prev, ec_iter);
341 ec_iter = ec_iter_tmp;
342 } else
343 ec_iter = ec_iter->lvl_nxt;
344 } while (ec_iter);
345
346 return rc;
347}
348
349/**
350 * Free and reset the seccomp filter DB
351 * @param db the seccomp filter DB
352 *
353 * This function frees any existing filters and resets the filter DB to a
354 * default state; only the DB architecture is preserved.
355 *
356 */
357static void _db_reset(struct db_filter *db)
358{
359 struct db_sys_list *s_iter;
360 struct db_api_rule_list *r_iter;
361
362 if (db == NULL)
363 return;
364
365 /* free any filters */
366 if (db->syscalls != NULL) {
367 s_iter = db->syscalls;
368 while (s_iter != NULL) {
369 db->syscalls = s_iter->next;
370 _db_tree_free(s_iter->chains);
371 free(s_iter);
372 s_iter = db->syscalls;
373 }
374 db->syscalls = NULL;
375 }
376
377 /* free any rules */
378 if (db->rules != NULL) {
379 /* split the loop first then loop and free */
380 db->rules->prev->next = NULL;
381 r_iter = db->rules;
382 while (r_iter != NULL) {
383 db->rules = r_iter->next;
384 free(r_iter->args);
385 free(r_iter);
386 r_iter = db->rules;
387 }
388 db->rules = NULL;
389 }
390}
391
392/**
393 * Intitalize a seccomp filter DB
394 * @param arch the architecture definition
395 *
396 * This function initializes a seccomp filter DB and readies it for use.
397 * Returns a pointer to the DB on success, NULL on failure.
398 *
399 */
400static struct db_filter *_db_init(const struct arch_def *arch)
401{
402 struct db_filter *db;
403
404 db = malloc(sizeof(*db));
405 if (db == NULL)
406 return NULL;
407
408 /* clear the buffer for the first time and set the arch */
409 memset(db, 0, sizeof(*db));
410 db->arch = arch;
411
412 /* reset the DB to a known state */
413 _db_reset(db);
414
415 return db;
416}
417
418/**
419 * Destroy a seccomp filter DB
420 * @param db the seccomp filter DB
421 *
422 * This function destroys a seccomp filter DB. After calling this function,
423 * the filter should no longer be referenced.
424 *
425 */
426static void _db_release(struct db_filter *db)
427{
428 if (db == NULL)
429 return;
430
431 /* free and reset the DB */
432 _db_reset(db);
433 free(db);
434}
435
436/**
437 * Destroy a seccomp filter snapshot
438 * @param snap the seccomp filter snapshot
439 *
440 * This function destroys a seccomp filter snapshot. After calling this
441 * function, the snapshot should no longer be referenced.
442 *
443 */
444static void _db_snap_release(struct db_filter_snap *snap)
445{
446 unsigned int iter;
447
448 if (snap->filter_cnt > 0) {
449 for (iter = 0; iter < snap->filter_cnt; iter++) {
450 if (snap->filters[iter])
451 _db_release(snap->filters[iter]);
452 }
453 free(snap->filters);
454 }
455 free(snap);
456}
457
458/**
459 * Update the user specified portion of the syscall priority
460 * @param db the seccomp filter db
461 * @param syscall the syscall number
462 * @param priority the syscall priority
463 *
464 * This function sets, or updates, the syscall priority; the highest priority
465 * value between the existing and specified value becomes the new syscall
466 * priority. If the syscall entry does not already exist, a new phantom
467 * syscall entry is created as a placeholder. Returns zero on success,
468 * negative values on failure.
469 *
470 */
471static int _db_syscall_priority(struct db_filter *db,
472 int syscall, uint8_t priority)
473{
474 unsigned int sys_pri = _DB_PRI_USER(priority);
475 struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
476
477 assert(db != NULL);
478
479 s_iter = db->syscalls;
480 while (s_iter != NULL && s_iter->num < syscall) {
481 s_prev = s_iter;
482 s_iter = s_iter->next;
483 }
484
485 /* matched an existing syscall entry */
486 if (s_iter != NULL && s_iter->num == syscall) {
487 if (sys_pri > (s_iter->priority & _DB_PRI_MASK_USER)) {
488 s_iter->priority &= (~_DB_PRI_MASK_USER);
489 s_iter->priority |= sys_pri;
490 }
491 return 0;
492 }
493
494 /* no existing syscall entry - create a phantom entry */
495 s_new = malloc(sizeof(*s_new));
496 if (s_new == NULL)
497 return -ENOMEM;
498 memset(s_new, 0, sizeof(*s_new));
499 s_new->num = syscall;
500 s_new->priority = sys_pri;
501 s_new->valid = false;
502
503 /* add it before s_iter */
504 if (s_prev != NULL) {
505 s_new->next = s_prev->next;
506 s_prev->next = s_new;
507 } else {
508 s_new->next = db->syscalls;
509 db->syscalls = s_new;
510 }
511
512 return 0;
513}
514
515/**
516 * Free and reset the seccomp filter collection
517 * @param col the seccomp filter collection
518 * @param def_action the default filter action
519 *
520 * This function frees any existing filter DBs and resets the collection to a
521 * default state. In the case of failure the filter collection may be in an
522 * unknown state and should be released. Returns zero on success, negative
523 * values on failure.
524 *
525 */
526int db_col_reset(struct db_filter_col *col, uint32_t def_action)
527{
528 unsigned int iter;
529 struct db_filter *db;
530 struct db_filter_snap *snap;
531
532 if (col == NULL)
533 return -EINVAL;
534
535 /* free any filters */
536 for (iter = 0; iter < col->filter_cnt; iter++)
537 _db_release(col->filters[iter]);
538 col->filter_cnt = 0;
539 if (col->filters)
540 free(col->filters);
541 col->filters = NULL;
542
543 /* set the endianess to undefined */
544 col->endian = 0;
545
546 /* set the default attribute values */
547 col->attr.act_default = def_action;
548 col->attr.act_badarch = SCMP_ACT_KILL;
549 col->attr.nnp_enable = 1;
550 col->attr.tsync_enable = 0;
551
552 /* set the state */
553 col->state = _DB_STA_VALID;
554
555 /* reset the initial db */
556 db = _db_init(arch_def_native);
557 if (db == NULL)
558 return -ENOMEM;
559 if (db_col_db_add(col, db) < 0) {
560 _db_release(db);
561 return -ENOMEM;
562 }
563
564 /* reset the transactions */
565 while (col->snapshots) {
566 snap = col->snapshots;
567 col->snapshots = snap->next;
568 for (iter = 0; iter < snap->filter_cnt; iter++)
569 _db_release(snap->filters[iter]);
570 free(snap->filters);
571 free(snap);
572 }
573
574 return 0;
575}
576
577/**
578 * Intitalize a seccomp filter collection
579 * @param def_action the default filter action
580 *
581 * This function initializes a seccomp filter collection and readies it for
582 * use. Returns a pointer to the collection on success, NULL on failure.
583 *
584 */
585struct db_filter_col *db_col_init(uint32_t def_action)
586{
587 struct db_filter_col *col;
588
589 col = malloc(sizeof(*col));
590 if (col == NULL)
591 return NULL;
592
593 /* clear the buffer for the first time */
594 memset(col, 0, sizeof(*col));
595
596 /* reset the DB to a known state */
597 if (db_col_reset(col, def_action) < 0)
598 goto init_failure;
599
600 return col;
601
602init_failure:
603 db_col_release(col);
604 return NULL;
605}
606
607/**
608 * Destroy a seccomp filter collection
609 * @param col the seccomp filter collection
610 *
611 * This function destroys a seccomp filter collection. After calling this
612 * function, the filter should no longer be referenced.
613 *
614 */
615void db_col_release(struct db_filter_col *col)
616{
617 unsigned int iter;
618
619 if (col == NULL)
620 return;
621
622 /* set the state, just in case */
623 col->state = _DB_STA_FREED;
624
625 /* free any filters */
626 for (iter = 0; iter < col->filter_cnt; iter++)
627 _db_release(col->filters[iter]);
628 col->filter_cnt = 0;
629 if (col->filters)
630 free(col->filters);
631 col->filters = NULL;
632
633 /* free the collection */
634 free(col);
635}
636
637/**
638 * Validate the seccomp action
639 * @param action the seccomp action
640 *
641 * Verify that the given action is a valid seccomp action; return zero if
642 * valid, -EINVAL if invalid.
643 */
644int db_action_valid(uint32_t action)
645{
646 if (action == SCMP_ACT_KILL)
647 return 0;
648 else if (action == SCMP_ACT_TRAP)
649 return 0;
650 else if ((action == SCMP_ACT_ERRNO(action & 0x0000ffff)) &&
651 ((action & 0x0000ffff) < MAX_ERRNO))
652 return 0;
653 else if (action == SCMP_ACT_TRACE(action & 0x0000ffff))
654 return 0;
655 else if (action == SCMP_ACT_ALLOW)
656 return 0;
657
658 return -EINVAL;
659}
660
661/**
662 * Validate a filter collection
663 * @param col the seccomp filter collection
664 *
665 * This function validates a seccomp filter collection. Returns zero if the
666 * collection is valid, negative values on failure.
667 *
668 */
669int db_col_valid(struct db_filter_col *col)
670{
671 if (col != NULL && col->state == _DB_STA_VALID && col->filter_cnt > 0)
672 return 0;
673 return -EINVAL;
674}
675
676/**
677 * Merge two filter collections
678 * @param col_dst the destination filter collection
679 * @param col_src the source filter collection
680 *
681 * This function merges two filter collections into the given destination
682 * collection. The source filter collection is no longer valid if the function
683 * returns successfully. Returns zero on success, negative values on failure.
684 *
685 */
686int db_col_merge(struct db_filter_col *col_dst, struct db_filter_col *col_src)
687{
688 unsigned int iter_a, iter_b;
689 struct db_filter **dbs;
690
691 /* verify that the endianess is a match */
692 if (col_dst->endian != col_src->endian)
693 return -EEXIST;
694
695 /* make sure we don't have any arch/filter collisions */
696 for (iter_a = 0; iter_a < col_dst->filter_cnt; iter_a++) {
697 for (iter_b = 0; iter_b < col_src->filter_cnt; iter_b++) {
698 if (col_dst->filters[iter_a]->arch->token ==
699 col_src->filters[iter_b]->arch->token)
700 return -EEXIST;
701 }
702 }
703
704 /* expand the destination */
705 dbs = realloc(col_dst->filters,
706 sizeof(struct db_filter *) *
707 (col_dst->filter_cnt + col_src->filter_cnt));
708 if (dbs == NULL)
709 return -ENOMEM;
710 col_dst->filters = dbs;
711
712 /* transfer the architecture filters */
713 for (iter_a = col_dst->filter_cnt, iter_b = 0;
714 iter_b < col_src->filter_cnt; iter_a++, iter_b++) {
715 col_dst->filters[iter_a] = col_src->filters[iter_b];
716 col_dst->filter_cnt++;
717 }
718
719 /* free the source */
720 col_src->filter_cnt = 0;
721 db_col_release(col_src);
722
723 return 0;
724}
725
726/**
727 * Check to see if an architecture filter exists in the filter collection
728 * @param col the seccomp filter collection
729 * @param arch_token the architecture token
730 *
731 * Iterate through the given filter collection checking to see if a filter
732 * exists for the specified architecture. Returns -EEXIST if a filter is found,
733 * zero if a matching filter does not exist.
734 *
735 */
736int db_col_arch_exist(struct db_filter_col *col, uint32_t arch_token)
737{
738 unsigned int iter;
739
740 for (iter = 0; iter < col->filter_cnt; iter++)
741 if (col->filters[iter]->arch->token == arch_token)
742 return -EEXIST;
743
744 return 0;
745}
746
747/**
748 * Get a filter attribute
749 * @param col the seccomp filter collection
750 * @param attr the filter attribute
751 * @param value the filter attribute value
752 *
753 * Get the requested filter attribute and provide it via @value. Returns zero
754 * on success, negative values on failure.
755 *
756 */
757int db_col_attr_get(const struct db_filter_col *col,
758 enum scmp_filter_attr attr, uint32_t *value)
759{
760 int rc = 0;
761
762 switch (attr) {
763 case SCMP_FLTATR_ACT_DEFAULT:
764 *value = col->attr.act_default;
765 break;
766 case SCMP_FLTATR_ACT_BADARCH:
767 *value = col->attr.act_badarch;
768 break;
769 case SCMP_FLTATR_CTL_NNP:
770 *value = col->attr.nnp_enable;
771 break;
772 case SCMP_FLTATR_CTL_TSYNC:
773 *value = col->attr.tsync_enable;
774 break;
775 default:
776 rc = -EEXIST;
777 break;
778 }
779
780 return rc;
781}
782
783/**
784 * Set a filter attribute
785 * @param col the seccomp filter collection
786 * @param attr the filter attribute
787 * @param value the filter attribute value
788 *
789 * Set the requested filter attribute with the given value. Returns zero on
790 * success, negative values on failure.
791 *
792 */
793int db_col_attr_set(struct db_filter_col *col,
794 enum scmp_filter_attr attr, uint32_t value)
795{
796 int rc = 0;
797
798 switch (attr) {
799 case SCMP_FLTATR_ACT_DEFAULT:
800 /* read only */
801 return -EACCES;
802 break;
803 case SCMP_FLTATR_ACT_BADARCH:
804 if (db_action_valid(value) == 0)
805 col->attr.act_badarch = value;
806 else
807 return -EINVAL;
808 break;
809 case SCMP_FLTATR_CTL_NNP:
810 col->attr.nnp_enable = (value ? 1 : 0);
811 break;
812 case SCMP_FLTATR_CTL_TSYNC:
813 rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC);
814 if (rc == 1) {
815 /* supported */
816 rc = 0;
817 col->attr.tsync_enable = (value ? 1 : 0);
818 } else if (rc == 0)
819 /* unsupported */
820 rc = -EOPNOTSUPP;
821 break;
822 default:
823 rc = -EEXIST;
824 break;
825 }
826
827 return rc;
828}
829
830/**
831 * Add a new architecture filter to a filter collection
832 * @param col the seccomp filter collection
833 * @param arch the architecture
834 *
835 * This function adds a new architecture filter DB to an existing seccomp
836 * filter collection assuming there isn't a filter DB already present with the
837 * same architecture. Returns zero on success, negative values on failure.
838 *
839 */
840int db_col_db_new(struct db_filter_col *col, const struct arch_def *arch)
841{
842 int rc;
843 struct db_filter *db;
844
845 db = _db_init(arch);
846 if (db == NULL)
847 return -ENOMEM;
848 rc = db_col_db_add(col, db);
849 if (rc < 0)
850 _db_release(db);
851
852 return rc;
853}
854
855/**
856 * Add a new filter DB to a filter collection
857 * @param col the seccomp filter collection
858 * @param db the seccomp filter DB
859 *
860 * This function adds an existing seccomp filter DB to an existing seccomp
861 * filter collection assuming there isn't a filter DB already present with the
862 * same architecture. Returns zero on success, negative values on failure.
863 *
864 */
865int db_col_db_add(struct db_filter_col *col, struct db_filter *db)
866{
867 struct db_filter **dbs;
868
869 if (col->endian != 0 && col->endian != db->arch->endian)
870 return -EEXIST;
871
872 if (db_col_arch_exist(col, db->arch->token))
873 return -EEXIST;
874
875 dbs = realloc(col->filters,
876 sizeof(struct db_filter *) * (col->filter_cnt + 1));
877 if (dbs == NULL)
878 return -ENOMEM;
879 col->filters = dbs;
880 col->filter_cnt++;
881 col->filters[col->filter_cnt - 1] = db;
882 if (col->endian == 0)
883 col->endian = db->arch->endian;
884
885 return 0;
886}
887
888/**
889 * Remove a filter DB from a filter collection
890 * @param col the seccomp filter collection
891 * @param arch_token the architecture token
892 *
893 * This function removes an existing seccomp filter DB from an existing seccomp
894 * filter collection. Returns zero on success, negative values on failure.
895 *
896 */
897int db_col_db_remove(struct db_filter_col *col, uint32_t arch_token)
898{
899 unsigned int iter;
900 unsigned int found;
901 struct db_filter **dbs;
902
903 if ((col->filter_cnt <= 0) || (db_col_arch_exist(col, arch_token) == 0))
904 return -EINVAL;
905
906 for (found = 0, iter = 0; iter < col->filter_cnt; iter++) {
907 if (found)
908 col->filters[iter - 1] = col->filters[iter];
909 else if (col->filters[iter]->arch->token == arch_token) {
910 _db_release(col->filters[iter]);
911 found = 1;
912 }
913 }
914 col->filters[--col->filter_cnt] = NULL;
915
916 if (col->filter_cnt > 0) {
917 /* NOTE: if we can't do the realloc it isn't fatal, we just
918 * have some extra space allocated */
919 dbs = realloc(col->filters,
920 sizeof(struct db_filter *) * col->filter_cnt);
921 if (dbs != NULL)
922 col->filters = dbs;
923 } else {
924 /* this was the last filter so free all the associated memory
925 * and reset the endian token */
926 free(col->filters);
927 col->filters = NULL;
928 col->endian = 0;
929 }
930
931 return 0;
932}
933
934/**
935 * Test if the argument filter can be skipped because it's a tautology
936 * @param arg argument filter
937 *
938 * If this argument filter applied to the lower 32 bit can be skipped this
939 * function returns false.
940 *
941 */
942static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg)
943{
944 if (arg->op == SCMP_CMP_MASKED_EQ && D64_LO(arg->mask) == 0)
945 return false;
946
947 return true;
948}
949
950/**
951 * Test if the argument filter can be skipped because it's a tautology
952 * @param arg argument filter
953 *
954 * If this argument filter applied to the upper 32 bit can be skipped this
955 * function returns false.
956 *
957 */
958static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg)
959{
960 if (arg->op == SCMP_CMP_MASKED_EQ && D64_HI(arg->mask) == 0)
961 return false;
962
963 return true;
964}
965
966/**
967 * Fixup the node based on the op/mask
968 * @param node the chain node
969 *
970 * Ensure the datum is masked as well.
971 *
972 */
973static void _db_node_mask_fixup(struct db_arg_chain_tree *node)
974{
975 node->datum &= node->mask;
976}
977
978/**
979 * Generate a new filter rule for a 64 bit system
980 * @param arch the architecture definition
981 * @param action the filter action
982 * @param syscall the syscall number
983 * @param chain argument filter chain
984 *
985 * This function generates a new syscall filter for a 64 bit system. Returns
986 * zero on success, negative values on failure.
987 *
988 */
989static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
990 uint32_t action,
991 unsigned int syscall,
992 struct db_api_arg *chain)
993{
994 unsigned int iter;
995 int chain_len_max;
996 struct db_sys_list *s_new;
997 struct db_arg_chain_tree *c_iter_hi = NULL, *c_iter_lo = NULL;
998 struct db_arg_chain_tree *c_prev_hi = NULL, *c_prev_lo = NULL;
999 bool tf_flag;
1000
1001 s_new = malloc(sizeof(*s_new));
1002 if (s_new == NULL)
1003 return NULL;
1004 memset(s_new, 0, sizeof(*s_new));
1005 s_new->num = syscall;
1006 s_new->valid = true;
1007 /* run through the argument chain */
1008 chain_len_max = arch_arg_count_max(arch);
1009 if (chain_len_max < 0)
1010 goto gen_64_failure;
1011 for (iter = 0; iter < chain_len_max; iter++) {
1012 if (chain[iter].valid == 0)
1013 continue;
1014
1015 /* TODO: handle the case were either hi or lo isn't needed */
1016
1017 /* skip generating instruction which are no-ops */
1018 if (!_db_arg_cmp_need_hi(&chain[iter]) &&
1019 !_db_arg_cmp_need_lo(&chain[iter]))
1020 continue;
1021
1022 c_iter_hi = malloc(sizeof(*c_iter_hi));
1023 if (c_iter_hi == NULL)
1024 goto gen_64_failure;
1025 memset(c_iter_hi, 0, sizeof(*c_iter_hi));
1026 c_iter_hi->refcnt = 1;
1027 c_iter_lo = malloc(sizeof(*c_iter_lo));
1028 if (c_iter_lo == NULL) {
1029 free(c_iter_hi);
1030 goto gen_64_failure;
1031 }
1032 memset(c_iter_lo, 0, sizeof(*c_iter_lo));
1033 c_iter_lo->refcnt = 1;
1034
1035 /* link this level to the previous level */
1036 if (c_prev_lo != NULL) {
1037 if (!tf_flag) {
1038 c_prev_lo->nxt_f = c_iter_hi;
1039 c_prev_hi->nxt_f = c_iter_hi;
1040 c_iter_hi->refcnt++;
1041 } else
1042 c_prev_lo->nxt_t = c_iter_hi;
1043 } else
1044 s_new->chains = c_iter_hi;
1045 s_new->node_cnt += 2;
1046
1047 /* set the arg, op, and datum fields */
1048 c_iter_hi->arg = chain[iter].arg;
1049 c_iter_lo->arg = chain[iter].arg;
1050 c_iter_hi->arg_offset = arch_arg_offset_hi(arch,
1051 c_iter_hi->arg);
1052 c_iter_lo->arg_offset = arch_arg_offset_lo(arch,
1053 c_iter_lo->arg);
1054 switch (chain[iter].op) {
1055 case SCMP_CMP_GT:
1056 c_iter_hi->op = SCMP_CMP_GE;
1057 c_iter_lo->op = SCMP_CMP_GT;
1058 tf_flag = true;
1059 break;
1060 case SCMP_CMP_NE:
1061 c_iter_hi->op = SCMP_CMP_EQ;
1062 c_iter_lo->op = SCMP_CMP_EQ;
1063 tf_flag = false;
1064 break;
1065 case SCMP_CMP_LT:
1066 c_iter_hi->op = SCMP_CMP_GE;
1067 c_iter_lo->op = SCMP_CMP_GE;
1068 tf_flag = false;
1069 break;
1070 case SCMP_CMP_LE:
1071 c_iter_hi->op = SCMP_CMP_GE;
1072 c_iter_lo->op = SCMP_CMP_GT;
1073 tf_flag = false;
1074 break;
1075 default:
1076 c_iter_hi->op = chain[iter].op;
1077 c_iter_lo->op = chain[iter].op;
1078 tf_flag = true;
1079 }
1080 c_iter_hi->mask = D64_HI(chain[iter].mask);
1081 c_iter_lo->mask = D64_LO(chain[iter].mask);
1082 c_iter_hi->datum = D64_HI(chain[iter].datum);
1083 c_iter_lo->datum = D64_LO(chain[iter].datum);
1084
1085 /* fixup the mask/datum */
1086 _db_node_mask_fixup(c_iter_hi);
1087 _db_node_mask_fixup(c_iter_lo);
1088
1089 /* link the hi and lo chain nodes */
1090 c_iter_hi->nxt_t = c_iter_lo;
1091
1092 c_prev_hi = c_iter_hi;
1093 c_prev_lo = c_iter_lo;
1094 }
1095 if (c_iter_lo != NULL) {
1096 /* set the leaf node */
1097 if (!tf_flag) {
1098 c_iter_lo->act_f_flg = true;
1099 c_iter_lo->act_f = action;
1100 c_iter_hi->act_f_flg = true;
1101 c_iter_hi->act_f = action;
1102 } else {
1103 c_iter_lo->act_t_flg = true;
1104 c_iter_lo->act_t = action;
1105 }
1106 } else
1107 s_new->action = action;
1108
1109 return s_new;
1110
1111gen_64_failure:
1112 /* free the new chain and its syscall struct */
1113 _db_tree_free(s_new->chains);
1114 free(s_new);
1115 return NULL;
1116}
1117
1118/**
1119 * Generate a new filter rule for a 32 bit system
1120 * @param arch the architecture definition
1121 * @param action the filter action
1122 * @param syscall the syscall number
1123 * @param chain argument filter chain
1124 *
1125 * This function generates a new syscall filter for a 32 bit system. Returns
1126 * zero on success, negative values on failure.
1127 *
1128 */
1129static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch,
1130 uint32_t action,
1131 unsigned int syscall,
1132 struct db_api_arg *chain)
1133{
1134 unsigned int iter;
1135 int chain_len_max;
1136 struct db_sys_list *s_new;
1137 struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL;
1138 bool tf_flag;
1139
1140 s_new = malloc(sizeof(*s_new));
1141 if (s_new == NULL)
1142 return NULL;
1143 memset(s_new, 0, sizeof(*s_new));
1144 s_new->num = syscall;
1145 s_new->valid = true;
1146 /* run through the argument chain */
1147 chain_len_max = arch_arg_count_max(arch);
1148 if (chain_len_max < 0)
1149 goto gen_32_failure;
1150 for (iter = 0; iter < chain_len_max; iter++) {
1151 if (chain[iter].valid == 0)
1152 continue;
1153
1154 /* skip generating instructions which are no-ops */
1155 if (!_db_arg_cmp_need_lo(&chain[iter]))
1156 continue;
1157
1158 c_iter = malloc(sizeof(*c_iter));
1159 if (c_iter == NULL)
1160 goto gen_32_failure;
1161 memset(c_iter, 0, sizeof(*c_iter));
1162 c_iter->refcnt = 1;
1163 c_iter->arg = chain[iter].arg;
1164 c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg);
1165 c_iter->op = chain[iter].op;
1166 /* implicitly strips off the upper 32 bit */
1167 c_iter->mask = chain[iter].mask;
1168 c_iter->datum = chain[iter].datum;
1169
1170 /* link in the new node and update the chain */
1171 if (c_prev != NULL) {
1172 if (tf_flag)
1173 c_prev->nxt_t = c_iter;
1174 else
1175 c_prev->nxt_f = c_iter;
1176 } else
1177 s_new->chains = c_iter;
1178 s_new->node_cnt++;
1179
1180 /* rewrite the op to reduce the op/datum combos */
1181 switch (c_iter->op) {
1182 case SCMP_CMP_NE:
1183 c_iter->op = SCMP_CMP_EQ;
1184 tf_flag = false;
1185 break;
1186 case SCMP_CMP_LT:
1187 c_iter->op = SCMP_CMP_GE;
1188 tf_flag = false;
1189 break;
1190 case SCMP_CMP_LE:
1191 c_iter->op = SCMP_CMP_GT;
1192 tf_flag = false;
1193 break;
1194 default:
1195 tf_flag = true;
1196 }
1197
1198 /* fixup the mask/datum */
1199 _db_node_mask_fixup(c_iter);
1200
1201 c_prev = c_iter;
1202 }
1203 if (c_iter != NULL) {
1204 /* set the leaf node */
1205 if (tf_flag) {
1206 c_iter->act_t_flg = true;
1207 c_iter->act_t = action;
1208 } else {
1209 c_iter->act_f_flg = true;
1210 c_iter->act_f = action;
1211 }
1212 } else
1213 s_new->action = action;
1214
1215 return s_new;
1216
1217gen_32_failure:
1218 /* free the new chain and its syscall struct */
1219 _db_tree_free(s_new->chains);
1220 free(s_new);
1221 return NULL;
1222}
1223
1224/**
1225 * Add a new rule to the seccomp filter DB
1226 * @param db the seccomp filter db
1227 * @param rule the filter rule
1228 *
1229 * This function adds a new syscall filter to the seccomp filter DB, adding to
1230 * the existing filters for the syscall, unless no argument specific filters
1231 * are present (filtering only on the syscall). When adding new chains, the
1232 * shortest chain, or most inclusive filter match, will be entered into the
1233 * filter DB. Returns zero on success, negative values on failure.
1234 *
1235 */
1236int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule)
1237{
1238 int rc = -ENOMEM;
1239 int syscall = rule->syscall;
1240 uint32_t action = rule->action;
1241 struct db_api_arg *chain = rule->args;
1242 struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
1243 struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL;
1244 struct db_arg_chain_tree *ec_iter;
1245 struct db_prune_state state;
1246 bool rm_flag = false;
1247 unsigned int new_chain_cnt = 0;
1248 unsigned int n_cnt;
1249
1250 assert(db != NULL);
1251
1252 /* do all our possible memory allocation up front so we don't have to
1253 * worry about failure once we get to the point where we start updating
1254 * the filter db */
1255 if (db->arch->size == ARCH_SIZE_64)
1256 s_new = _db_rule_gen_64(db->arch, action, syscall, chain);
1257 else if (db->arch->size == ARCH_SIZE_32)
1258 s_new = _db_rule_gen_32(db->arch, action, syscall, chain);
1259 else
1260 return -EFAULT;
1261 if (s_new == NULL)
1262 return -ENOMEM;
1263 new_chain_cnt = s_new->node_cnt;
1264
1265 /* no more failures allowed after this point that would result in the
1266 * stored filter being in an inconsistent state */
1267
1268 /* find a matching syscall/chain or insert a new one */
1269 s_iter = db->syscalls;
1270 while (s_iter != NULL && s_iter->num < syscall) {
1271 s_prev = s_iter;
1272 s_iter = s_iter->next;
1273 }
1274add_reset:
1275 s_new->node_cnt = new_chain_cnt;
1276 s_new->priority = _DB_PRI_MASK_CHAIN - s_new->node_cnt;
1277 c_prev = NULL;
1278 c_iter = s_new->chains;
1279 if (s_iter != NULL)
1280 ec_iter = s_iter->chains;
1281 else
1282 ec_iter = NULL;
1283 if (s_iter == NULL || s_iter->num != syscall) {
1284 /* new syscall, add before s_iter */
1285 if (s_prev != NULL) {
1286 s_new->next = s_prev->next;
1287 s_prev->next = s_new;
1288 } else {
1289 s_new->next = db->syscalls;
1290 db->syscalls = s_new;
1291 }
1292 return 0;
1293 } else if (s_iter->chains == NULL) {
1294 if (rm_flag || !s_iter->valid) {
1295 /* we are here because our previous pass cleared the
1296 * entire syscall chain when searching for a subtree
1297 * match or the existing syscall entry is a phantom,
1298 * so either way add the new chain */
1299 s_iter->chains = s_new->chains;
1300 s_iter->action = s_new->action;
1301 s_iter->node_cnt = s_new->node_cnt;
1302 if (s_iter->valid)
1303 s_iter->priority = s_new->priority;
1304 s_iter->valid = true;
1305 free(s_new);
1306 rc = 0;
1307 goto add_priority_update;
1308 } else
1309 /* syscall exists without any chains - existing filter
1310 * is at least as large as the new entry so cleanup and
1311 * exit */
1312 goto add_free_ok;
1313 } else if (s_iter->chains != NULL && s_new->chains == NULL) {
1314 /* syscall exists with chains but the new filter has no chains
1315 * so we need to clear the existing chains and exit */
1316 _db_tree_free(s_iter->chains);
1317 s_iter->chains = NULL;
1318 s_iter->node_cnt = 0;
1319 s_iter->action = action;
1320 goto add_free_ok;
1321 }
1322
1323 /* check for sub-tree matches */
1324 memset(&state, 0, sizeof(state));
1325 rc = _db_tree_sub_prune(&(s_iter->chains), ec_iter, c_iter, &state);
1326 if (rc > 0) {
1327 rm_flag = true;
1328 s_iter->node_cnt -= rc;
1329 goto add_reset;
1330 } else if (rc < 0)
1331 goto add_free_ok;
1332
1333 /* syscall exists and has at least one existing chain - start at the
1334 * top and walk the two chains */
1335 do {
1336 /* insert the new rule into the existing tree */
1337 if (db_chain_eq(c_iter, ec_iter)) {
1338 /* found a matching node on this chain level */
1339 if (db_chain_action(c_iter) &&
1340 db_chain_action(ec_iter)) {
1341 /* both are "action" nodes */
1342 if (c_iter->act_t_flg && ec_iter->act_t_flg) {
1343 if (ec_iter->act_t != action)
1344 goto add_free_exist;
1345 } else if (c_iter->act_t_flg) {
1346 ec_iter->act_t_flg = true;
1347 ec_iter->act_t = action;
1348 }
1349 if (c_iter->act_f_flg && ec_iter->act_f_flg) {
1350 if (ec_iter->act_f != action)
1351 goto add_free_exist;
1352 } else if (c_iter->act_f_flg) {
1353 ec_iter->act_f_flg = true;
1354 ec_iter->act_f = action;
1355 }
1356 if (ec_iter->act_t_flg == ec_iter->act_f_flg &&
1357 ec_iter->act_t == ec_iter->act_f) {
1358 n_cnt = _db_tree_remove(
1359 &(s_iter->chains),
1360 ec_iter);
1361 s_iter->node_cnt -= n_cnt;
1362 goto add_free_ok;
1363 }
1364 } else if (db_chain_action(c_iter)) {
1365 /* new is shorter */
1366 if (c_iter->act_t_flg) {
1367 rc = _db_tree_act_check(ec_iter->nxt_t,
1368 action);
1369 if (rc < 0)
1370 goto add_free;
1371 n_cnt = _db_tree_free(ec_iter->nxt_t);
1372 ec_iter->nxt_t = NULL;
1373 ec_iter->act_t_flg = true;
1374 ec_iter->act_t = action;
1375 } else {
1376 rc = _db_tree_act_check(ec_iter->nxt_f,
1377 action);
1378 if (rc < 0)
1379 goto add_free;
1380 n_cnt = _db_tree_free(ec_iter->nxt_f);
1381 ec_iter->nxt_f = NULL;
1382 ec_iter->act_f_flg = true;
1383 ec_iter->act_f = action;
1384 }
1385 s_iter->node_cnt -= n_cnt;
1386 }
1387 if (c_iter->nxt_t != NULL) {
1388 if (ec_iter->nxt_t != NULL) {
1389 /* jump to the next level */
1390 c_prev = c_iter;
1391 c_iter = c_iter->nxt_t;
1392 ec_iter = ec_iter->nxt_t;
1393 s_new->node_cnt--;
1394 } else if (ec_iter->act_t_flg) {
1395 /* existing is shorter */
1396 if (ec_iter->act_t == action)
1397 goto add_free_ok;
1398 goto add_free_exist;
1399 } else {
1400 /* add a new branch */
1401 c_prev = c_iter;
1402 ec_iter->nxt_t = c_iter->nxt_t;
1403 s_iter->node_cnt +=
1404 (s_new->node_cnt - 1);
1405 goto add_free_match;
1406 }
1407 } else if (c_iter->nxt_f != NULL) {
1408 if (ec_iter->nxt_f != NULL) {
1409 /* jump to the next level */
1410 c_prev = c_iter;
1411 c_iter = c_iter->nxt_f;
1412 ec_iter = ec_iter->nxt_f;
1413 s_new->node_cnt--;
1414 } else if (ec_iter->act_f_flg) {
1415 /* existing is shorter */
1416 if (ec_iter->act_f == action)
1417 goto add_free_ok;
1418 goto add_free_exist;
1419 } else {
1420 /* add a new branch */
1421 c_prev = c_iter;
1422 ec_iter->nxt_f = c_iter->nxt_f;
1423 s_iter->node_cnt +=
1424 (s_new->node_cnt - 1);
1425 goto add_free_match;
1426 }
1427 } else
1428 goto add_free_ok;
1429 } else {
1430 /* need to check other nodes on this level */
1431 if (db_chain_lt(c_iter, ec_iter)) {
1432 if (ec_iter->lvl_prv == NULL) {
1433 /* add to the start of the level */
1434 ec_iter->lvl_prv = c_iter;
1435 c_iter->lvl_nxt = ec_iter;
1436 if (ec_iter == s_iter->chains)
1437 s_iter->chains = c_iter;
1438 s_iter->node_cnt += s_new->node_cnt;
1439 goto add_free_match;
1440 } else
1441 ec_iter = ec_iter->lvl_prv;
1442 } else {
1443 if (ec_iter->lvl_nxt == NULL) {
1444 /* add to the end of the level */
1445 ec_iter->lvl_nxt = c_iter;
1446 c_iter->lvl_prv = ec_iter;
1447 s_iter->node_cnt += s_new->node_cnt;
1448 goto add_free_match;
1449 } else if (db_chain_lt(c_iter,
1450 ec_iter->lvl_nxt)) {
1451 /* add new chain in between */
1452 c_iter->lvl_nxt = ec_iter->lvl_nxt;
1453 ec_iter->lvl_nxt->lvl_prv = c_iter;
1454 ec_iter->lvl_nxt = c_iter;
1455 c_iter->lvl_prv = ec_iter;
1456 s_iter->node_cnt += s_new->node_cnt;
1457 goto add_free_match;
1458 } else
1459 ec_iter = ec_iter->lvl_nxt;
1460 }
1461 }
1462 } while ((c_iter != NULL) && (ec_iter != NULL));
1463
1464 /* we should never be here! */
1465 return -EFAULT;
1466
1467add_free_exist:
1468 rc = -EEXIST;
1469 goto add_free;
1470add_free_ok:
1471 rc = 0;
1472add_free:
1473 /* free the new chain and its syscall struct */
1474 _db_tree_free(s_new->chains);
1475 free(s_new);
1476 goto add_priority_update;
1477add_free_match:
1478 /* free the matching portion of new chain */
1479 if (c_prev != NULL) {
1480 c_prev->nxt_t = NULL;
1481 c_prev->nxt_f = NULL;
1482 _db_tree_free(s_new->chains);
1483 }
1484 free(s_new);
1485 rc = 0;
1486add_priority_update:
1487 /* update the priority */
1488 if (s_iter != NULL) {
1489 s_iter->priority &= (~_DB_PRI_MASK_CHAIN);
1490 s_iter->priority |= (_DB_PRI_MASK_CHAIN - s_iter->node_cnt);
1491 }
1492 return rc;
1493}
1494
1495/**
1496 * Set the priority of a given syscall
1497 * @param col the filter collection
1498 * @param syscall the syscall number
1499 * @param priority priority value, higher value == higher priority
1500 *
1501 * This function sets the priority of the given syscall; this value is used
1502 * when generating the seccomp filter code such that higher priority syscalls
1503 * will incur less filter code overhead than the lower priority syscalls in the
1504 * filter. Returns zero on success, negative values on failure.
1505 *
1506 */
1507int db_col_syscall_priority(struct db_filter_col *col,
1508 int syscall, uint8_t priority)
1509{
1510 int rc = 0, rc_tmp;
1511 unsigned int iter;
1512 int sc_tmp;
1513 struct db_filter *filter;
1514
1515 for (iter = 0; iter < col->filter_cnt; iter++) {
1516 filter = col->filters[iter];
1517 sc_tmp = syscall;
1518
1519 rc_tmp = arch_syscall_translate(filter->arch, &sc_tmp);
1520 if (rc_tmp < 0)
1521 goto priority_failure;
1522
1523 /* if this is a pseudo syscall (syscall < 0) then we need to
1524 * rewrite the syscall for some arch specific reason */
1525 if (sc_tmp < 0) {
1526 /* we set this as a strict op - we don't really care
1527 * since priorities are a "best effort" thing - as we
1528 * want to catch the -EDOM error and bail on this
1529 * architecture */
1530 rc_tmp = arch_syscall_rewrite(filter->arch, &sc_tmp);
1531 if (rc_tmp == -EDOM)
1532 continue;
1533 if (rc_tmp < 0)
1534 goto priority_failure;
1535 }
1536
1537 rc_tmp = _db_syscall_priority(filter, sc_tmp, priority);
1538
1539priority_failure:
1540 if (rc == 0 && rc_tmp < 0)
1541 rc = rc_tmp;
1542 }
1543
1544 return rc;
1545}
1546
1547/**
1548 * Add a new rule to the current filter
1549 * @param col the filter collection
1550 * @param strict the strict flag
1551 * @param action the filter action
1552 * @param syscall the syscall number
1553 * @param arg_cnt the number of argument filters in the argument filter chain
1554 * @param arg_array the argument filter chain, (uint, enum scmp_compare, ulong)
1555 *
1556 * This function adds a new argument/comparison/value to the seccomp filter for
1557 * a syscall; multiple arguments can be specified and they will be chained
1558 * together (essentially AND'd together) in the filter. When the strict flag
1559 * is true the function will fail if the exact rule can not be added to the
1560 * filter, if the strict flag is false the function will not fail if the
1561 * function needs to adjust the rule due to architecture specifics. Returns
1562 * zero on success, negative values on failure.
1563 *
1564 */
1565int db_col_rule_add(struct db_filter_col *col,
1566 bool strict, uint32_t action, int syscall,
1567 unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
1568{
1569 int rc = 0, rc_tmp;
1570 unsigned int iter;
1571 unsigned int chain_len;
1572 unsigned int arg_num;
1573 size_t chain_size;
1574 struct db_api_arg *chain = NULL;
1575 struct scmp_arg_cmp arg_data;
1576
1577 /* collect the arguments for the filter rule */
1578 chain_len = ARG_COUNT_MAX;
1579 chain_size = sizeof(*chain) * chain_len;
1580 chain = malloc(chain_size);
1581 if (chain == NULL)
1582 return -ENOMEM;
1583 memset(chain, 0, chain_size);
1584 for (iter = 0; iter < arg_cnt; iter++) {
1585 arg_data = arg_array[iter];
1586 arg_num = arg_data.arg;
1587 if (arg_num < chain_len && chain[arg_num].valid == 0) {
1588 chain[arg_num].valid = 1;
1589 chain[arg_num].arg = arg_num;
1590 chain[arg_num].op = arg_data.op;
1591 /* XXX - we should check datum/mask size against the
1592 * arch definition, e.g. 64 bit datum on x86 */
1593 switch (chain[arg_num].op) {
1594 case SCMP_CMP_NE:
1595 case SCMP_CMP_LT:
1596 case SCMP_CMP_LE:
1597 case SCMP_CMP_EQ:
1598 case SCMP_CMP_GE:
1599 case SCMP_CMP_GT:
1600 chain[arg_num].mask = DATUM_MAX;
1601 chain[arg_num].datum = arg_data.datum_a;
1602 break;
1603 case SCMP_CMP_MASKED_EQ:
1604 chain[arg_num].mask = arg_data.datum_a;
1605 chain[arg_num].datum = arg_data.datum_b;
1606 break;
1607 default:
1608 rc = -EINVAL;
1609 goto add_return;
1610 }
1611 } else {
1612 rc = -EINVAL;
1613 goto add_return;
1614 }
1615 }
1616
1617 for (iter = 0; iter < col->filter_cnt; iter++) {
1618 rc_tmp = arch_filter_rule_add(col, col->filters[iter], strict,
1619 action, syscall,
1620 chain_len, chain);
1621 if (rc == 0 && rc_tmp < 0)
1622 rc = rc_tmp;
1623 }
1624
1625add_return:
1626 if (chain != NULL)
1627 free(chain);
1628 return rc;
1629}
1630
1631/**
1632 * Start a new seccomp filter transaction
1633 * @param col the filter collection
1634 *
1635 * This function starts a new seccomp filter transaction for the given filter
1636 * collection. Returns zero on success, negative values on failure.
1637 *
1638 */
1639int db_col_transaction_start(struct db_filter_col *col)
1640{
1641 unsigned int iter;
1642 size_t args_size;
1643 struct db_filter_snap *snap;
1644 struct db_filter *filter_o, *filter_s;
1645 struct db_api_rule_list *rule_o, *rule_s;
1646
1647 /* allocate the snapshot */
1648 snap = malloc(sizeof(*snap));
1649 if (snap == NULL)
1650 return -ENOMEM;
1651 snap->filters = malloc(sizeof(struct db_filter *) * col->filter_cnt);
1652 if (snap->filters == NULL) {
1653 free(snap);
1654 return -ENOMEM;
1655 }
1656 snap->filter_cnt = col->filter_cnt;
1657 for (iter = 0; iter < snap->filter_cnt; iter++)
1658 snap->filters[iter] = NULL;
1659 snap->next = NULL;
1660
1661 /* create a snapshot of the current filter state */
1662 for (iter = 0; iter < col->filter_cnt; iter++) {
1663 /* allocate a new filter */
1664 filter_o = col->filters[iter];
1665 filter_s = _db_init(filter_o->arch);
1666 if (filter_s == NULL)
1667 goto trans_start_failure;
1668 snap->filters[iter] = filter_s;
1669
1670 /* create a filter snapshot from existing rules */
1671 rule_o = filter_o->rules;
1672 if (rule_o == NULL)
1673 continue;
1674 do {
1675 /* copy the rule */
1676 rule_s = malloc(sizeof(*rule_s));
1677 if (rule_s == NULL)
1678 goto trans_start_failure;
1679 args_size = sizeof(*rule_s->args) * rule_o->args_cnt;
1680 rule_s->args = malloc(args_size);
1681 if (rule_s->args == NULL) {
1682 free(rule_s);
1683 goto trans_start_failure;
1684 }
1685 rule_s->action = rule_o->action;
1686 rule_s->syscall = rule_o->syscall;
1687 rule_s->args_cnt = rule_o->args_cnt;
1688 memcpy(rule_s->args, rule_o->args, args_size);
1689 if (filter_s->rules != NULL) {
1690 rule_s->prev = filter_s->rules->prev;
1691 rule_s->next = filter_s->rules;
1692 filter_s->rules->prev->next = rule_s;
1693 filter_s->rules->prev = rule_s;
1694 } else {
1695 rule_s->prev = rule_s;
1696 rule_s->next = rule_s;
1697 filter_s->rules = rule_s;
1698 }
1699
1700 /* insert the rule into the filter */
1701 if (db_rule_add(filter_s, rule_o) != 0)
1702 goto trans_start_failure;
1703
1704 /* next rule */
1705 rule_o = rule_o->next;
1706 } while (rule_o != filter_o->rules);
1707 }
1708
1709 /* add the snapshot to the list */
1710 snap->next = col->snapshots;
1711 col->snapshots = snap;
1712
1713 return 0;
1714
1715trans_start_failure:
1716 _db_snap_release(snap);
1717 return -ENOMEM;
1718}
1719
1720/**
1721 * Abort the top most seccomp filter transaction
1722 * @param col the filter collection
1723 *
1724 * This function aborts the most recent seccomp filter transaction.
1725 *
1726 */
1727void db_col_transaction_abort(struct db_filter_col *col)
1728{
1729 int iter;
1730 unsigned int filter_cnt;
1731 struct db_filter **filters;
1732 struct db_filter_snap *snap;
1733
1734 if (col->snapshots == NULL)
1735 return;
1736
1737 /* replace the current filter with the last snapshot */
1738 snap = col->snapshots;
1739 col->snapshots = snap->next;
1740 filter_cnt = col->filter_cnt;
1741 filters = col->filters;
1742 col->filter_cnt = snap->filter_cnt;
1743 col->filters = snap->filters;
1744 free(snap);
1745
1746 /* free the filter we swapped out */
1747 for (iter = 0; iter < filter_cnt; iter++)
1748 _db_release(filters[iter]);
1749 free(filters);
1750}
1751
1752/**
1753 * Commit the top most seccomp filter transaction
1754 * @param col the filter collection
1755 *
1756 * This function commits the most recent seccomp filter transaction.
1757 *
1758 */
1759void db_col_transaction_commit(struct db_filter_col *col)
1760{
1761 struct db_filter_snap *snap;
1762
1763 snap = col->snapshots;
1764 col->snapshots = snap->next;
1765 _db_snap_release(snap);
1766}
This page took 0.097779 seconds and 4 git commands to generate.