2 * Enhanced Seccomp Filter DB
4 * Copyright (c) 2012,2016 Red Hat <pmoore@redhat.com>
5 * Author: Paul Moore <paul@paul-moore.com>
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.
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
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>.
36 #define _DB_STA_VALID 0xA1B2C3D4
37 #define _DB_STA_FREED 0x1A2B3C4D
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)
46 /* private structure for tracking the state of the sub-tree "pruning" */
47 struct db_prune_state
{
53 static unsigned int _db_tree_free(struct db_arg_chain_tree
*tree
);
56 * Do not call this function directly, use _db_tree_free() instead
58 static unsigned int __db_tree_free(struct db_arg_chain_tree
*tree
)
62 if (tree
== NULL
|| --(tree
->refcnt
) > 0)
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
);
75 * Free a syscall filter argument chain tree
76 * @param tree the argument chain list
78 * This function frees a tree and returns the number of nodes freed.
81 static unsigned int _db_tree_free(struct db_arg_chain_tree
*tree
)
83 struct db_arg_chain_tree
*iter
;
89 while (iter
->lvl_prv
!= NULL
)
92 return __db_tree_free(iter
);
96 * Remove a node from an argument chain tree
97 * @param tree the pointer to the tree
98 * @param node the node to remove
100 * This function searches the tree looking for the node and removes it once
101 * found. Returns the number of nodes freed.
104 static unsigned int _db_tree_remove(struct db_arg_chain_tree
**tree
,
105 struct db_arg_chain_tree
*node
)
108 struct db_arg_chain_tree
*c_iter
;
110 if (tree
== NULL
|| *tree
== NULL
|| node
== NULL
)
114 while (c_iter
->lvl_prv
!= NULL
)
115 c_iter
= c_iter
->lvl_prv
;
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
;
124 *tree
= c_iter
->lvl_nxt
;
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
;
131 /* free and return */
132 c_iter
->lvl_prv
= NULL
;
133 c_iter
->lvl_nxt
= NULL
;
134 cnt
+= _db_tree_free(c_iter
);
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
);
142 c_iter
= c_iter
->lvl_nxt
;
143 } while (c_iter
!= NULL
);
149 * Traverse a tree checking the action values
150 * @param tree the pointer to the tree
151 * @param action the action
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
158 static int _db_tree_act_check(struct db_arg_chain_tree
*tree
, uint32_t action
)
161 struct db_arg_chain_tree
*c_iter
;
167 while (c_iter
->lvl_prv
!= NULL
)
168 c_iter
= c_iter
->lvl_prv
;
171 if (c_iter
->act_t_flg
&& c_iter
->act_t
!= action
)
173 if (c_iter
->act_f_flg
&& c_iter
->act_f
!= action
)
176 rc
= _db_tree_act_check(c_iter
->nxt_t
, action
);
179 rc
= _db_tree_act_check(c_iter
->nxt_f
, action
);
183 c_iter
= c_iter
->lvl_nxt
;
184 } while (c_iter
!= NULL
);
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
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.
202 static 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
)
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
;
214 if (!state
|| !existing
|| !new)
220 if (db_chain_eq(ec_iter
, c_iter
)) {
223 if (db_chain_leaf(c_iter
)) {
225 if (db_chain_eq_result(ec_iter
, c_iter
)) {
226 /* identical results */
228 return _db_tree_remove(prev
,
233 if (c_iter
->act_t_flg
&& ec_iter
->nxt_t
) {
234 /* new is shorter (true) */
237 rc
+= _db_tree_remove(&(ec_iter
->nxt_t
),
239 ec_iter
->act_t
= c_iter
->act_t
;
240 ec_iter
->act_t_flg
= true;
242 if (c_iter
->act_f_flg
&& ec_iter
->nxt_f
) {
243 /* new is shorter (false) */
246 rc
+= _db_tree_remove(&(ec_iter
->nxt_f
),
248 ec_iter
->act_f
= c_iter
->act_f
;
249 ec_iter
->act_f_flg
= true;
255 if (c_iter
->nxt_t
&& ec_iter
->act_t_flg
)
256 /* existing is shorter (true) */
258 if (c_iter
->nxt_f
&& ec_iter
->act_f_flg
)
259 /* existing is shorter (false) */
264 state_new
.matched
= true;
265 rc_tmp
= _db_tree_sub_prune((prev
?
270 rc
+= (rc_tmp
> 0 ? rc_tmp
: 0);
271 if (state
->prefix_new
&& rc_tmp
< 0)
272 return (rc
> 0 ? rc
: rc_tmp
);
276 state_new
.matched
= true;
277 rc_tmp
= _db_tree_sub_prune((prev
?
282 rc
+= (rc_tmp
> 0 ? rc_tmp
: 0);
283 if (state
->prefix_new
&& rc_tmp
< 0)
284 return (rc
> 0 ? rc
: rc_tmp
);
286 } else if (db_chain_lt(ec_iter
, c_iter
)) {
288 if (state
->matched
|| state
->prefix_new
)
291 state_new
.prefix_exist
= true;
293 if (ec_iter
->nxt_t
) {
294 rc_tmp
= _db_tree_sub_prune((prev
?
299 rc
+= (rc_tmp
> 0 ? rc_tmp
: 0);
301 if (ec_iter
->nxt_f
) {
302 rc_tmp
= _db_tree_sub_prune((prev
?
307 rc
+= (rc_tmp
> 0 ? rc_tmp
: 0);
309 } else if (db_chain_gt(ec_iter
, c_iter
)) {
311 if (state
->matched
|| state
->prefix_exist
)
314 state_new
.prefix_new
= true;
317 rc_tmp
= _db_tree_sub_prune(NULL
,
321 rc
+= (rc_tmp
> 0 ? rc_tmp
: 0);
323 return (rc
> 0 ? rc
: rc_tmp
);
326 rc_tmp
= _db_tree_sub_prune(NULL
,
330 rc
+= (rc_tmp
> 0 ? rc_tmp
: 0);
332 return (rc
> 0 ? rc
: rc_tmp
);
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
;
343 ec_iter
= ec_iter
->lvl_nxt
;
350 * Free and reset the seccomp filter DB
351 * @param db the seccomp filter DB
353 * This function frees any existing filters and resets the filter DB to a
354 * default state; only the DB architecture is preserved.
357 static void _db_reset(struct db_filter
*db
)
359 struct db_sys_list
*s_iter
;
360 struct db_api_rule_list
*r_iter
;
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
);
372 s_iter
= db
->syscalls
;
378 if (db
->rules
!= NULL
) {
379 /* split the loop first then loop and free */
380 db
->rules
->prev
->next
= NULL
;
382 while (r_iter
!= NULL
) {
383 db
->rules
= r_iter
->next
;
393 * Intitalize a seccomp filter DB
394 * @param arch the architecture definition
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.
400 static struct db_filter
*_db_init(const struct arch_def
*arch
)
402 struct db_filter
*db
;
404 db
= malloc(sizeof(*db
));
408 /* clear the buffer for the first time and set the arch */
409 memset(db
, 0, sizeof(*db
));
412 /* reset the DB to a known state */
419 * Destroy a seccomp filter DB
420 * @param db the seccomp filter DB
422 * This function destroys a seccomp filter DB. After calling this function,
423 * the filter should no longer be referenced.
426 static void _db_release(struct db_filter
*db
)
431 /* free and reset the DB */
437 * Destroy a seccomp filter snapshot
438 * @param snap the seccomp filter snapshot
440 * This function destroys a seccomp filter snapshot. After calling this
441 * function, the snapshot should no longer be referenced.
444 static void _db_snap_release(struct db_filter_snap
*snap
)
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
]);
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
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.
471 static int _db_syscall_priority(struct db_filter
*db
,
472 int syscall
, uint8_t priority
)
474 unsigned int sys_pri
= _DB_PRI_USER(priority
);
475 struct db_sys_list
*s_new
, *s_iter
, *s_prev
= NULL
;
479 s_iter
= db
->syscalls
;
480 while (s_iter
!= NULL
&& s_iter
->num
< syscall
) {
482 s_iter
= s_iter
->next
;
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
;
494 /* no existing syscall entry - create a phantom entry */
495 s_new
= malloc(sizeof(*s_new
));
498 memset(s_new
, 0, sizeof(*s_new
));
499 s_new
->num
= syscall
;
500 s_new
->priority
= sys_pri
;
501 s_new
->valid
= false;
503 /* add it before s_iter */
504 if (s_prev
!= NULL
) {
505 s_new
->next
= s_prev
->next
;
506 s_prev
->next
= s_new
;
508 s_new
->next
= db
->syscalls
;
509 db
->syscalls
= s_new
;
516 * Free and reset the seccomp filter collection
517 * @param col the seccomp filter collection
518 * @param def_action the default filter action
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
526 int db_col_reset(struct db_filter_col
*col
, uint32_t def_action
)
529 struct db_filter
*db
;
530 struct db_filter_snap
*snap
;
535 /* free any filters */
536 for (iter
= 0; iter
< col
->filter_cnt
; iter
++)
537 _db_release(col
->filters
[iter
]);
543 /* set the endianess to undefined */
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;
553 col
->state
= _DB_STA_VALID
;
555 /* reset the initial db */
556 db
= _db_init(arch_def_native
);
559 if (db_col_db_add(col
, db
) < 0) {
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
]);
578 * Intitalize a seccomp filter collection
579 * @param def_action the default filter action
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.
585 struct db_filter_col
*db_col_init(uint32_t def_action
)
587 struct db_filter_col
*col
;
589 col
= malloc(sizeof(*col
));
593 /* clear the buffer for the first time */
594 memset(col
, 0, sizeof(*col
));
596 /* reset the DB to a known state */
597 if (db_col_reset(col
, def_action
) < 0)
608 * Destroy a seccomp filter collection
609 * @param col the seccomp filter collection
611 * This function destroys a seccomp filter collection. After calling this
612 * function, the filter should no longer be referenced.
615 void db_col_release(struct db_filter_col
*col
)
622 /* set the state, just in case */
623 col
->state
= _DB_STA_FREED
;
625 /* free any filters */
626 for (iter
= 0; iter
< col
->filter_cnt
; iter
++)
627 _db_release(col
->filters
[iter
]);
633 /* free the collection */
638 * Validate the seccomp action
639 * @param action the seccomp action
641 * Verify that the given action is a valid seccomp action; return zero if
642 * valid, -EINVAL if invalid.
644 int db_action_valid(uint32_t action
)
646 if (action
== SCMP_ACT_KILL
)
648 else if (action
== SCMP_ACT_TRAP
)
650 else if ((action
== SCMP_ACT_ERRNO(action
& 0x0000ffff)) &&
651 ((action
& 0x0000ffff) < MAX_ERRNO
))
653 else if (action
== SCMP_ACT_TRACE(action
& 0x0000ffff))
655 else if (action
== SCMP_ACT_ALLOW
)
662 * Validate a filter collection
663 * @param col the seccomp filter collection
665 * This function validates a seccomp filter collection. Returns zero if the
666 * collection is valid, negative values on failure.
669 int db_col_valid(struct db_filter_col
*col
)
671 if (col
!= NULL
&& col
->state
== _DB_STA_VALID
&& col
->filter_cnt
> 0)
677 * Merge two filter collections
678 * @param col_dst the destination filter collection
679 * @param col_src the source filter collection
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.
686 int db_col_merge(struct db_filter_col
*col_dst
, struct db_filter_col
*col_src
)
688 unsigned int iter_a
, iter_b
;
689 struct db_filter
**dbs
;
691 /* verify that the endianess is a match */
692 if (col_dst
->endian
!= col_src
->endian
)
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
)
704 /* expand the destination */
705 dbs
= realloc(col_dst
->filters
,
706 sizeof(struct db_filter
*) *
707 (col_dst
->filter_cnt
+ col_src
->filter_cnt
));
710 col_dst
->filters
= dbs
;
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
++;
719 /* free the source */
720 col_src
->filter_cnt
= 0;
721 db_col_release(col_src
);
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
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.
736 int db_col_arch_exist(struct db_filter_col
*col
, uint32_t arch_token
)
740 for (iter
= 0; iter
< col
->filter_cnt
; iter
++)
741 if (col
->filters
[iter
]->arch
->token
== arch_token
)
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
753 * Get the requested filter attribute and provide it via @value. Returns zero
754 * on success, negative values on failure.
757 int db_col_attr_get(const struct db_filter_col
*col
,
758 enum scmp_filter_attr attr
, uint32_t *value
)
763 case SCMP_FLTATR_ACT_DEFAULT
:
764 *value
= col
->attr
.act_default
;
766 case SCMP_FLTATR_ACT_BADARCH
:
767 *value
= col
->attr
.act_badarch
;
769 case SCMP_FLTATR_CTL_NNP
:
770 *value
= col
->attr
.nnp_enable
;
772 case SCMP_FLTATR_CTL_TSYNC
:
773 *value
= col
->attr
.tsync_enable
;
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
789 * Set the requested filter attribute with the given value. Returns zero on
790 * success, negative values on failure.
793 int db_col_attr_set(struct db_filter_col
*col
,
794 enum scmp_filter_attr attr
, uint32_t value
)
799 case SCMP_FLTATR_ACT_DEFAULT
:
803 case SCMP_FLTATR_ACT_BADARCH
:
804 if (db_action_valid(value
) == 0)
805 col
->attr
.act_badarch
= value
;
809 case SCMP_FLTATR_CTL_NNP
:
810 col
->attr
.nnp_enable
= (value
? 1 : 0);
812 case SCMP_FLTATR_CTL_TSYNC
:
813 rc
= sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC
);
817 col
->attr
.tsync_enable
= (value
? 1 : 0);
831 * Add a new architecture filter to a filter collection
832 * @param col the seccomp filter collection
833 * @param arch the architecture
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.
840 int db_col_db_new(struct db_filter_col
*col
, const struct arch_def
*arch
)
843 struct db_filter
*db
;
848 rc
= db_col_db_add(col
, db
);
856 * Add a new filter DB to a filter collection
857 * @param col the seccomp filter collection
858 * @param db the seccomp filter DB
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.
865 int db_col_db_add(struct db_filter_col
*col
, struct db_filter
*db
)
867 struct db_filter
**dbs
;
869 if (col
->endian
!= 0 && col
->endian
!= db
->arch
->endian
)
872 if (db_col_arch_exist(col
, db
->arch
->token
))
875 dbs
= realloc(col
->filters
,
876 sizeof(struct db_filter
*) * (col
->filter_cnt
+ 1));
881 col
->filters
[col
->filter_cnt
- 1] = db
;
882 if (col
->endian
== 0)
883 col
->endian
= db
->arch
->endian
;
889 * Remove a filter DB from a filter collection
890 * @param col the seccomp filter collection
891 * @param arch_token the architecture token
893 * This function removes an existing seccomp filter DB from an existing seccomp
894 * filter collection. Returns zero on success, negative values on failure.
897 int db_col_db_remove(struct db_filter_col
*col
, uint32_t arch_token
)
901 struct db_filter
**dbs
;
903 if ((col
->filter_cnt
<= 0) || (db_col_arch_exist(col
, arch_token
) == 0))
906 for (found
= 0, iter
= 0; iter
< col
->filter_cnt
; iter
++) {
908 col
->filters
[iter
- 1] = col
->filters
[iter
];
909 else if (col
->filters
[iter
]->arch
->token
== arch_token
) {
910 _db_release(col
->filters
[iter
]);
914 col
->filters
[--col
->filter_cnt
] = NULL
;
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
);
924 /* this was the last filter so free all the associated memory
925 * and reset the endian token */
935 * Test if the argument filter can be skipped because it's a tautology
936 * @param arg argument filter
938 * If this argument filter applied to the lower 32 bit can be skipped this
939 * function returns false.
942 static bool _db_arg_cmp_need_lo(const struct db_api_arg
*arg
)
944 if (arg
->op
== SCMP_CMP_MASKED_EQ
&& D64_LO(arg
->mask
) == 0)
951 * Test if the argument filter can be skipped because it's a tautology
952 * @param arg argument filter
954 * If this argument filter applied to the upper 32 bit can be skipped this
955 * function returns false.
958 static bool _db_arg_cmp_need_hi(const struct db_api_arg
*arg
)
960 if (arg
->op
== SCMP_CMP_MASKED_EQ
&& D64_HI(arg
->mask
) == 0)
967 * Fixup the node based on the op/mask
968 * @param node the chain node
970 * Ensure the datum is masked as well.
973 static void _db_node_mask_fixup(struct db_arg_chain_tree
*node
)
975 node
->datum
&= node
->mask
;
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
985 * This function generates a new syscall filter for a 64 bit system. Returns
986 * zero on success, negative values on failure.
989 static struct db_sys_list
*_db_rule_gen_64(const struct arch_def
*arch
,
991 unsigned int syscall
,
992 struct db_api_arg
*chain
)
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
;
1001 s_new
= malloc(sizeof(*s_new
));
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)
1015 /* TODO: handle the case were either hi or lo isn't needed */
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
]))
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
) {
1030 goto gen_64_failure
;
1032 memset(c_iter_lo
, 0, sizeof(*c_iter_lo
));
1033 c_iter_lo
->refcnt
= 1;
1035 /* link this level to the previous level */
1036 if (c_prev_lo
!= NULL
) {
1038 c_prev_lo
->nxt_f
= c_iter_hi
;
1039 c_prev_hi
->nxt_f
= c_iter_hi
;
1040 c_iter_hi
->refcnt
++;
1042 c_prev_lo
->nxt_t
= c_iter_hi
;
1044 s_new
->chains
= c_iter_hi
;
1045 s_new
->node_cnt
+= 2;
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
,
1052 c_iter_lo
->arg_offset
= arch_arg_offset_lo(arch
,
1054 switch (chain
[iter
].op
) {
1056 c_iter_hi
->op
= SCMP_CMP_GE
;
1057 c_iter_lo
->op
= SCMP_CMP_GT
;
1061 c_iter_hi
->op
= SCMP_CMP_EQ
;
1062 c_iter_lo
->op
= SCMP_CMP_EQ
;
1066 c_iter_hi
->op
= SCMP_CMP_GE
;
1067 c_iter_lo
->op
= SCMP_CMP_GE
;
1071 c_iter_hi
->op
= SCMP_CMP_GE
;
1072 c_iter_lo
->op
= SCMP_CMP_GT
;
1076 c_iter_hi
->op
= chain
[iter
].op
;
1077 c_iter_lo
->op
= chain
[iter
].op
;
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
);
1085 /* fixup the mask/datum */
1086 _db_node_mask_fixup(c_iter_hi
);
1087 _db_node_mask_fixup(c_iter_lo
);
1089 /* link the hi and lo chain nodes */
1090 c_iter_hi
->nxt_t
= c_iter_lo
;
1092 c_prev_hi
= c_iter_hi
;
1093 c_prev_lo
= c_iter_lo
;
1095 if (c_iter_lo
!= NULL
) {
1096 /* set the leaf node */
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
;
1103 c_iter_lo
->act_t_flg
= true;
1104 c_iter_lo
->act_t
= action
;
1107 s_new
->action
= action
;
1112 /* free the new chain and its syscall struct */
1113 _db_tree_free(s_new
->chains
);
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
1125 * This function generates a new syscall filter for a 32 bit system. Returns
1126 * zero on success, negative values on failure.
1129 static struct db_sys_list
*_db_rule_gen_32(const struct arch_def
*arch
,
1131 unsigned int syscall
,
1132 struct db_api_arg
*chain
)
1136 struct db_sys_list
*s_new
;
1137 struct db_arg_chain_tree
*c_iter
= NULL
, *c_prev
= NULL
;
1140 s_new
= malloc(sizeof(*s_new
));
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)
1154 /* skip generating instructions which are no-ops */
1155 if (!_db_arg_cmp_need_lo(&chain
[iter
]))
1158 c_iter
= malloc(sizeof(*c_iter
));
1160 goto gen_32_failure
;
1161 memset(c_iter
, 0, sizeof(*c_iter
));
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
;
1170 /* link in the new node and update the chain */
1171 if (c_prev
!= NULL
) {
1173 c_prev
->nxt_t
= c_iter
;
1175 c_prev
->nxt_f
= c_iter
;
1177 s_new
->chains
= c_iter
;
1180 /* rewrite the op to reduce the op/datum combos */
1181 switch (c_iter
->op
) {
1183 c_iter
->op
= SCMP_CMP_EQ
;
1187 c_iter
->op
= SCMP_CMP_GE
;
1191 c_iter
->op
= SCMP_CMP_GT
;
1198 /* fixup the mask/datum */
1199 _db_node_mask_fixup(c_iter
);
1203 if (c_iter
!= NULL
) {
1204 /* set the leaf node */
1206 c_iter
->act_t_flg
= true;
1207 c_iter
->act_t
= action
;
1209 c_iter
->act_f_flg
= true;
1210 c_iter
->act_f
= action
;
1213 s_new
->action
= action
;
1218 /* free the new chain and its syscall struct */
1219 _db_tree_free(s_new
->chains
);
1225 * Add a new rule to the seccomp filter DB
1226 * @param db the seccomp filter db
1227 * @param rule the filter rule
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.
1236 int db_rule_add(struct db_filter
*db
, const struct db_api_rule_list
*rule
)
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;
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
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
);
1263 new_chain_cnt
= s_new
->node_cnt
;
1265 /* no more failures allowed after this point that would result in the
1266 * stored filter being in an inconsistent state */
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
) {
1272 s_iter
= s_iter
->next
;
1275 s_new
->node_cnt
= new_chain_cnt
;
1276 s_new
->priority
= _DB_PRI_MASK_CHAIN
- s_new
->node_cnt
;
1278 c_iter
= s_new
->chains
;
1280 ec_iter
= s_iter
->chains
;
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
;
1289 s_new
->next
= db
->syscalls
;
1290 db
->syscalls
= s_new
;
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
;
1303 s_iter
->priority
= s_new
->priority
;
1304 s_iter
->valid
= true;
1307 goto add_priority_update
;
1309 /* syscall exists without any chains - existing filter
1310 * is at least as large as the new entry so cleanup and
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
;
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
);
1328 s_iter
->node_cnt
-= rc
;
1333 /* syscall exists and has at least one existing chain - start at the
1334 * top and walk the two chains */
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
;
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
;
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(
1361 s_iter
->node_cnt
-= n_cnt
;
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
,
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
;
1376 rc
= _db_tree_act_check(ec_iter
->nxt_f
,
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
;
1385 s_iter
->node_cnt
-= n_cnt
;
1387 if (c_iter
->nxt_t
!= NULL
) {
1388 if (ec_iter
->nxt_t
!= NULL
) {
1389 /* jump to the next level */
1391 c_iter
= c_iter
->nxt_t
;
1392 ec_iter
= ec_iter
->nxt_t
;
1394 } else if (ec_iter
->act_t_flg
) {
1395 /* existing is shorter */
1396 if (ec_iter
->act_t
== action
)
1398 goto add_free_exist
;
1400 /* add a new branch */
1402 ec_iter
->nxt_t
= c_iter
->nxt_t
;
1404 (s_new
->node_cnt
- 1);
1405 goto add_free_match
;
1407 } else if (c_iter
->nxt_f
!= NULL
) {
1408 if (ec_iter
->nxt_f
!= NULL
) {
1409 /* jump to the next level */
1411 c_iter
= c_iter
->nxt_f
;
1412 ec_iter
= ec_iter
->nxt_f
;
1414 } else if (ec_iter
->act_f_flg
) {
1415 /* existing is shorter */
1416 if (ec_iter
->act_f
== action
)
1418 goto add_free_exist
;
1420 /* add a new branch */
1422 ec_iter
->nxt_f
= c_iter
->nxt_f
;
1424 (s_new
->node_cnt
- 1);
1425 goto add_free_match
;
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
;
1441 ec_iter
= ec_iter
->lvl_prv
;
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
;
1459 ec_iter
= ec_iter
->lvl_nxt
;
1462 } while ((c_iter
!= NULL
) && (ec_iter
!= NULL
));
1464 /* we should never be here! */
1473 /* free the new chain and its syscall struct */
1474 _db_tree_free(s_new
->chains
);
1476 goto add_priority_update
;
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
);
1486 add_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
);
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
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.
1507 int db_col_syscall_priority(struct db_filter_col
*col
,
1508 int syscall
, uint8_t priority
)
1513 struct db_filter
*filter
;
1515 for (iter
= 0; iter
< col
->filter_cnt
; iter
++) {
1516 filter
= col
->filters
[iter
];
1519 rc_tmp
= arch_syscall_translate(filter
->arch
, &sc_tmp
);
1521 goto priority_failure
;
1523 /* if this is a pseudo syscall (syscall < 0) then we need to
1524 * rewrite the syscall for some arch specific reason */
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
1530 rc_tmp
= arch_syscall_rewrite(filter
->arch
, &sc_tmp
);
1531 if (rc_tmp
== -EDOM
)
1534 goto priority_failure
;
1537 rc_tmp
= _db_syscall_priority(filter
, sc_tmp
, priority
);
1540 if (rc
== 0 && rc_tmp
< 0)
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)
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.
1565 int 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
)
1571 unsigned int chain_len
;
1572 unsigned int arg_num
;
1574 struct db_api_arg
*chain
= NULL
;
1575 struct scmp_arg_cmp arg_data
;
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
);
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
) {
1600 chain
[arg_num
].mask
= DATUM_MAX
;
1601 chain
[arg_num
].datum
= arg_data
.datum_a
;
1603 case SCMP_CMP_MASKED_EQ
:
1604 chain
[arg_num
].mask
= arg_data
.datum_a
;
1605 chain
[arg_num
].datum
= arg_data
.datum_b
;
1617 for (iter
= 0; iter
< col
->filter_cnt
; iter
++) {
1618 rc_tmp
= arch_filter_rule_add(col
, col
->filters
[iter
], strict
,
1621 if (rc
== 0 && rc_tmp
< 0)
1632 * Start a new seccomp filter transaction
1633 * @param col the filter collection
1635 * This function starts a new seccomp filter transaction for the given filter
1636 * collection. Returns zero on success, negative values on failure.
1639 int db_col_transaction_start(struct db_filter_col
*col
)
1643 struct db_filter_snap
*snap
;
1644 struct db_filter
*filter_o
, *filter_s
;
1645 struct db_api_rule_list
*rule_o
, *rule_s
;
1647 /* allocate the snapshot */
1648 snap
= malloc(sizeof(*snap
));
1651 snap
->filters
= malloc(sizeof(struct db_filter
*) * col
->filter_cnt
);
1652 if (snap
->filters
== NULL
) {
1656 snap
->filter_cnt
= col
->filter_cnt
;
1657 for (iter
= 0; iter
< snap
->filter_cnt
; iter
++)
1658 snap
->filters
[iter
] = NULL
;
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
;
1670 /* create a filter snapshot from existing rules */
1671 rule_o
= filter_o
->rules
;
1676 rule_s
= malloc(sizeof(*rule_s
));
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
) {
1683 goto trans_start_failure
;
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
;
1695 rule_s
->prev
= rule_s
;
1696 rule_s
->next
= rule_s
;
1697 filter_s
->rules
= rule_s
;
1700 /* insert the rule into the filter */
1701 if (db_rule_add(filter_s
, rule_o
) != 0)
1702 goto trans_start_failure
;
1705 rule_o
= rule_o
->next
;
1706 } while (rule_o
!= filter_o
->rules
);
1709 /* add the snapshot to the list */
1710 snap
->next
= col
->snapshots
;
1711 col
->snapshots
= snap
;
1715 trans_start_failure
:
1716 _db_snap_release(snap
);
1721 * Abort the top most seccomp filter transaction
1722 * @param col the filter collection
1724 * This function aborts the most recent seccomp filter transaction.
1727 void db_col_transaction_abort(struct db_filter_col
*col
)
1730 unsigned int filter_cnt
;
1731 struct db_filter
**filters
;
1732 struct db_filter_snap
*snap
;
1734 if (col
->snapshots
== NULL
)
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
;
1746 /* free the filter we swapped out */
1747 for (iter
= 0; iter
< filter_cnt
; iter
++)
1748 _db_release(filters
[iter
]);
1753 * Commit the top most seccomp filter transaction
1754 * @param col the filter collection
1756 * This function commits the most recent seccomp filter transaction.
1759 void db_col_transaction_commit(struct db_filter_col
*col
)
1761 struct db_filter_snap
*snap
;
1763 snap
= col
->snapshots
;
1764 col
->snapshots
= snap
->next
;
1765 _db_snap_release(snap
);