X-Git-Url: http://git.ieval.ro/?p=linux-seccomp.git;a=blobdiff_plain;f=libseccomp%2Fsrc%2Fdb.c;fp=libseccomp%2Fsrc%2Fdb.c;h=0000000000000000000000000000000000000000;hp=1a85b1f82aeb5498901bb8aeb6fbc23fa8a2f977;hb=a7f80a138de41255578bc28d034438a295ecbb2e;hpb=a8d04cb21e9ce2cfa37d893162df3e4943d9e480 diff --git a/libseccomp/src/db.c b/libseccomp/src/db.c deleted file mode 100644 index 1a85b1f..0000000 --- a/libseccomp/src/db.c +++ /dev/null @@ -1,1766 +0,0 @@ -/** - * Enhanced Seccomp Filter DB - * - * Copyright (c) 2012,2016 Red Hat - * Author: Paul Moore - */ - -/* - * This library is free software; you can redistribute it and/or modify it - * under the terms of version 2.1 of the GNU Lesser General Public License as - * published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "arch.h" -#include "db.h" -#include "system.h" - -/* state values */ -#define _DB_STA_VALID 0xA1B2C3D4 -#define _DB_STA_FREED 0x1A2B3C4D - -/* the priority field is fairly simple - without any user hints, or in the case - * of a hint "tie", we give higher priority to syscalls with less chain nodes - * (filter is easier to evaluate) */ -#define _DB_PRI_MASK_CHAIN 0x0000FFFF -#define _DB_PRI_MASK_USER 0x00FF0000 -#define _DB_PRI_USER(x) (((x) << 16) & _DB_PRI_MASK_USER) - -/* private structure for tracking the state of the sub-tree "pruning" */ -struct db_prune_state { - bool prefix_exist; - bool prefix_new; - bool matched; -}; - -static unsigned int _db_tree_free(struct db_arg_chain_tree *tree); - -/** - * Do not call this function directly, use _db_tree_free() instead - */ -static unsigned int __db_tree_free(struct db_arg_chain_tree *tree) -{ - int cnt; - - if (tree == NULL || --(tree->refcnt) > 0) - return 0; - - /* we assume the caller has ensured that 'tree->lvl_prv == NULL' */ - cnt = __db_tree_free(tree->lvl_nxt); - cnt += _db_tree_free(tree->nxt_t); - cnt += _db_tree_free(tree->nxt_f); - - free(tree); - return cnt + 1; -} - -/** - * Free a syscall filter argument chain tree - * @param tree the argument chain list - * - * This function frees a tree and returns the number of nodes freed. - * - */ -static unsigned int _db_tree_free(struct db_arg_chain_tree *tree) -{ - struct db_arg_chain_tree *iter; - - if (tree == NULL) - return 0; - - iter = tree; - while (iter->lvl_prv != NULL) - iter = iter->lvl_prv; - - return __db_tree_free(iter); -} - -/** - * Remove a node from an argument chain tree - * @param tree the pointer to the tree - * @param node the node to remove - * - * This function searches the tree looking for the node and removes it once - * found. Returns the number of nodes freed. - * - */ -static unsigned int _db_tree_remove(struct db_arg_chain_tree **tree, - struct db_arg_chain_tree *node) -{ - int cnt = 0; - struct db_arg_chain_tree *c_iter; - - if (tree == NULL || *tree == NULL || node == NULL) - return 0; - - c_iter = *tree; - while (c_iter->lvl_prv != NULL) - c_iter = c_iter->lvl_prv; - - do { - if (c_iter == node || db_chain_zombie(c_iter)) { - /* remove from the tree */ - if (c_iter == *tree) { - if (c_iter->lvl_prv != NULL) - *tree = c_iter->lvl_prv; - else - *tree = c_iter->lvl_nxt; - } - if (c_iter->lvl_prv != NULL) - c_iter->lvl_prv->lvl_nxt = c_iter->lvl_nxt; - if (c_iter->lvl_nxt != NULL) - c_iter->lvl_nxt->lvl_prv = c_iter->lvl_prv; - - /* free and return */ - c_iter->lvl_prv = NULL; - c_iter->lvl_nxt = NULL; - cnt += _db_tree_free(c_iter); - return cnt; - } - - /* check the true/false sub-trees */ - cnt += _db_tree_remove(&(c_iter->nxt_t), node); - cnt += _db_tree_remove(&(c_iter->nxt_f), node); - - c_iter = c_iter->lvl_nxt; - } while (c_iter != NULL); - - return cnt; -} - -/** - * Traverse a tree checking the action values - * @param tree the pointer to the tree - * @param action the action - * - * Traverse the tree inspecting each action to see if it matches the given - * action. Returns zero if all actions match the given action, negative values - * on failure. - * - */ -static int _db_tree_act_check(struct db_arg_chain_tree *tree, uint32_t action) -{ - int rc; - struct db_arg_chain_tree *c_iter; - - if (tree == NULL) - return 0; - - c_iter = tree; - while (c_iter->lvl_prv != NULL) - c_iter = c_iter->lvl_prv; - - do { - if (c_iter->act_t_flg && c_iter->act_t != action) - return -EEXIST; - if (c_iter->act_f_flg && c_iter->act_f != action) - return -EEXIST; - - rc = _db_tree_act_check(c_iter->nxt_t, action); - if (rc < 0) - return rc; - rc = _db_tree_act_check(c_iter->nxt_f, action); - if (rc < 0) - return rc; - - c_iter = c_iter->lvl_nxt; - } while (c_iter != NULL); - - return 0; -} - -/** - * Checks for a sub-tree match in an existing tree and prunes the tree - * @param prev the head of the existing tree or sub-tree - * @param existing the starting point into the existing tree - * @param new pointer to the new tree - * @param state pointer to the pruning state - * - * This function searches the existing and new trees trying to prune each to - * eliminate redundancy. Returns the number of nodes removed from the tree on - * success, zero if no changes were made, and negative values if the new tree - * should be discarded. - * - */ -static int _db_tree_sub_prune(struct db_arg_chain_tree **prev, - struct db_arg_chain_tree *existing, - struct db_arg_chain_tree *new, - struct db_prune_state *state) -{ - int rc = 0; - int rc_tmp; - struct db_arg_chain_tree *ec_iter; - struct db_arg_chain_tree *ec_iter_tmp; - struct db_arg_chain_tree *c_iter; - struct db_prune_state state_new; - - if (!state || !existing || !new) - return 0; - - ec_iter = existing; - c_iter = new; - do { - if (db_chain_eq(ec_iter, c_iter)) { - /* equal */ - - if (db_chain_leaf(c_iter)) { - /* leaf */ - if (db_chain_eq_result(ec_iter, c_iter)) { - /* identical results */ - if (prev != NULL) - return _db_tree_remove(prev, - ec_iter); - else - return -1; - } - if (c_iter->act_t_flg && ec_iter->nxt_t) { - /* new is shorter (true) */ - if (prev == NULL) - return -1; - rc += _db_tree_remove(&(ec_iter->nxt_t), - ec_iter->nxt_t); - ec_iter->act_t = c_iter->act_t; - ec_iter->act_t_flg = true; - } - if (c_iter->act_f_flg && ec_iter->nxt_f) { - /* new is shorter (false) */ - if (prev == NULL) - return -1; - rc += _db_tree_remove(&(ec_iter->nxt_f), - ec_iter->nxt_f); - ec_iter->act_f = c_iter->act_f; - ec_iter->act_f_flg = true; - } - - return rc; - } - - if (c_iter->nxt_t && ec_iter->act_t_flg) - /* existing is shorter (true) */ - return -1; - if (c_iter->nxt_f && ec_iter->act_f_flg) - /* existing is shorter (false) */ - return -1; - - if (c_iter->nxt_t) { - state_new = *state; - state_new.matched = true; - rc_tmp = _db_tree_sub_prune((prev ? - &ec_iter : NULL), - ec_iter->nxt_t, - c_iter->nxt_t, - &state_new); - rc += (rc_tmp > 0 ? rc_tmp : 0); - if (state->prefix_new && rc_tmp < 0) - return (rc > 0 ? rc : rc_tmp); - } - if (c_iter->nxt_f) { - state_new = *state; - state_new.matched = true; - rc_tmp = _db_tree_sub_prune((prev ? - &ec_iter : NULL), - ec_iter->nxt_f, - c_iter->nxt_f, - &state_new); - rc += (rc_tmp > 0 ? rc_tmp : 0); - if (state->prefix_new && rc_tmp < 0) - return (rc > 0 ? rc : rc_tmp); - } - } else if (db_chain_lt(ec_iter, c_iter)) { - /* less than */ - if (state->matched || state->prefix_new) - goto next; - state_new = *state; - state_new.prefix_exist = true; - - if (ec_iter->nxt_t) { - rc_tmp = _db_tree_sub_prune((prev ? - &ec_iter : NULL), - ec_iter->nxt_t, - c_iter, - &state_new); - rc += (rc_tmp > 0 ? rc_tmp : 0); - } - if (ec_iter->nxt_f) { - rc_tmp = _db_tree_sub_prune((prev ? - &ec_iter : NULL), - ec_iter->nxt_f, - c_iter, - &state_new); - rc += (rc_tmp > 0 ? rc_tmp : 0); - } - } else if (db_chain_gt(ec_iter, c_iter)) { - /* greater than */ - if (state->matched || state->prefix_exist) - goto next; - state_new = *state; - state_new.prefix_new = true; - - if (c_iter->nxt_t) { - rc_tmp = _db_tree_sub_prune(NULL, - ec_iter, - c_iter->nxt_t, - &state_new); - rc += (rc_tmp > 0 ? rc_tmp : 0); - if (rc_tmp < 0) - return (rc > 0 ? rc : rc_tmp); - } - if (c_iter->nxt_f) { - rc_tmp = _db_tree_sub_prune(NULL, - ec_iter, - c_iter->nxt_f, - &state_new); - rc += (rc_tmp > 0 ? rc_tmp : 0); - if (rc_tmp < 0) - return (rc > 0 ? rc : rc_tmp); - } - } - -next: - /* re-check current node and advance to the next node */ - if (db_chain_zombie(ec_iter)) { - ec_iter_tmp = ec_iter->lvl_nxt; - rc += _db_tree_remove(prev, ec_iter); - ec_iter = ec_iter_tmp; - } else - ec_iter = ec_iter->lvl_nxt; - } while (ec_iter); - - return rc; -} - -/** - * Free and reset the seccomp filter DB - * @param db the seccomp filter DB - * - * This function frees any existing filters and resets the filter DB to a - * default state; only the DB architecture is preserved. - * - */ -static void _db_reset(struct db_filter *db) -{ - struct db_sys_list *s_iter; - struct db_api_rule_list *r_iter; - - if (db == NULL) - return; - - /* free any filters */ - if (db->syscalls != NULL) { - s_iter = db->syscalls; - while (s_iter != NULL) { - db->syscalls = s_iter->next; - _db_tree_free(s_iter->chains); - free(s_iter); - s_iter = db->syscalls; - } - db->syscalls = NULL; - } - - /* free any rules */ - if (db->rules != NULL) { - /* split the loop first then loop and free */ - db->rules->prev->next = NULL; - r_iter = db->rules; - while (r_iter != NULL) { - db->rules = r_iter->next; - free(r_iter->args); - free(r_iter); - r_iter = db->rules; - } - db->rules = NULL; - } -} - -/** - * Intitalize a seccomp filter DB - * @param arch the architecture definition - * - * This function initializes a seccomp filter DB and readies it for use. - * Returns a pointer to the DB on success, NULL on failure. - * - */ -static struct db_filter *_db_init(const struct arch_def *arch) -{ - struct db_filter *db; - - db = malloc(sizeof(*db)); - if (db == NULL) - return NULL; - - /* clear the buffer for the first time and set the arch */ - memset(db, 0, sizeof(*db)); - db->arch = arch; - - /* reset the DB to a known state */ - _db_reset(db); - - return db; -} - -/** - * Destroy a seccomp filter DB - * @param db the seccomp filter DB - * - * This function destroys a seccomp filter DB. After calling this function, - * the filter should no longer be referenced. - * - */ -static void _db_release(struct db_filter *db) -{ - if (db == NULL) - return; - - /* free and reset the DB */ - _db_reset(db); - free(db); -} - -/** - * Destroy a seccomp filter snapshot - * @param snap the seccomp filter snapshot - * - * This function destroys a seccomp filter snapshot. After calling this - * function, the snapshot should no longer be referenced. - * - */ -static void _db_snap_release(struct db_filter_snap *snap) -{ - unsigned int iter; - - if (snap->filter_cnt > 0) { - for (iter = 0; iter < snap->filter_cnt; iter++) { - if (snap->filters[iter]) - _db_release(snap->filters[iter]); - } - free(snap->filters); - } - free(snap); -} - -/** - * Update the user specified portion of the syscall priority - * @param db the seccomp filter db - * @param syscall the syscall number - * @param priority the syscall priority - * - * This function sets, or updates, the syscall priority; the highest priority - * value between the existing and specified value becomes the new syscall - * priority. If the syscall entry does not already exist, a new phantom - * syscall entry is created as a placeholder. Returns zero on success, - * negative values on failure. - * - */ -static int _db_syscall_priority(struct db_filter *db, - int syscall, uint8_t priority) -{ - unsigned int sys_pri = _DB_PRI_USER(priority); - struct db_sys_list *s_new, *s_iter, *s_prev = NULL; - - assert(db != NULL); - - s_iter = db->syscalls; - while (s_iter != NULL && s_iter->num < syscall) { - s_prev = s_iter; - s_iter = s_iter->next; - } - - /* matched an existing syscall entry */ - if (s_iter != NULL && s_iter->num == syscall) { - if (sys_pri > (s_iter->priority & _DB_PRI_MASK_USER)) { - s_iter->priority &= (~_DB_PRI_MASK_USER); - s_iter->priority |= sys_pri; - } - return 0; - } - - /* no existing syscall entry - create a phantom entry */ - s_new = malloc(sizeof(*s_new)); - if (s_new == NULL) - return -ENOMEM; - memset(s_new, 0, sizeof(*s_new)); - s_new->num = syscall; - s_new->priority = sys_pri; - s_new->valid = false; - - /* add it before s_iter */ - if (s_prev != NULL) { - s_new->next = s_prev->next; - s_prev->next = s_new; - } else { - s_new->next = db->syscalls; - db->syscalls = s_new; - } - - return 0; -} - -/** - * Free and reset the seccomp filter collection - * @param col the seccomp filter collection - * @param def_action the default filter action - * - * This function frees any existing filter DBs and resets the collection to a - * default state. In the case of failure the filter collection may be in an - * unknown state and should be released. Returns zero on success, negative - * values on failure. - * - */ -int db_col_reset(struct db_filter_col *col, uint32_t def_action) -{ - unsigned int iter; - struct db_filter *db; - struct db_filter_snap *snap; - - if (col == NULL) - return -EINVAL; - - /* free any filters */ - for (iter = 0; iter < col->filter_cnt; iter++) - _db_release(col->filters[iter]); - col->filter_cnt = 0; - if (col->filters) - free(col->filters); - col->filters = NULL; - - /* set the endianess to undefined */ - col->endian = 0; - - /* set the default attribute values */ - col->attr.act_default = def_action; - col->attr.act_badarch = SCMP_ACT_KILL; - col->attr.nnp_enable = 1; - col->attr.tsync_enable = 0; - - /* set the state */ - col->state = _DB_STA_VALID; - - /* reset the initial db */ - db = _db_init(arch_def_native); - if (db == NULL) - return -ENOMEM; - if (db_col_db_add(col, db) < 0) { - _db_release(db); - return -ENOMEM; - } - - /* reset the transactions */ - while (col->snapshots) { - snap = col->snapshots; - col->snapshots = snap->next; - for (iter = 0; iter < snap->filter_cnt; iter++) - _db_release(snap->filters[iter]); - free(snap->filters); - free(snap); - } - - return 0; -} - -/** - * Intitalize a seccomp filter collection - * @param def_action the default filter action - * - * This function initializes a seccomp filter collection and readies it for - * use. Returns a pointer to the collection on success, NULL on failure. - * - */ -struct db_filter_col *db_col_init(uint32_t def_action) -{ - struct db_filter_col *col; - - col = malloc(sizeof(*col)); - if (col == NULL) - return NULL; - - /* clear the buffer for the first time */ - memset(col, 0, sizeof(*col)); - - /* reset the DB to a known state */ - if (db_col_reset(col, def_action) < 0) - goto init_failure; - - return col; - -init_failure: - db_col_release(col); - return NULL; -} - -/** - * Destroy a seccomp filter collection - * @param col the seccomp filter collection - * - * This function destroys a seccomp filter collection. After calling this - * function, the filter should no longer be referenced. - * - */ -void db_col_release(struct db_filter_col *col) -{ - unsigned int iter; - - if (col == NULL) - return; - - /* set the state, just in case */ - col->state = _DB_STA_FREED; - - /* free any filters */ - for (iter = 0; iter < col->filter_cnt; iter++) - _db_release(col->filters[iter]); - col->filter_cnt = 0; - if (col->filters) - free(col->filters); - col->filters = NULL; - - /* free the collection */ - free(col); -} - -/** - * Validate the seccomp action - * @param action the seccomp action - * - * Verify that the given action is a valid seccomp action; return zero if - * valid, -EINVAL if invalid. - */ -int db_action_valid(uint32_t action) -{ - if (action == SCMP_ACT_KILL) - return 0; - else if (action == SCMP_ACT_TRAP) - return 0; - else if ((action == SCMP_ACT_ERRNO(action & 0x0000ffff)) && - ((action & 0x0000ffff) < MAX_ERRNO)) - return 0; - else if (action == SCMP_ACT_TRACE(action & 0x0000ffff)) - return 0; - else if (action == SCMP_ACT_ALLOW) - return 0; - - return -EINVAL; -} - -/** - * Validate a filter collection - * @param col the seccomp filter collection - * - * This function validates a seccomp filter collection. Returns zero if the - * collection is valid, negative values on failure. - * - */ -int db_col_valid(struct db_filter_col *col) -{ - if (col != NULL && col->state == _DB_STA_VALID && col->filter_cnt > 0) - return 0; - return -EINVAL; -} - -/** - * Merge two filter collections - * @param col_dst the destination filter collection - * @param col_src the source filter collection - * - * This function merges two filter collections into the given destination - * collection. The source filter collection is no longer valid if the function - * returns successfully. Returns zero on success, negative values on failure. - * - */ -int db_col_merge(struct db_filter_col *col_dst, struct db_filter_col *col_src) -{ - unsigned int iter_a, iter_b; - struct db_filter **dbs; - - /* verify that the endianess is a match */ - if (col_dst->endian != col_src->endian) - return -EEXIST; - - /* make sure we don't have any arch/filter collisions */ - for (iter_a = 0; iter_a < col_dst->filter_cnt; iter_a++) { - for (iter_b = 0; iter_b < col_src->filter_cnt; iter_b++) { - if (col_dst->filters[iter_a]->arch->token == - col_src->filters[iter_b]->arch->token) - return -EEXIST; - } - } - - /* expand the destination */ - dbs = realloc(col_dst->filters, - sizeof(struct db_filter *) * - (col_dst->filter_cnt + col_src->filter_cnt)); - if (dbs == NULL) - return -ENOMEM; - col_dst->filters = dbs; - - /* transfer the architecture filters */ - for (iter_a = col_dst->filter_cnt, iter_b = 0; - iter_b < col_src->filter_cnt; iter_a++, iter_b++) { - col_dst->filters[iter_a] = col_src->filters[iter_b]; - col_dst->filter_cnt++; - } - - /* free the source */ - col_src->filter_cnt = 0; - db_col_release(col_src); - - return 0; -} - -/** - * Check to see if an architecture filter exists in the filter collection - * @param col the seccomp filter collection - * @param arch_token the architecture token - * - * Iterate through the given filter collection checking to see if a filter - * exists for the specified architecture. Returns -EEXIST if a filter is found, - * zero if a matching filter does not exist. - * - */ -int db_col_arch_exist(struct db_filter_col *col, uint32_t arch_token) -{ - unsigned int iter; - - for (iter = 0; iter < col->filter_cnt; iter++) - if (col->filters[iter]->arch->token == arch_token) - return -EEXIST; - - return 0; -} - -/** - * Get a filter attribute - * @param col the seccomp filter collection - * @param attr the filter attribute - * @param value the filter attribute value - * - * Get the requested filter attribute and provide it via @value. Returns zero - * on success, negative values on failure. - * - */ -int db_col_attr_get(const struct db_filter_col *col, - enum scmp_filter_attr attr, uint32_t *value) -{ - int rc = 0; - - switch (attr) { - case SCMP_FLTATR_ACT_DEFAULT: - *value = col->attr.act_default; - break; - case SCMP_FLTATR_ACT_BADARCH: - *value = col->attr.act_badarch; - break; - case SCMP_FLTATR_CTL_NNP: - *value = col->attr.nnp_enable; - break; - case SCMP_FLTATR_CTL_TSYNC: - *value = col->attr.tsync_enable; - break; - default: - rc = -EEXIST; - break; - } - - return rc; -} - -/** - * Set a filter attribute - * @param col the seccomp filter collection - * @param attr the filter attribute - * @param value the filter attribute value - * - * Set the requested filter attribute with the given value. Returns zero on - * success, negative values on failure. - * - */ -int db_col_attr_set(struct db_filter_col *col, - enum scmp_filter_attr attr, uint32_t value) -{ - int rc = 0; - - switch (attr) { - case SCMP_FLTATR_ACT_DEFAULT: - /* read only */ - return -EACCES; - break; - case SCMP_FLTATR_ACT_BADARCH: - if (db_action_valid(value) == 0) - col->attr.act_badarch = value; - else - return -EINVAL; - break; - case SCMP_FLTATR_CTL_NNP: - col->attr.nnp_enable = (value ? 1 : 0); - break; - case SCMP_FLTATR_CTL_TSYNC: - rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC); - if (rc == 1) { - /* supported */ - rc = 0; - col->attr.tsync_enable = (value ? 1 : 0); - } else if (rc == 0) - /* unsupported */ - rc = -EOPNOTSUPP; - break; - default: - rc = -EEXIST; - break; - } - - return rc; -} - -/** - * Add a new architecture filter to a filter collection - * @param col the seccomp filter collection - * @param arch the architecture - * - * This function adds a new architecture filter DB to an existing seccomp - * filter collection assuming there isn't a filter DB already present with the - * same architecture. Returns zero on success, negative values on failure. - * - */ -int db_col_db_new(struct db_filter_col *col, const struct arch_def *arch) -{ - int rc; - struct db_filter *db; - - db = _db_init(arch); - if (db == NULL) - return -ENOMEM; - rc = db_col_db_add(col, db); - if (rc < 0) - _db_release(db); - - return rc; -} - -/** - * Add a new filter DB to a filter collection - * @param col the seccomp filter collection - * @param db the seccomp filter DB - * - * This function adds an existing seccomp filter DB to an existing seccomp - * filter collection assuming there isn't a filter DB already present with the - * same architecture. Returns zero on success, negative values on failure. - * - */ -int db_col_db_add(struct db_filter_col *col, struct db_filter *db) -{ - struct db_filter **dbs; - - if (col->endian != 0 && col->endian != db->arch->endian) - return -EEXIST; - - if (db_col_arch_exist(col, db->arch->token)) - return -EEXIST; - - dbs = realloc(col->filters, - sizeof(struct db_filter *) * (col->filter_cnt + 1)); - if (dbs == NULL) - return -ENOMEM; - col->filters = dbs; - col->filter_cnt++; - col->filters[col->filter_cnt - 1] = db; - if (col->endian == 0) - col->endian = db->arch->endian; - - return 0; -} - -/** - * Remove a filter DB from a filter collection - * @param col the seccomp filter collection - * @param arch_token the architecture token - * - * This function removes an existing seccomp filter DB from an existing seccomp - * filter collection. Returns zero on success, negative values on failure. - * - */ -int db_col_db_remove(struct db_filter_col *col, uint32_t arch_token) -{ - unsigned int iter; - unsigned int found; - struct db_filter **dbs; - - if ((col->filter_cnt <= 0) || (db_col_arch_exist(col, arch_token) == 0)) - return -EINVAL; - - for (found = 0, iter = 0; iter < col->filter_cnt; iter++) { - if (found) - col->filters[iter - 1] = col->filters[iter]; - else if (col->filters[iter]->arch->token == arch_token) { - _db_release(col->filters[iter]); - found = 1; - } - } - col->filters[--col->filter_cnt] = NULL; - - if (col->filter_cnt > 0) { - /* NOTE: if we can't do the realloc it isn't fatal, we just - * have some extra space allocated */ - dbs = realloc(col->filters, - sizeof(struct db_filter *) * col->filter_cnt); - if (dbs != NULL) - col->filters = dbs; - } else { - /* this was the last filter so free all the associated memory - * and reset the endian token */ - free(col->filters); - col->filters = NULL; - col->endian = 0; - } - - return 0; -} - -/** - * Test if the argument filter can be skipped because it's a tautology - * @param arg argument filter - * - * If this argument filter applied to the lower 32 bit can be skipped this - * function returns false. - * - */ -static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg) -{ - if (arg->op == SCMP_CMP_MASKED_EQ && D64_LO(arg->mask) == 0) - return false; - - return true; -} - -/** - * Test if the argument filter can be skipped because it's a tautology - * @param arg argument filter - * - * If this argument filter applied to the upper 32 bit can be skipped this - * function returns false. - * - */ -static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg) -{ - if (arg->op == SCMP_CMP_MASKED_EQ && D64_HI(arg->mask) == 0) - return false; - - return true; -} - -/** - * Fixup the node based on the op/mask - * @param node the chain node - * - * Ensure the datum is masked as well. - * - */ -static void _db_node_mask_fixup(struct db_arg_chain_tree *node) -{ - node->datum &= node->mask; -} - -/** - * Generate a new filter rule for a 64 bit system - * @param arch the architecture definition - * @param action the filter action - * @param syscall the syscall number - * @param chain argument filter chain - * - * This function generates a new syscall filter for a 64 bit system. Returns - * zero on success, negative values on failure. - * - */ -static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch, - uint32_t action, - unsigned int syscall, - struct db_api_arg *chain) -{ - unsigned int iter; - int chain_len_max; - struct db_sys_list *s_new; - struct db_arg_chain_tree *c_iter_hi = NULL, *c_iter_lo = NULL; - struct db_arg_chain_tree *c_prev_hi = NULL, *c_prev_lo = NULL; - bool tf_flag; - - s_new = malloc(sizeof(*s_new)); - if (s_new == NULL) - return NULL; - memset(s_new, 0, sizeof(*s_new)); - s_new->num = syscall; - s_new->valid = true; - /* run through the argument chain */ - chain_len_max = arch_arg_count_max(arch); - if (chain_len_max < 0) - goto gen_64_failure; - for (iter = 0; iter < chain_len_max; iter++) { - if (chain[iter].valid == 0) - continue; - - /* TODO: handle the case were either hi or lo isn't needed */ - - /* skip generating instruction which are no-ops */ - if (!_db_arg_cmp_need_hi(&chain[iter]) && - !_db_arg_cmp_need_lo(&chain[iter])) - continue; - - c_iter_hi = malloc(sizeof(*c_iter_hi)); - if (c_iter_hi == NULL) - goto gen_64_failure; - memset(c_iter_hi, 0, sizeof(*c_iter_hi)); - c_iter_hi->refcnt = 1; - c_iter_lo = malloc(sizeof(*c_iter_lo)); - if (c_iter_lo == NULL) { - free(c_iter_hi); - goto gen_64_failure; - } - memset(c_iter_lo, 0, sizeof(*c_iter_lo)); - c_iter_lo->refcnt = 1; - - /* link this level to the previous level */ - if (c_prev_lo != NULL) { - if (!tf_flag) { - c_prev_lo->nxt_f = c_iter_hi; - c_prev_hi->nxt_f = c_iter_hi; - c_iter_hi->refcnt++; - } else - c_prev_lo->nxt_t = c_iter_hi; - } else - s_new->chains = c_iter_hi; - s_new->node_cnt += 2; - - /* set the arg, op, and datum fields */ - c_iter_hi->arg = chain[iter].arg; - c_iter_lo->arg = chain[iter].arg; - c_iter_hi->arg_offset = arch_arg_offset_hi(arch, - c_iter_hi->arg); - c_iter_lo->arg_offset = arch_arg_offset_lo(arch, - c_iter_lo->arg); - switch (chain[iter].op) { - case SCMP_CMP_GT: - c_iter_hi->op = SCMP_CMP_GE; - c_iter_lo->op = SCMP_CMP_GT; - tf_flag = true; - break; - case SCMP_CMP_NE: - c_iter_hi->op = SCMP_CMP_EQ; - c_iter_lo->op = SCMP_CMP_EQ; - tf_flag = false; - break; - case SCMP_CMP_LT: - c_iter_hi->op = SCMP_CMP_GE; - c_iter_lo->op = SCMP_CMP_GE; - tf_flag = false; - break; - case SCMP_CMP_LE: - c_iter_hi->op = SCMP_CMP_GE; - c_iter_lo->op = SCMP_CMP_GT; - tf_flag = false; - break; - default: - c_iter_hi->op = chain[iter].op; - c_iter_lo->op = chain[iter].op; - tf_flag = true; - } - c_iter_hi->mask = D64_HI(chain[iter].mask); - c_iter_lo->mask = D64_LO(chain[iter].mask); - c_iter_hi->datum = D64_HI(chain[iter].datum); - c_iter_lo->datum = D64_LO(chain[iter].datum); - - /* fixup the mask/datum */ - _db_node_mask_fixup(c_iter_hi); - _db_node_mask_fixup(c_iter_lo); - - /* link the hi and lo chain nodes */ - c_iter_hi->nxt_t = c_iter_lo; - - c_prev_hi = c_iter_hi; - c_prev_lo = c_iter_lo; - } - if (c_iter_lo != NULL) { - /* set the leaf node */ - if (!tf_flag) { - c_iter_lo->act_f_flg = true; - c_iter_lo->act_f = action; - c_iter_hi->act_f_flg = true; - c_iter_hi->act_f = action; - } else { - c_iter_lo->act_t_flg = true; - c_iter_lo->act_t = action; - } - } else - s_new->action = action; - - return s_new; - -gen_64_failure: - /* free the new chain and its syscall struct */ - _db_tree_free(s_new->chains); - free(s_new); - return NULL; -} - -/** - * Generate a new filter rule for a 32 bit system - * @param arch the architecture definition - * @param action the filter action - * @param syscall the syscall number - * @param chain argument filter chain - * - * This function generates a new syscall filter for a 32 bit system. Returns - * zero on success, negative values on failure. - * - */ -static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch, - uint32_t action, - unsigned int syscall, - struct db_api_arg *chain) -{ - unsigned int iter; - int chain_len_max; - struct db_sys_list *s_new; - struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL; - bool tf_flag; - - s_new = malloc(sizeof(*s_new)); - if (s_new == NULL) - return NULL; - memset(s_new, 0, sizeof(*s_new)); - s_new->num = syscall; - s_new->valid = true; - /* run through the argument chain */ - chain_len_max = arch_arg_count_max(arch); - if (chain_len_max < 0) - goto gen_32_failure; - for (iter = 0; iter < chain_len_max; iter++) { - if (chain[iter].valid == 0) - continue; - - /* skip generating instructions which are no-ops */ - if (!_db_arg_cmp_need_lo(&chain[iter])) - continue; - - c_iter = malloc(sizeof(*c_iter)); - if (c_iter == NULL) - goto gen_32_failure; - memset(c_iter, 0, sizeof(*c_iter)); - c_iter->refcnt = 1; - c_iter->arg = chain[iter].arg; - c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg); - c_iter->op = chain[iter].op; - /* implicitly strips off the upper 32 bit */ - c_iter->mask = chain[iter].mask; - c_iter->datum = chain[iter].datum; - - /* link in the new node and update the chain */ - if (c_prev != NULL) { - if (tf_flag) - c_prev->nxt_t = c_iter; - else - c_prev->nxt_f = c_iter; - } else - s_new->chains = c_iter; - s_new->node_cnt++; - - /* rewrite the op to reduce the op/datum combos */ - switch (c_iter->op) { - case SCMP_CMP_NE: - c_iter->op = SCMP_CMP_EQ; - tf_flag = false; - break; - case SCMP_CMP_LT: - c_iter->op = SCMP_CMP_GE; - tf_flag = false; - break; - case SCMP_CMP_LE: - c_iter->op = SCMP_CMP_GT; - tf_flag = false; - break; - default: - tf_flag = true; - } - - /* fixup the mask/datum */ - _db_node_mask_fixup(c_iter); - - c_prev = c_iter; - } - if (c_iter != NULL) { - /* set the leaf node */ - if (tf_flag) { - c_iter->act_t_flg = true; - c_iter->act_t = action; - } else { - c_iter->act_f_flg = true; - c_iter->act_f = action; - } - } else - s_new->action = action; - - return s_new; - -gen_32_failure: - /* free the new chain and its syscall struct */ - _db_tree_free(s_new->chains); - free(s_new); - return NULL; -} - -/** - * Add a new rule to the seccomp filter DB - * @param db the seccomp filter db - * @param rule the filter rule - * - * This function adds a new syscall filter to the seccomp filter DB, adding to - * the existing filters for the syscall, unless no argument specific filters - * are present (filtering only on the syscall). When adding new chains, the - * shortest chain, or most inclusive filter match, will be entered into the - * filter DB. Returns zero on success, negative values on failure. - * - */ -int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule) -{ - int rc = -ENOMEM; - int syscall = rule->syscall; - uint32_t action = rule->action; - struct db_api_arg *chain = rule->args; - struct db_sys_list *s_new, *s_iter, *s_prev = NULL; - struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL; - struct db_arg_chain_tree *ec_iter; - struct db_prune_state state; - bool rm_flag = false; - unsigned int new_chain_cnt = 0; - unsigned int n_cnt; - - assert(db != NULL); - - /* do all our possible memory allocation up front so we don't have to - * worry about failure once we get to the point where we start updating - * the filter db */ - if (db->arch->size == ARCH_SIZE_64) - s_new = _db_rule_gen_64(db->arch, action, syscall, chain); - else if (db->arch->size == ARCH_SIZE_32) - s_new = _db_rule_gen_32(db->arch, action, syscall, chain); - else - return -EFAULT; - if (s_new == NULL) - return -ENOMEM; - new_chain_cnt = s_new->node_cnt; - - /* no more failures allowed after this point that would result in the - * stored filter being in an inconsistent state */ - - /* find a matching syscall/chain or insert a new one */ - s_iter = db->syscalls; - while (s_iter != NULL && s_iter->num < syscall) { - s_prev = s_iter; - s_iter = s_iter->next; - } -add_reset: - s_new->node_cnt = new_chain_cnt; - s_new->priority = _DB_PRI_MASK_CHAIN - s_new->node_cnt; - c_prev = NULL; - c_iter = s_new->chains; - if (s_iter != NULL) - ec_iter = s_iter->chains; - else - ec_iter = NULL; - if (s_iter == NULL || s_iter->num != syscall) { - /* new syscall, add before s_iter */ - if (s_prev != NULL) { - s_new->next = s_prev->next; - s_prev->next = s_new; - } else { - s_new->next = db->syscalls; - db->syscalls = s_new; - } - return 0; - } else if (s_iter->chains == NULL) { - if (rm_flag || !s_iter->valid) { - /* we are here because our previous pass cleared the - * entire syscall chain when searching for a subtree - * match or the existing syscall entry is a phantom, - * so either way add the new chain */ - s_iter->chains = s_new->chains; - s_iter->action = s_new->action; - s_iter->node_cnt = s_new->node_cnt; - if (s_iter->valid) - s_iter->priority = s_new->priority; - s_iter->valid = true; - free(s_new); - rc = 0; - goto add_priority_update; - } else - /* syscall exists without any chains - existing filter - * is at least as large as the new entry so cleanup and - * exit */ - goto add_free_ok; - } else if (s_iter->chains != NULL && s_new->chains == NULL) { - /* syscall exists with chains but the new filter has no chains - * so we need to clear the existing chains and exit */ - _db_tree_free(s_iter->chains); - s_iter->chains = NULL; - s_iter->node_cnt = 0; - s_iter->action = action; - goto add_free_ok; - } - - /* check for sub-tree matches */ - memset(&state, 0, sizeof(state)); - rc = _db_tree_sub_prune(&(s_iter->chains), ec_iter, c_iter, &state); - if (rc > 0) { - rm_flag = true; - s_iter->node_cnt -= rc; - goto add_reset; - } else if (rc < 0) - goto add_free_ok; - - /* syscall exists and has at least one existing chain - start at the - * top and walk the two chains */ - do { - /* insert the new rule into the existing tree */ - if (db_chain_eq(c_iter, ec_iter)) { - /* found a matching node on this chain level */ - if (db_chain_action(c_iter) && - db_chain_action(ec_iter)) { - /* both are "action" nodes */ - if (c_iter->act_t_flg && ec_iter->act_t_flg) { - if (ec_iter->act_t != action) - goto add_free_exist; - } else if (c_iter->act_t_flg) { - ec_iter->act_t_flg = true; - ec_iter->act_t = action; - } - if (c_iter->act_f_flg && ec_iter->act_f_flg) { - if (ec_iter->act_f != action) - goto add_free_exist; - } else if (c_iter->act_f_flg) { - ec_iter->act_f_flg = true; - ec_iter->act_f = action; - } - if (ec_iter->act_t_flg == ec_iter->act_f_flg && - ec_iter->act_t == ec_iter->act_f) { - n_cnt = _db_tree_remove( - &(s_iter->chains), - ec_iter); - s_iter->node_cnt -= n_cnt; - goto add_free_ok; - } - } else if (db_chain_action(c_iter)) { - /* new is shorter */ - if (c_iter->act_t_flg) { - rc = _db_tree_act_check(ec_iter->nxt_t, - action); - if (rc < 0) - goto add_free; - n_cnt = _db_tree_free(ec_iter->nxt_t); - ec_iter->nxt_t = NULL; - ec_iter->act_t_flg = true; - ec_iter->act_t = action; - } else { - rc = _db_tree_act_check(ec_iter->nxt_f, - action); - if (rc < 0) - goto add_free; - n_cnt = _db_tree_free(ec_iter->nxt_f); - ec_iter->nxt_f = NULL; - ec_iter->act_f_flg = true; - ec_iter->act_f = action; - } - s_iter->node_cnt -= n_cnt; - } - if (c_iter->nxt_t != NULL) { - if (ec_iter->nxt_t != NULL) { - /* jump to the next level */ - c_prev = c_iter; - c_iter = c_iter->nxt_t; - ec_iter = ec_iter->nxt_t; - s_new->node_cnt--; - } else if (ec_iter->act_t_flg) { - /* existing is shorter */ - if (ec_iter->act_t == action) - goto add_free_ok; - goto add_free_exist; - } else { - /* add a new branch */ - c_prev = c_iter; - ec_iter->nxt_t = c_iter->nxt_t; - s_iter->node_cnt += - (s_new->node_cnt - 1); - goto add_free_match; - } - } else if (c_iter->nxt_f != NULL) { - if (ec_iter->nxt_f != NULL) { - /* jump to the next level */ - c_prev = c_iter; - c_iter = c_iter->nxt_f; - ec_iter = ec_iter->nxt_f; - s_new->node_cnt--; - } else if (ec_iter->act_f_flg) { - /* existing is shorter */ - if (ec_iter->act_f == action) - goto add_free_ok; - goto add_free_exist; - } else { - /* add a new branch */ - c_prev = c_iter; - ec_iter->nxt_f = c_iter->nxt_f; - s_iter->node_cnt += - (s_new->node_cnt - 1); - goto add_free_match; - } - } else - goto add_free_ok; - } else { - /* need to check other nodes on this level */ - if (db_chain_lt(c_iter, ec_iter)) { - if (ec_iter->lvl_prv == NULL) { - /* add to the start of the level */ - ec_iter->lvl_prv = c_iter; - c_iter->lvl_nxt = ec_iter; - if (ec_iter == s_iter->chains) - s_iter->chains = c_iter; - s_iter->node_cnt += s_new->node_cnt; - goto add_free_match; - } else - ec_iter = ec_iter->lvl_prv; - } else { - if (ec_iter->lvl_nxt == NULL) { - /* add to the end of the level */ - ec_iter->lvl_nxt = c_iter; - c_iter->lvl_prv = ec_iter; - s_iter->node_cnt += s_new->node_cnt; - goto add_free_match; - } else if (db_chain_lt(c_iter, - ec_iter->lvl_nxt)) { - /* add new chain in between */ - c_iter->lvl_nxt = ec_iter->lvl_nxt; - ec_iter->lvl_nxt->lvl_prv = c_iter; - ec_iter->lvl_nxt = c_iter; - c_iter->lvl_prv = ec_iter; - s_iter->node_cnt += s_new->node_cnt; - goto add_free_match; - } else - ec_iter = ec_iter->lvl_nxt; - } - } - } while ((c_iter != NULL) && (ec_iter != NULL)); - - /* we should never be here! */ - return -EFAULT; - -add_free_exist: - rc = -EEXIST; - goto add_free; -add_free_ok: - rc = 0; -add_free: - /* free the new chain and its syscall struct */ - _db_tree_free(s_new->chains); - free(s_new); - goto add_priority_update; -add_free_match: - /* free the matching portion of new chain */ - if (c_prev != NULL) { - c_prev->nxt_t = NULL; - c_prev->nxt_f = NULL; - _db_tree_free(s_new->chains); - } - free(s_new); - rc = 0; -add_priority_update: - /* update the priority */ - if (s_iter != NULL) { - s_iter->priority &= (~_DB_PRI_MASK_CHAIN); - s_iter->priority |= (_DB_PRI_MASK_CHAIN - s_iter->node_cnt); - } - return rc; -} - -/** - * Set the priority of a given syscall - * @param col the filter collection - * @param syscall the syscall number - * @param priority priority value, higher value == higher priority - * - * This function sets the priority of the given syscall; this value is used - * when generating the seccomp filter code such that higher priority syscalls - * will incur less filter code overhead than the lower priority syscalls in the - * filter. Returns zero on success, negative values on failure. - * - */ -int db_col_syscall_priority(struct db_filter_col *col, - int syscall, uint8_t priority) -{ - int rc = 0, rc_tmp; - unsigned int iter; - int sc_tmp; - struct db_filter *filter; - - for (iter = 0; iter < col->filter_cnt; iter++) { - filter = col->filters[iter]; - sc_tmp = syscall; - - rc_tmp = arch_syscall_translate(filter->arch, &sc_tmp); - if (rc_tmp < 0) - goto priority_failure; - - /* if this is a pseudo syscall (syscall < 0) then we need to - * rewrite the syscall for some arch specific reason */ - if (sc_tmp < 0) { - /* we set this as a strict op - we don't really care - * since priorities are a "best effort" thing - as we - * want to catch the -EDOM error and bail on this - * architecture */ - rc_tmp = arch_syscall_rewrite(filter->arch, &sc_tmp); - if (rc_tmp == -EDOM) - continue; - if (rc_tmp < 0) - goto priority_failure; - } - - rc_tmp = _db_syscall_priority(filter, sc_tmp, priority); - -priority_failure: - if (rc == 0 && rc_tmp < 0) - rc = rc_tmp; - } - - return rc; -} - -/** - * Add a new rule to the current filter - * @param col the filter collection - * @param strict the strict flag - * @param action the filter action - * @param syscall the syscall number - * @param arg_cnt the number of argument filters in the argument filter chain - * @param arg_array the argument filter chain, (uint, enum scmp_compare, ulong) - * - * This function adds a new argument/comparison/value to the seccomp filter for - * a syscall; multiple arguments can be specified and they will be chained - * together (essentially AND'd together) in the filter. When the strict flag - * is true the function will fail if the exact rule can not be added to the - * filter, if the strict flag is false the function will not fail if the - * function needs to adjust the rule due to architecture specifics. Returns - * zero on success, negative values on failure. - * - */ -int db_col_rule_add(struct db_filter_col *col, - bool strict, uint32_t action, int syscall, - unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array) -{ - int rc = 0, rc_tmp; - unsigned int iter; - unsigned int chain_len; - unsigned int arg_num; - size_t chain_size; - struct db_api_arg *chain = NULL; - struct scmp_arg_cmp arg_data; - - /* collect the arguments for the filter rule */ - chain_len = ARG_COUNT_MAX; - chain_size = sizeof(*chain) * chain_len; - chain = malloc(chain_size); - if (chain == NULL) - return -ENOMEM; - memset(chain, 0, chain_size); - for (iter = 0; iter < arg_cnt; iter++) { - arg_data = arg_array[iter]; - arg_num = arg_data.arg; - if (arg_num < chain_len && chain[arg_num].valid == 0) { - chain[arg_num].valid = 1; - chain[arg_num].arg = arg_num; - chain[arg_num].op = arg_data.op; - /* XXX - we should check datum/mask size against the - * arch definition, e.g. 64 bit datum on x86 */ - switch (chain[arg_num].op) { - case SCMP_CMP_NE: - case SCMP_CMP_LT: - case SCMP_CMP_LE: - case SCMP_CMP_EQ: - case SCMP_CMP_GE: - case SCMP_CMP_GT: - chain[arg_num].mask = DATUM_MAX; - chain[arg_num].datum = arg_data.datum_a; - break; - case SCMP_CMP_MASKED_EQ: - chain[arg_num].mask = arg_data.datum_a; - chain[arg_num].datum = arg_data.datum_b; - break; - default: - rc = -EINVAL; - goto add_return; - } - } else { - rc = -EINVAL; - goto add_return; - } - } - - for (iter = 0; iter < col->filter_cnt; iter++) { - rc_tmp = arch_filter_rule_add(col, col->filters[iter], strict, - action, syscall, - chain_len, chain); - if (rc == 0 && rc_tmp < 0) - rc = rc_tmp; - } - -add_return: - if (chain != NULL) - free(chain); - return rc; -} - -/** - * Start a new seccomp filter transaction - * @param col the filter collection - * - * This function starts a new seccomp filter transaction for the given filter - * collection. Returns zero on success, negative values on failure. - * - */ -int db_col_transaction_start(struct db_filter_col *col) -{ - unsigned int iter; - size_t args_size; - struct db_filter_snap *snap; - struct db_filter *filter_o, *filter_s; - struct db_api_rule_list *rule_o, *rule_s; - - /* allocate the snapshot */ - snap = malloc(sizeof(*snap)); - if (snap == NULL) - return -ENOMEM; - snap->filters = malloc(sizeof(struct db_filter *) * col->filter_cnt); - if (snap->filters == NULL) { - free(snap); - return -ENOMEM; - } - snap->filter_cnt = col->filter_cnt; - for (iter = 0; iter < snap->filter_cnt; iter++) - snap->filters[iter] = NULL; - snap->next = NULL; - - /* create a snapshot of the current filter state */ - for (iter = 0; iter < col->filter_cnt; iter++) { - /* allocate a new filter */ - filter_o = col->filters[iter]; - filter_s = _db_init(filter_o->arch); - if (filter_s == NULL) - goto trans_start_failure; - snap->filters[iter] = filter_s; - - /* create a filter snapshot from existing rules */ - rule_o = filter_o->rules; - if (rule_o == NULL) - continue; - do { - /* copy the rule */ - rule_s = malloc(sizeof(*rule_s)); - if (rule_s == NULL) - goto trans_start_failure; - args_size = sizeof(*rule_s->args) * rule_o->args_cnt; - rule_s->args = malloc(args_size); - if (rule_s->args == NULL) { - free(rule_s); - goto trans_start_failure; - } - rule_s->action = rule_o->action; - rule_s->syscall = rule_o->syscall; - rule_s->args_cnt = rule_o->args_cnt; - memcpy(rule_s->args, rule_o->args, args_size); - if (filter_s->rules != NULL) { - rule_s->prev = filter_s->rules->prev; - rule_s->next = filter_s->rules; - filter_s->rules->prev->next = rule_s; - filter_s->rules->prev = rule_s; - } else { - rule_s->prev = rule_s; - rule_s->next = rule_s; - filter_s->rules = rule_s; - } - - /* insert the rule into the filter */ - if (db_rule_add(filter_s, rule_o) != 0) - goto trans_start_failure; - - /* next rule */ - rule_o = rule_o->next; - } while (rule_o != filter_o->rules); - } - - /* add the snapshot to the list */ - snap->next = col->snapshots; - col->snapshots = snap; - - return 0; - -trans_start_failure: - _db_snap_release(snap); - return -ENOMEM; -} - -/** - * Abort the top most seccomp filter transaction - * @param col the filter collection - * - * This function aborts the most recent seccomp filter transaction. - * - */ -void db_col_transaction_abort(struct db_filter_col *col) -{ - int iter; - unsigned int filter_cnt; - struct db_filter **filters; - struct db_filter_snap *snap; - - if (col->snapshots == NULL) - return; - - /* replace the current filter with the last snapshot */ - snap = col->snapshots; - col->snapshots = snap->next; - filter_cnt = col->filter_cnt; - filters = col->filters; - col->filter_cnt = snap->filter_cnt; - col->filters = snap->filters; - free(snap); - - /* free the filter we swapped out */ - for (iter = 0; iter < filter_cnt; iter++) - _db_release(filters[iter]); - free(filters); -} - -/** - * Commit the top most seccomp filter transaction - * @param col the filter collection - * - * This function commits the most recent seccomp filter transaction. - * - */ -void db_col_transaction_commit(struct db_filter_col *col) -{ - struct db_filter_snap *snap; - - snap = col->snapshots; - col->snapshots = snap->next; - _db_snap_release(snap); -}