|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Enhanced Seccomp Filter DB
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Copyright (c) 2012,2016,2018 Red Hat <pmoore@redhat.com>
|
|
Packit |
56e23f |
* Copyright (c) 2019 Cisco Systems, Inc. <pmoore2@cisco.com>
|
|
Packit |
56e23f |
* Author: Paul Moore <paul@paul-moore.com>
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/*
|
|
Packit |
56e23f |
* This library is free software; you can redistribute it and/or modify it
|
|
Packit |
56e23f |
* under the terms of version 2.1 of the GNU Lesser General Public License as
|
|
Packit |
56e23f |
* published by the Free Software Foundation.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This library is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit |
56e23f |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
Packit |
56e23f |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
Packit |
56e23f |
* for more details.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
56e23f |
* along with this library; if not, see <http://www.gnu.org/licenses>.
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
#include <assert.h>
|
|
Packit |
56e23f |
#include <errno.h>
|
|
Packit |
56e23f |
#include <inttypes.h>
|
|
Packit |
56e23f |
#include <stdlib.h>
|
|
Packit |
56e23f |
#include <string.h>
|
|
Packit |
56e23f |
#include <stdarg.h>
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
#include <seccomp.h>
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
#include "arch.h"
|
|
Packit |
56e23f |
#include "db.h"
|
|
Packit |
56e23f |
#include "system.h"
|
|
Packit |
56e23f |
#include "helper.h"
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* state values */
|
|
Packit |
56e23f |
#define _DB_STA_VALID 0xA1B2C3D4
|
|
Packit |
56e23f |
#define _DB_STA_FREED 0x1A2B3C4D
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* the priority field is fairly simple - without any user hints, or in the case
|
|
Packit |
56e23f |
* of a hint "tie", we give higher priority to syscalls with less chain nodes
|
|
Packit |
56e23f |
* (filter is easier to evaluate) */
|
|
Packit |
56e23f |
#define _DB_PRI_MASK_CHAIN 0x0000FFFF
|
|
Packit |
56e23f |
#define _DB_PRI_MASK_USER 0x00FF0000
|
|
Packit |
56e23f |
#define _DB_PRI_USER(x) (((x) << 16) & _DB_PRI_MASK_USER)
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* prove information about the sub-tree check results */
|
|
Packit |
56e23f |
struct db_iter_state {
|
|
Packit |
56e23f |
#define _DB_IST_NONE 0x00000000
|
|
Packit |
56e23f |
#define _DB_IST_MATCH 0x00000001
|
|
Packit |
56e23f |
#define _DB_IST_MATCH_ONCE 0x00000002
|
|
Packit |
56e23f |
#define _DB_IST_X_FINISHED 0x00000010
|
|
Packit |
56e23f |
#define _DB_IST_N_FINISHED 0x00000020
|
|
Packit |
56e23f |
#define _DB_IST_X_PREFIX 0x00000100
|
|
Packit |
56e23f |
#define _DB_IST_N_PREFIX 0x00000200
|
|
Packit |
56e23f |
#define _DB_IST_M_MATCHSET (_DB_IST_MATCH|_DB_IST_MATCH_ONCE)
|
|
Packit |
56e23f |
#define _DB_IST_M_REDUNDANT (_DB_IST_MATCH| \
|
|
Packit |
56e23f |
_DB_IST_X_FINISHED| \
|
|
Packit |
56e23f |
_DB_IST_N_PREFIX)
|
|
Packit |
56e23f |
unsigned int flags;
|
|
Packit |
56e23f |
uint32_t action;
|
|
Packit |
56e23f |
struct db_sys_list *sx;
|
|
Packit |
56e23f |
};
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
static unsigned int _db_node_put(struct db_arg_chain_tree **node);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Define the syscall argument priority for nodes on the same level of the tree
|
|
Packit |
56e23f |
* @param a tree node
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Prioritize the syscall argument value, taking into account hi/lo words.
|
|
Packit |
56e23f |
* Should only ever really be called by _db_chain_{lt,eq}(). Returns an
|
|
Packit |
56e23f |
* arbitrary value indicating priority.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static unsigned int __db_chain_arg_priority(const struct db_arg_chain_tree *a)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
return (a->arg << 1) + (a->arg_h_flg ? 1 : 0);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Define the "op" priority for nodes on the same level of the tree
|
|
Packit |
56e23f |
* @param op the argument operator
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Prioritize the syscall argument comparison operator. Should only ever
|
|
Packit |
56e23f |
* really be called by _db_chain_{lt,eq}(). Returns an arbitrary value
|
|
Packit |
56e23f |
* indicating priority.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static unsigned int __db_chain_op_priority(enum scmp_compare op)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
/* the distinction between LT/LT and GT/GE is mostly to make the
|
|
Packit |
56e23f |
* ordering as repeatable as possible regardless of the order in which
|
|
Packit |
56e23f |
* the rules are added */
|
|
Packit |
56e23f |
switch (op) {
|
|
Packit |
56e23f |
case SCMP_CMP_MASKED_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_NE:
|
|
Packit |
56e23f |
return 3;
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
return 2;
|
|
Packit |
56e23f |
case SCMP_CMP_GE:
|
|
Packit |
56e23f |
case SCMP_CMP_GT:
|
|
Packit |
56e23f |
return 1;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Determine if node "a" is less than node "b"
|
|
Packit |
56e23f |
* @param a tree node
|
|
Packit |
56e23f |
* @param b tree node
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* The logic is best explained by looking at the comparison code in the
|
|
Packit |
56e23f |
* function.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static bool _db_chain_lt(const struct db_arg_chain_tree *a,
|
|
Packit |
56e23f |
const struct db_arg_chain_tree *b)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int a_arg, b_arg;
|
|
Packit |
56e23f |
unsigned int a_op, b_op;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
a_arg = __db_chain_arg_priority(a);
|
|
Packit |
56e23f |
b_arg = __db_chain_arg_priority(b);
|
|
Packit |
56e23f |
if (a_arg < b_arg)
|
|
Packit |
56e23f |
return true;
|
|
Packit |
56e23f |
else if (a_arg > b_arg)
|
|
Packit |
56e23f |
return false;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
a_op = __db_chain_op_priority(a->op_orig);
|
|
Packit |
56e23f |
b_op = __db_chain_op_priority(b->op_orig);
|
|
Packit |
56e23f |
if (a_op < b_op)
|
|
Packit |
56e23f |
return true;
|
|
Packit |
56e23f |
else if (a_op > b_op)
|
|
Packit |
56e23f |
return false;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* NOTE: at this point the arg and op priorities are equal */
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
switch (a->op_orig) {
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
/* in order to ensure proper ordering for LT/LE comparisons we
|
|
Packit |
56e23f |
* need to invert the argument value so smaller values come
|
|
Packit |
56e23f |
* first */
|
|
Packit |
56e23f |
if (a->datum > b->datum)
|
|
Packit |
56e23f |
return true;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
if (a->datum < b->datum)
|
|
Packit |
56e23f |
return true;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return false;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Determine if two nodes have equal argument datum values
|
|
Packit |
56e23f |
* @param a tree node
|
|
Packit |
56e23f |
* @param b tree node
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* In order to return true the nodes must have the same datum and mask for the
|
|
Packit |
56e23f |
* same argument.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static bool _db_chain_eq(const struct db_arg_chain_tree *a,
|
|
Packit |
56e23f |
const struct db_arg_chain_tree *b)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int a_arg, b_arg;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
a_arg = __db_chain_arg_priority(a);
|
|
Packit |
56e23f |
b_arg = __db_chain_arg_priority(b);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return ((a_arg == b_arg) && (a->op == b->op) &&
|
|
Packit |
56e23f |
(a->datum == b->datum) && (a->mask == b->mask));
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Determine if a given tree node is a leaf node
|
|
Packit |
56e23f |
* @param iter the node to test
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* A leaf node is a node with no other nodes beneath it.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static bool _db_chain_leaf(const struct db_arg_chain_tree *iter)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
return (iter->nxt_t == NULL && iter->nxt_f == NULL);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Determine if a given tree node is a zombie node
|
|
Packit |
56e23f |
* @param iter the node to test
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* A zombie node is a leaf node that also has no true or false actions.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static bool _db_chain_zombie(const struct db_arg_chain_tree *iter)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
return (_db_chain_leaf(iter) &&
|
|
Packit |
56e23f |
!(iter->act_t_flg) && !(iter->act_f_flg));
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Get a node reference
|
|
Packit |
56e23f |
* @param node pointer to a node
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function gets a reference to an individual node. Returns a pointer
|
|
Packit |
56e23f |
* to the node.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static struct db_arg_chain_tree *_db_node_get(struct db_arg_chain_tree *node)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
if (node != NULL)
|
|
Packit |
56e23f |
node->refcnt++;
|
|
Packit |
56e23f |
return node;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Garbage collect a level of the tree
|
|
Packit |
56e23f |
* @param node tree node
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Check the entire level on which @node resides, if there is no other part of
|
|
Packit |
56e23f |
* the tree which points to a node on this level, remove the entire level.
|
|
Packit |
56e23f |
* Returns the number of nodes removed.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static unsigned int _db_level_clean(struct db_arg_chain_tree *node)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int cnt = 0;
|
|
Packit |
56e23f |
unsigned int links;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *n = node;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *start;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
while (n->lvl_prv)
|
|
Packit |
56e23f |
n = n->lvl_prv;
|
|
Packit |
56e23f |
start = n;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
while (n != NULL) {
|
|
Packit |
56e23f |
links = 0;
|
|
Packit |
56e23f |
if (n->lvl_prv)
|
|
Packit |
56e23f |
links++;
|
|
Packit |
56e23f |
if (n->lvl_nxt)
|
|
Packit |
56e23f |
links++;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (n->refcnt > links)
|
|
Packit |
56e23f |
return cnt;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
n = n->lvl_nxt;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
n = start;
|
|
Packit |
56e23f |
while (n != NULL)
|
|
Packit |
56e23f |
cnt += _db_node_put(&n);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return cnt;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Free a syscall filter argument chain tree
|
|
Packit |
56e23f |
* @param tree the argument chain list
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function drops a reference to the tree pointed to by @tree and garbage
|
|
Packit |
56e23f |
* collects the top level. Returns the number of nodes removed.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static unsigned int _db_tree_put(struct db_arg_chain_tree **tree)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int cnt;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
cnt = _db_node_put(tree);
|
|
Packit |
56e23f |
if (*tree)
|
|
Packit |
56e23f |
cnt += _db_level_clean(*tree);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return cnt;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Release a node reference
|
|
Packit |
56e23f |
* @param node pointer to a node
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function drops a reference to an individual node, unless this is the
|
|
Packit |
56e23f |
* last reference in which the entire sub-tree is affected. Returns the number
|
|
Packit |
56e23f |
* of nodes freed.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static unsigned int _db_node_put(struct db_arg_chain_tree **node)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int cnt = 0;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *n = *node;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *lvl_p, *lvl_n, *nxt_t, *nxt_f;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (n == NULL)
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (--(n->refcnt) == 0) {
|
|
Packit |
56e23f |
lvl_p = n->lvl_prv;
|
|
Packit |
56e23f |
lvl_n = n->lvl_nxt;
|
|
Packit |
56e23f |
nxt_t = n->nxt_t;
|
|
Packit |
56e23f |
nxt_f = n->nxt_f;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* split the current level */
|
|
Packit |
56e23f |
/* NOTE: we still hold a ref for both lvl_p and lvl_n */
|
|
Packit |
56e23f |
if (lvl_p)
|
|
Packit |
56e23f |
lvl_p->lvl_nxt = NULL;
|
|
Packit |
56e23f |
if (lvl_n)
|
|
Packit |
56e23f |
lvl_n->lvl_prv = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* drop refcnts on the current level */
|
|
Packit |
56e23f |
if (lvl_p)
|
|
Packit |
56e23f |
cnt += _db_node_put(&lvl_p);
|
|
Packit |
56e23f |
if (lvl_n)
|
|
Packit |
56e23f |
cnt += _db_node_put(&lvl_n);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* re-link current level if it still exists */
|
|
Packit |
56e23f |
if (lvl_p)
|
|
Packit |
56e23f |
lvl_p->lvl_nxt = _db_node_get(lvl_n);
|
|
Packit |
56e23f |
if (lvl_n)
|
|
Packit |
56e23f |
lvl_n->lvl_prv = _db_node_get(lvl_p);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* update caller's pointer */
|
|
Packit |
56e23f |
if (lvl_p)
|
|
Packit |
56e23f |
*node = lvl_p;
|
|
Packit |
56e23f |
else if (lvl_n)
|
|
Packit |
56e23f |
*node = lvl_n;
|
|
Packit |
56e23f |
else
|
|
Packit |
56e23f |
*node = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* drop the next level(s) */
|
|
Packit |
56e23f |
cnt += _db_tree_put(&nxt_t);
|
|
Packit |
56e23f |
cnt += _db_tree_put(&nxt_f);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* cleanup and accounting */
|
|
Packit |
56e23f |
free(n);
|
|
Packit |
56e23f |
cnt++;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return cnt;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Remove a node from an argument chain tree
|
|
Packit |
56e23f |
* @param tree the pointer to the tree
|
|
Packit |
56e23f |
* @param node the node to remove
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function searches the tree looking for the node and removes it as well
|
|
Packit |
56e23f |
* as any sub-trees beneath it. Returns the number of nodes freed.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static unsigned int _db_tree_remove(struct db_arg_chain_tree **tree,
|
|
Packit |
56e23f |
struct db_arg_chain_tree *node)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int cnt = 0;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *c_iter;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (tree == NULL || *tree == NULL || node == NULL)
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter = *tree;
|
|
Packit |
56e23f |
while (c_iter->lvl_prv != NULL)
|
|
Packit |
56e23f |
c_iter = c_iter->lvl_prv;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
do {
|
|
Packit |
56e23f |
/* current node? */
|
|
Packit |
56e23f |
if (c_iter == node)
|
|
Packit |
56e23f |
goto remove;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* check the sub-trees */
|
|
Packit |
56e23f |
cnt += _db_tree_remove(&(c_iter->nxt_t), node);
|
|
Packit |
56e23f |
cnt += _db_tree_remove(&(c_iter->nxt_f), node);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* check for empty/zombie nodes */
|
|
Packit |
56e23f |
if (_db_chain_zombie(c_iter))
|
|
Packit |
56e23f |
goto remove;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* next node on this level */
|
|
Packit |
56e23f |
c_iter = c_iter->lvl_nxt;
|
|
Packit |
56e23f |
} while (c_iter != NULL && cnt == 0);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return cnt;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
remove:
|
|
Packit |
56e23f |
/* reset the tree pointer if needed */
|
|
Packit |
56e23f |
if (c_iter == *tree) {
|
|
Packit |
56e23f |
if (c_iter->lvl_prv != NULL)
|
|
Packit |
56e23f |
*tree = c_iter->lvl_prv;
|
|
Packit |
56e23f |
else
|
|
Packit |
56e23f |
*tree = c_iter->lvl_nxt;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* remove the node from the current level */
|
|
Packit |
56e23f |
if (c_iter->lvl_prv)
|
|
Packit |
56e23f |
c_iter->lvl_prv->lvl_nxt = c_iter->lvl_nxt;
|
|
Packit |
56e23f |
if (c_iter->lvl_nxt)
|
|
Packit |
56e23f |
c_iter->lvl_nxt->lvl_prv = c_iter->lvl_prv;
|
|
Packit |
56e23f |
c_iter->lvl_prv = NULL;
|
|
Packit |
56e23f |
c_iter->lvl_nxt = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free the node and any sub-trees */
|
|
Packit |
56e23f |
cnt += _db_node_put(&c_iter);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return cnt;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Traverse a tree checking the action values
|
|
Packit |
56e23f |
* @param tree the pointer to the tree
|
|
Packit |
56e23f |
* @param action the action
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Traverse the tree inspecting each action to see if it matches the given
|
|
Packit |
56e23f |
* action. Returns zero if all actions match the given action, negative values
|
|
Packit |
56e23f |
* on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static int _db_tree_act_check(struct db_arg_chain_tree *tree, uint32_t action)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *c_iter;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (tree == NULL)
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter = tree;
|
|
Packit |
56e23f |
while (c_iter->lvl_prv != NULL)
|
|
Packit |
56e23f |
c_iter = c_iter->lvl_prv;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
do {
|
|
Packit |
56e23f |
if (c_iter->act_t_flg && c_iter->act_t != action)
|
|
Packit |
56e23f |
return -EEXIST;
|
|
Packit |
56e23f |
if (c_iter->act_f_flg && c_iter->act_f != action)
|
|
Packit |
56e23f |
return -EEXIST;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
rc = _db_tree_act_check(c_iter->nxt_t, action);
|
|
Packit |
56e23f |
if (rc < 0)
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
rc = _db_tree_act_check(c_iter->nxt_f, action);
|
|
Packit |
56e23f |
if (rc < 0)
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter = c_iter->lvl_nxt;
|
|
Packit |
56e23f |
} while (c_iter != NULL);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Checks for a sub-tree match in an existing tree and prunes the tree
|
|
Packit |
56e23f |
* @param existing pointer to the existing tree
|
|
Packit |
56e23f |
* @param new pointer to the new tree
|
|
Packit |
56e23f |
* @param state pointer to a state structure
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function searches the existing tree trying to prune it based on the
|
|
Packit |
56e23f |
* new tree. Returns the number of nodes removed from the tree on success,
|
|
Packit |
56e23f |
* zero if no changes were made.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static int _db_tree_prune(struct db_arg_chain_tree **existing,
|
|
Packit |
56e23f |
struct db_arg_chain_tree *new,
|
|
Packit |
56e23f |
struct db_iter_state *state)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int cnt = 0;
|
|
Packit |
56e23f |
struct db_iter_state state_nxt;
|
|
Packit |
56e23f |
struct db_iter_state state_new = *state;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *x_iter_next;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *x_iter = *existing;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *n_iter = new;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* check if either tree is finished */
|
|
Packit |
56e23f |
if (n_iter == NULL || x_iter == NULL)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* bail out if we have a broken match */
|
|
Packit |
56e23f |
if ((state->flags & _DB_IST_M_MATCHSET) == _DB_IST_MATCH_ONCE)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* get to the start of the existing level */
|
|
Packit |
56e23f |
while (x_iter->lvl_prv)
|
|
Packit |
56e23f |
x_iter = x_iter->lvl_prv;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* NOTE: a few comments on the code below ...
|
|
Packit |
56e23f |
* 1) we need to take a reference before we go down a level in case
|
|
Packit |
56e23f |
* we end up dropping the sub-tree (see the _db_node_get() calls)
|
|
Packit |
56e23f |
* 2) since the new tree really only has one branch, we can only ever
|
|
Packit |
56e23f |
* match on one branch in the existing tree, if we "hit" then we
|
|
Packit |
56e23f |
* can bail on the other branches */
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
do {
|
|
Packit |
56e23f |
/* store this now in case we remove x_iter */
|
|
Packit |
56e23f |
x_iter_next = x_iter->lvl_nxt;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* compare the two nodes */
|
|
Packit |
56e23f |
if (_db_chain_eq(x_iter, n_iter)) {
|
|
Packit |
56e23f |
/* we have a match */
|
|
Packit |
56e23f |
state_new.flags |= _DB_IST_M_MATCHSET;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* check if either tree is finished */
|
|
Packit |
56e23f |
if (_db_chain_leaf(n_iter))
|
|
Packit |
56e23f |
state_new.flags |= _DB_IST_N_FINISHED;
|
|
Packit |
56e23f |
if (_db_chain_leaf(x_iter))
|
|
Packit |
56e23f |
state_new.flags |= _DB_IST_X_FINISHED;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* don't remove nodes if we have more actions/levels */
|
|
Packit |
56e23f |
if ((x_iter->act_t_flg || x_iter->nxt_t) &&
|
|
Packit |
56e23f |
!(n_iter->act_t_flg || n_iter->nxt_t))
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
if ((x_iter->act_f_flg || x_iter->nxt_f) &&
|
|
Packit |
56e23f |
!(n_iter->act_f_flg || n_iter->nxt_f))
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* if finished, compare actions */
|
|
Packit |
56e23f |
if ((state_new.flags & _DB_IST_N_FINISHED) &&
|
|
Packit |
56e23f |
(state_new.flags & _DB_IST_X_FINISHED)) {
|
|
Packit |
56e23f |
if (n_iter->act_t_flg != x_iter->act_t_flg)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
if (n_iter->act_t != x_iter->act_t)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (n_iter->act_f_flg != x_iter->act_f_flg)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
if (n_iter->act_f != x_iter->act_f)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* check next level */
|
|
Packit |
56e23f |
if (n_iter->nxt_t) {
|
|
Packit |
56e23f |
_db_node_get(x_iter);
|
|
Packit |
56e23f |
state_nxt = *state;
|
|
Packit |
56e23f |
state_nxt.flags |= _DB_IST_M_MATCHSET;
|
|
Packit |
56e23f |
cnt += _db_tree_prune(&x_iter->nxt_t,
|
|
Packit |
56e23f |
n_iter->nxt_t,
|
|
Packit |
56e23f |
&state_nxt);
|
|
Packit |
56e23f |
cnt += _db_node_put(&x_iter);
|
|
Packit |
56e23f |
if (state_nxt.flags & _DB_IST_MATCH) {
|
|
Packit |
56e23f |
state_new.flags |= state_nxt.flags;
|
|
Packit |
56e23f |
/* don't return yet, we need to check
|
|
Packit |
56e23f |
* the current node */
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (x_iter == NULL)
|
|
Packit |
56e23f |
goto prune_next_node;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (n_iter->nxt_f) {
|
|
Packit |
56e23f |
_db_node_get(x_iter);
|
|
Packit |
56e23f |
state_nxt = *state;
|
|
Packit |
56e23f |
state_nxt.flags |= _DB_IST_M_MATCHSET;
|
|
Packit |
56e23f |
cnt += _db_tree_prune(&x_iter->nxt_f,
|
|
Packit |
56e23f |
n_iter->nxt_f,
|
|
Packit |
56e23f |
&state_nxt);
|
|
Packit |
56e23f |
cnt += _db_node_put(&x_iter);
|
|
Packit |
56e23f |
if (state_nxt.flags & _DB_IST_MATCH) {
|
|
Packit |
56e23f |
state_new.flags |= state_nxt.flags;
|
|
Packit |
56e23f |
/* don't return yet, we need to check
|
|
Packit |
56e23f |
* the current node */
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (x_iter == NULL)
|
|
Packit |
56e23f |
goto prune_next_node;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* remove the node? */
|
|
Packit |
56e23f |
if (!_db_tree_act_check(x_iter, state_new.action) &&
|
|
Packit |
56e23f |
(state_new.flags & _DB_IST_MATCH) &&
|
|
Packit |
56e23f |
(state_new.flags & _DB_IST_N_FINISHED) &&
|
|
Packit |
56e23f |
(state_new.flags & _DB_IST_X_PREFIX)) {
|
|
Packit |
56e23f |
/* yes - the new tree is "shorter" */
|
|
Packit |
56e23f |
cnt += _db_tree_remove(&state->sx->chains,
|
|
Packit |
56e23f |
x_iter);
|
|
Packit |
56e23f |
if (state->sx->chains == NULL)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
} else if (!_db_tree_act_check(x_iter, state_new.action)
|
|
Packit |
56e23f |
&& (state_new.flags & _DB_IST_MATCH) &&
|
|
Packit |
56e23f |
(state_new.flags & _DB_IST_X_FINISHED) &&
|
|
Packit |
56e23f |
(state_new.flags & _DB_IST_N_PREFIX)) {
|
|
Packit |
56e23f |
/* no - the new tree is "longer" */
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else if (_db_chain_lt(x_iter, n_iter)) {
|
|
Packit |
56e23f |
/* bail if we have a prefix on the new tree */
|
|
Packit |
56e23f |
if (state->flags & _DB_IST_N_PREFIX)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* check the next level in the existing tree */
|
|
Packit |
56e23f |
if (x_iter->nxt_t) {
|
|
Packit |
56e23f |
_db_node_get(x_iter);
|
|
Packit |
56e23f |
state_nxt = *state;
|
|
Packit |
56e23f |
state_nxt.flags &= ~_DB_IST_MATCH;
|
|
Packit |
56e23f |
state_nxt.flags |= _DB_IST_X_PREFIX;
|
|
Packit |
56e23f |
cnt += _db_tree_prune(&x_iter->nxt_t, n_iter,
|
|
Packit |
56e23f |
&state_nxt);
|
|
Packit |
56e23f |
cnt += _db_node_put(&x_iter);
|
|
Packit |
56e23f |
if (state_nxt.flags & _DB_IST_MATCH) {
|
|
Packit |
56e23f |
state_new.flags |= state_nxt.flags;
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (x_iter == NULL)
|
|
Packit |
56e23f |
goto prune_next_node;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (x_iter->nxt_f) {
|
|
Packit |
56e23f |
_db_node_get(x_iter);
|
|
Packit |
56e23f |
state_nxt = *state;
|
|
Packit |
56e23f |
state_nxt.flags &= ~_DB_IST_MATCH;
|
|
Packit |
56e23f |
state_nxt.flags |= _DB_IST_X_PREFIX;
|
|
Packit |
56e23f |
cnt += _db_tree_prune(&x_iter->nxt_f, n_iter,
|
|
Packit |
56e23f |
&state_nxt);
|
|
Packit |
56e23f |
cnt += _db_node_put(&x_iter);
|
|
Packit |
56e23f |
if (state_nxt.flags & _DB_IST_MATCH) {
|
|
Packit |
56e23f |
state_new.flags |= state_nxt.flags;
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (x_iter == NULL)
|
|
Packit |
56e23f |
goto prune_next_node;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
/* bail if we have a prefix on the existing tree */
|
|
Packit |
56e23f |
if (state->flags & _DB_IST_X_PREFIX)
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* check the next level in the new tree */
|
|
Packit |
56e23f |
if (n_iter->nxt_t) {
|
|
Packit |
56e23f |
_db_node_get(x_iter);
|
|
Packit |
56e23f |
state_nxt = *state;
|
|
Packit |
56e23f |
state_nxt.flags &= ~_DB_IST_MATCH;
|
|
Packit |
56e23f |
state_nxt.flags |= _DB_IST_N_PREFIX;
|
|
Packit |
56e23f |
cnt += _db_tree_prune(&x_iter, n_iter->nxt_t,
|
|
Packit |
56e23f |
&state_nxt);
|
|
Packit |
56e23f |
cnt += _db_node_put(&x_iter);
|
|
Packit |
56e23f |
if (state_nxt.flags & _DB_IST_MATCH) {
|
|
Packit |
56e23f |
state_new.flags |= state_nxt.flags;
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (x_iter == NULL)
|
|
Packit |
56e23f |
goto prune_next_node;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (n_iter->nxt_f) {
|
|
Packit |
56e23f |
_db_node_get(x_iter);
|
|
Packit |
56e23f |
state_nxt = *state;
|
|
Packit |
56e23f |
state_nxt.flags &= ~_DB_IST_MATCH;
|
|
Packit |
56e23f |
state_nxt.flags |= _DB_IST_N_PREFIX;
|
|
Packit |
56e23f |
cnt += _db_tree_prune(&x_iter, n_iter->nxt_f,
|
|
Packit |
56e23f |
&state_nxt);
|
|
Packit |
56e23f |
cnt += _db_node_put(&x_iter);
|
|
Packit |
56e23f |
if (state_nxt.flags & _DB_IST_MATCH) {
|
|
Packit |
56e23f |
state_new.flags |= state_nxt.flags;
|
|
Packit |
56e23f |
goto prune_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (x_iter == NULL)
|
|
Packit |
56e23f |
goto prune_next_node;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
prune_next_node:
|
|
Packit |
56e23f |
/* check next node on this level */
|
|
Packit |
56e23f |
x_iter = x_iter_next;
|
|
Packit |
56e23f |
} while (x_iter);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
// if we are falling through, we clearly didn't match on anything
|
|
Packit |
56e23f |
state_new.flags &= ~_DB_IST_MATCH;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
prune_return:
|
|
Packit |
56e23f |
/* no more nodes on this level, return to the level above */
|
|
Packit |
56e23f |
if (state_new.flags & _DB_IST_MATCH)
|
|
Packit |
56e23f |
state->flags |= state_new.flags;
|
|
Packit |
56e23f |
else
|
|
Packit |
56e23f |
state->flags &= ~_DB_IST_MATCH;
|
|
Packit |
56e23f |
return cnt;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Add a new tree into an existing tree
|
|
Packit |
56e23f |
* @param existing pointer to the existing tree
|
|
Packit |
56e23f |
* @param new pointer to the new tree
|
|
Packit |
56e23f |
* @param state pointer to a state structure
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function adds the new tree into the existing tree, fetching additional
|
|
Packit |
56e23f |
* references as necessary. Returns zero on success, negative values on
|
|
Packit |
56e23f |
* failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static int _db_tree_add(struct db_arg_chain_tree **existing,
|
|
Packit |
56e23f |
struct db_arg_chain_tree *new,
|
|
Packit |
56e23f |
struct db_iter_state *state)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *x_iter = *existing;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *n_iter = new;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
do {
|
|
Packit |
56e23f |
if (_db_chain_eq(x_iter, n_iter)) {
|
|
Packit |
56e23f |
if (n_iter->act_t_flg) {
|
|
Packit |
56e23f |
if (!x_iter->act_t_flg) {
|
|
Packit |
56e23f |
/* new node has a true action */
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* do the actions match? */
|
|
Packit |
56e23f |
rc = _db_tree_act_check(x_iter->nxt_t,
|
|
Packit |
56e23f |
n_iter->act_t);
|
|
Packit |
56e23f |
if (rc != 0)
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* update with the new action */
|
|
Packit |
56e23f |
rc = _db_node_put(&x_iter->nxt_t);
|
|
Packit |
56e23f |
x_iter->nxt_t = NULL;
|
|
Packit |
56e23f |
x_iter->act_t = n_iter->act_t;
|
|
Packit |
56e23f |
x_iter->act_t_flg = true;
|
|
Packit |
56e23f |
state->sx->node_cnt -= rc;
|
|
Packit |
56e23f |
} else if (n_iter->act_t != x_iter->act_t) {
|
|
Packit |
56e23f |
/* if we are dealing with a 64-bit
|
|
Packit |
56e23f |
* comparison, we need to adjust our
|
|
Packit |
56e23f |
* action based on the full 64-bit
|
|
Packit |
56e23f |
* value to ensure we handle GT/GE
|
|
Packit |
56e23f |
* comparisons correctly */
|
|
Packit |
56e23f |
if (n_iter->arg_h_flg &&
|
|
Packit |
56e23f |
(n_iter->datum_full >
|
|
Packit |
56e23f |
x_iter->datum_full))
|
|
Packit |
56e23f |
x_iter->act_t = n_iter->act_t;
|
|
Packit |
56e23f |
if (_db_chain_leaf(x_iter) ||
|
|
Packit |
56e23f |
_db_chain_leaf(n_iter))
|
|
Packit |
56e23f |
return -EEXIST;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (n_iter->act_f_flg) {
|
|
Packit |
56e23f |
if (!x_iter->act_f_flg) {
|
|
Packit |
56e23f |
/* new node has a false action */
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* do the actions match? */
|
|
Packit |
56e23f |
rc = _db_tree_act_check(x_iter->nxt_f,
|
|
Packit |
56e23f |
n_iter->act_f);
|
|
Packit |
56e23f |
if (rc != 0)
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* update with the new action */
|
|
Packit |
56e23f |
rc = _db_node_put(&x_iter->nxt_f);
|
|
Packit |
56e23f |
x_iter->nxt_f = NULL;
|
|
Packit |
56e23f |
x_iter->act_f = n_iter->act_f;
|
|
Packit |
56e23f |
x_iter->act_f_flg = true;
|
|
Packit |
56e23f |
state->sx->node_cnt -= rc;
|
|
Packit |
56e23f |
} else if (n_iter->act_f != x_iter->act_f) {
|
|
Packit |
56e23f |
/* if we are dealing with a 64-bit
|
|
Packit |
56e23f |
* comparison, we need to adjust our
|
|
Packit |
56e23f |
* action based on the full 64-bit
|
|
Packit |
56e23f |
* value to ensure we handle LT/LE
|
|
Packit |
56e23f |
* comparisons correctly */
|
|
Packit |
56e23f |
if (n_iter->arg_h_flg &&
|
|
Packit |
56e23f |
(n_iter->datum_full <
|
|
Packit |
56e23f |
x_iter->datum_full))
|
|
Packit |
56e23f |
x_iter->act_t = n_iter->act_t;
|
|
Packit |
56e23f |
if (_db_chain_leaf(x_iter) ||
|
|
Packit |
56e23f |
_db_chain_leaf(n_iter))
|
|
Packit |
56e23f |
return -EEXIST;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (n_iter->nxt_t) {
|
|
Packit |
56e23f |
if (x_iter->nxt_t) {
|
|
Packit |
56e23f |
/* compare the next level */
|
|
Packit |
56e23f |
rc = _db_tree_add(&x_iter->nxt_t,
|
|
Packit |
56e23f |
n_iter->nxt_t,
|
|
Packit |
56e23f |
state);
|
|
Packit |
56e23f |
if (rc != 0)
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
} else if (!x_iter->act_t_flg) {
|
|
Packit |
56e23f |
/* add a new sub-tree */
|
|
Packit |
56e23f |
x_iter->nxt_t = _db_node_get(n_iter->nxt_t);
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
/* done - existing tree is "shorter" */
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (n_iter->nxt_f) {
|
|
Packit |
56e23f |
if (x_iter->nxt_f) {
|
|
Packit |
56e23f |
/* compare the next level */
|
|
Packit |
56e23f |
rc = _db_tree_add(&x_iter->nxt_f,
|
|
Packit |
56e23f |
n_iter->nxt_f,
|
|
Packit |
56e23f |
state);
|
|
Packit |
56e23f |
if (rc != 0)
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
} else if (!x_iter->act_f_flg) {
|
|
Packit |
56e23f |
/* add a new sub-tree */
|
|
Packit |
56e23f |
x_iter->nxt_f = _db_node_get(n_iter->nxt_f);
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
/* done - existing tree is "shorter" */
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
} else if (!_db_chain_lt(x_iter, n_iter)) {
|
|
Packit |
56e23f |
/* try to move along the current level */
|
|
Packit |
56e23f |
if (x_iter->lvl_nxt == NULL) {
|
|
Packit |
56e23f |
/* add to the end of this level */
|
|
Packit |
56e23f |
n_iter->lvl_prv = _db_node_get(x_iter);
|
|
Packit |
56e23f |
x_iter->lvl_nxt = _db_node_get(n_iter);
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
/* next */
|
|
Packit |
56e23f |
x_iter = x_iter->lvl_nxt;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
/* add before the existing node on this level*/
|
|
Packit |
56e23f |
if (x_iter->lvl_prv != NULL) {
|
|
Packit |
56e23f |
x_iter->lvl_prv->lvl_nxt = _db_node_get(n_iter);
|
|
Packit |
56e23f |
n_iter->lvl_prv = x_iter->lvl_prv;
|
|
Packit |
56e23f |
x_iter->lvl_prv = _db_node_get(n_iter);
|
|
Packit |
56e23f |
n_iter->lvl_nxt = x_iter;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
x_iter->lvl_prv = _db_node_get(n_iter);
|
|
Packit |
56e23f |
n_iter->lvl_nxt = _db_node_get(x_iter);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (*existing == x_iter) {
|
|
Packit |
56e23f |
*existing = _db_node_get(n_iter);
|
|
Packit |
56e23f |
_db_node_put(&x_iter);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} while (x_iter);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Free and reset the seccomp filter DB
|
|
Packit |
56e23f |
* @param db the seccomp filter DB
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function frees any existing filters and resets the filter DB to a
|
|
Packit |
56e23f |
* default state; only the DB architecture is preserved.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static void _db_reset(struct db_filter *db)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
struct db_sys_list *s_iter;
|
|
Packit |
56e23f |
struct db_api_rule_list *r_iter;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (db == NULL)
|
|
Packit |
56e23f |
return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free any filters */
|
|
Packit |
56e23f |
if (db->syscalls != NULL) {
|
|
Packit |
56e23f |
s_iter = db->syscalls;
|
|
Packit |
56e23f |
while (s_iter != NULL) {
|
|
Packit |
56e23f |
db->syscalls = s_iter->next;
|
|
Packit |
56e23f |
_db_tree_put(&s_iter->chains);
|
|
Packit |
56e23f |
free(s_iter);
|
|
Packit |
56e23f |
s_iter = db->syscalls;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
db->syscalls = NULL;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free any rules */
|
|
Packit |
56e23f |
if (db->rules != NULL) {
|
|
Packit |
56e23f |
/* split the loop first then loop and free */
|
|
Packit |
56e23f |
db->rules->prev->next = NULL;
|
|
Packit |
56e23f |
r_iter = db->rules;
|
|
Packit |
56e23f |
while (r_iter != NULL) {
|
|
Packit |
56e23f |
db->rules = r_iter->next;
|
|
Packit |
56e23f |
free(r_iter);
|
|
Packit |
56e23f |
r_iter = db->rules;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
db->rules = NULL;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Intitalize a seccomp filter DB
|
|
Packit |
56e23f |
* @param arch the architecture definition
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function initializes a seccomp filter DB and readies it for use.
|
|
Packit |
56e23f |
* Returns a pointer to the DB on success, NULL on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static struct db_filter *_db_init(const struct arch_def *arch)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
struct db_filter *db;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
db = zmalloc(sizeof(*db));
|
|
Packit |
56e23f |
if (db == NULL)
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* set the arch and reset the DB to a known state */
|
|
Packit |
56e23f |
db->arch = arch;
|
|
Packit |
56e23f |
_db_reset(db);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return db;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Destroy a seccomp filter DB
|
|
Packit |
56e23f |
* @param db the seccomp filter DB
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function destroys a seccomp filter DB. After calling this function,
|
|
Packit |
56e23f |
* the filter should no longer be referenced.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static void _db_release(struct db_filter *db)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
if (db == NULL)
|
|
Packit |
56e23f |
return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free and reset the DB */
|
|
Packit |
56e23f |
_db_reset(db);
|
|
Packit |
56e23f |
free(db);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Destroy a seccomp filter snapshot
|
|
Packit |
56e23f |
* @param snap the seccomp filter snapshot
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function destroys a seccomp filter snapshot. After calling this
|
|
Packit |
56e23f |
* function, the snapshot should no longer be referenced.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static void _db_snap_release(struct db_filter_snap *snap)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (snap->filter_cnt > 0) {
|
|
Packit |
56e23f |
for (iter = 0; iter < snap->filter_cnt; iter++) {
|
|
Packit |
56e23f |
if (snap->filters[iter])
|
|
Packit |
56e23f |
_db_release(snap->filters[iter]);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
free(snap->filters);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
free(snap);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Update the user specified portion of the syscall priority
|
|
Packit |
56e23f |
* @param db the seccomp filter db
|
|
Packit |
56e23f |
* @param syscall the syscall number
|
|
Packit |
56e23f |
* @param priority the syscall priority
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function sets, or updates, the syscall priority; the highest priority
|
|
Packit |
56e23f |
* value between the existing and specified value becomes the new syscall
|
|
Packit |
56e23f |
* priority. If the syscall entry does not already exist, a new phantom
|
|
Packit |
56e23f |
* syscall entry is created as a placeholder. Returns zero on success,
|
|
Packit |
56e23f |
* negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static int _db_syscall_priority(struct db_filter *db,
|
|
Packit |
56e23f |
int syscall, uint8_t priority)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int sys_pri = _DB_PRI_USER(priority);
|
|
Packit |
56e23f |
struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
assert(db != NULL);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
s_iter = db->syscalls;
|
|
Packit |
56e23f |
while (s_iter != NULL && s_iter->num < syscall) {
|
|
Packit |
56e23f |
s_prev = s_iter;
|
|
Packit |
56e23f |
s_iter = s_iter->next;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* matched an existing syscall entry */
|
|
Packit |
56e23f |
if (s_iter != NULL && s_iter->num == syscall) {
|
|
Packit |
56e23f |
if (sys_pri > (s_iter->priority & _DB_PRI_MASK_USER)) {
|
|
Packit |
56e23f |
s_iter->priority &= (~_DB_PRI_MASK_USER);
|
|
Packit |
56e23f |
s_iter->priority |= sys_pri;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* no existing syscall entry - create a phantom entry */
|
|
Packit |
56e23f |
s_new = zmalloc(sizeof(*s_new));
|
|
Packit |
56e23f |
if (s_new == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
s_new->num = syscall;
|
|
Packit |
56e23f |
s_new->priority = sys_pri;
|
|
Packit |
56e23f |
s_new->valid = false;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* add it before s_iter */
|
|
Packit |
56e23f |
if (s_prev != NULL) {
|
|
Packit |
56e23f |
s_new->next = s_prev->next;
|
|
Packit |
56e23f |
s_prev->next = s_new;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
s_new->next = db->syscalls;
|
|
Packit |
56e23f |
db->syscalls = s_new;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Create a new rule
|
|
Packit |
56e23f |
* @param strict the strict value
|
|
Packit |
56e23f |
* @param action the rule's action
|
|
Packit |
56e23f |
* @param syscall the syscall number
|
|
Packit |
56e23f |
* @param chain the syscall argument filter
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function creates a new rule structure based on the given arguments.
|
|
Packit |
56e23f |
* Returns a pointer to the new rule on success, NULL on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static struct db_api_rule_list *_db_rule_new(bool strict,
|
|
Packit |
56e23f |
uint32_t action, int syscall,
|
|
Packit |
56e23f |
struct db_api_arg *chain)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
struct db_api_rule_list *rule;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
rule = zmalloc(sizeof(*rule));
|
|
Packit |
56e23f |
if (rule == NULL)
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
rule->action = action;
|
|
Packit |
56e23f |
rule->syscall = syscall;
|
|
Packit |
56e23f |
rule->strict = strict;
|
|
Packit |
56e23f |
memcpy(rule->args, chain, sizeof(*chain) * ARG_COUNT_MAX);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return rule;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Duplicate an existing filter rule
|
|
Packit |
56e23f |
* @param src the rule to duplicate
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function makes an exact copy of the given rule, but does not add it
|
|
Packit |
56e23f |
* to any lists. Returns a pointer to the new rule on success, NULL on
|
|
Packit |
56e23f |
* failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
struct db_api_rule_list *db_rule_dup(const struct db_api_rule_list *src)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
struct db_api_rule_list *dest;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
dest = malloc(sizeof(*dest));
|
|
Packit |
56e23f |
if (dest == NULL)
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
memcpy(dest, src, sizeof(*dest));
|
|
Packit |
56e23f |
dest->prev = NULL;
|
|
Packit |
56e23f |
dest->next = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return dest;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Free and reset the seccomp filter collection
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
* @param def_action the default filter action
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function frees any existing filter DBs and resets the collection to a
|
|
Packit |
56e23f |
* default state. In the case of failure the filter collection may be in an
|
|
Packit |
56e23f |
* unknown state and should be released. Returns zero on success, negative
|
|
Packit |
56e23f |
* values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_reset(struct db_filter_col *col, uint32_t def_action)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
struct db_filter *db;
|
|
Packit |
56e23f |
struct db_filter_snap *snap;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (col == NULL)
|
|
Packit |
56e23f |
return -EINVAL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free any filters */
|
|
Packit |
56e23f |
for (iter = 0; iter < col->filter_cnt; iter++)
|
|
Packit |
56e23f |
_db_release(col->filters[iter]);
|
|
Packit |
56e23f |
col->filter_cnt = 0;
|
|
Packit |
56e23f |
if (col->filters)
|
|
Packit |
56e23f |
free(col->filters);
|
|
Packit |
56e23f |
col->filters = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* set the endianess to undefined */
|
|
Packit |
56e23f |
col->endian = 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* set the default attribute values */
|
|
Packit |
56e23f |
col->attr.act_default = def_action;
|
|
Packit |
56e23f |
col->attr.act_badarch = SCMP_ACT_KILL;
|
|
Packit |
56e23f |
col->attr.nnp_enable = 1;
|
|
Packit |
56e23f |
col->attr.tsync_enable = 0;
|
|
Packit |
56e23f |
col->attr.api_tskip = 0;
|
|
Packit |
56e23f |
col->attr.log_enable = 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* set the state */
|
|
Packit |
56e23f |
col->state = _DB_STA_VALID;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* reset the initial db */
|
|
Packit |
56e23f |
db = _db_init(arch_def_native);
|
|
Packit |
56e23f |
if (db == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
if (db_col_db_add(col, db) < 0) {
|
|
Packit |
56e23f |
_db_release(db);
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* reset the transactions */
|
|
Packit |
56e23f |
while (col->snapshots) {
|
|
Packit |
56e23f |
snap = col->snapshots;
|
|
Packit |
56e23f |
col->snapshots = snap->next;
|
|
Packit |
56e23f |
for (iter = 0; iter < snap->filter_cnt; iter++)
|
|
Packit |
56e23f |
_db_release(snap->filters[iter]);
|
|
Packit |
56e23f |
free(snap->filters);
|
|
Packit |
56e23f |
free(snap);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Intitalize a seccomp filter collection
|
|
Packit |
56e23f |
* @param def_action the default filter action
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function initializes a seccomp filter collection and readies it for
|
|
Packit |
56e23f |
* use. Returns a pointer to the collection on success, NULL on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
struct db_filter_col *db_col_init(uint32_t def_action)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
struct db_filter_col *col;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
col = zmalloc(sizeof(*col));
|
|
Packit |
56e23f |
if (col == NULL)
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* reset the DB to a known state */
|
|
Packit |
56e23f |
if (db_col_reset(col, def_action) < 0)
|
|
Packit |
56e23f |
goto init_failure;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return col;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
init_failure:
|
|
Packit |
56e23f |
db_col_release(col);
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Destroy a seccomp filter collection
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function destroys a seccomp filter collection. After calling this
|
|
Packit |
56e23f |
* function, the filter should no longer be referenced.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
void db_col_release(struct db_filter_col *col)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (col == NULL)
|
|
Packit |
56e23f |
return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* set the state, just in case */
|
|
Packit |
56e23f |
col->state = _DB_STA_FREED;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free any filters */
|
|
Packit |
56e23f |
for (iter = 0; iter < col->filter_cnt; iter++)
|
|
Packit |
56e23f |
_db_release(col->filters[iter]);
|
|
Packit |
56e23f |
col->filter_cnt = 0;
|
|
Packit |
56e23f |
if (col->filters)
|
|
Packit |
56e23f |
free(col->filters);
|
|
Packit |
56e23f |
col->filters = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free the collection */
|
|
Packit |
56e23f |
free(col);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Validate the seccomp action
|
|
Packit |
56e23f |
* @param action the seccomp action
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Verify that the given action is a valid seccomp action; return zero if
|
|
Packit |
56e23f |
* valid, -EINVAL if invalid.
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_action_valid(uint32_t action)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
if (sys_chk_seccomp_action(action) == 1)
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
return -EINVAL;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Validate a filter collection
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function validates a seccomp filter collection. Returns zero if the
|
|
Packit |
56e23f |
* collection is valid, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_valid(struct db_filter_col *col)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
if (col != NULL && col->state == _DB_STA_VALID && col->filter_cnt > 0)
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
return -EINVAL;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Merge two filter collections
|
|
Packit |
56e23f |
* @param col_dst the destination filter collection
|
|
Packit |
56e23f |
* @param col_src the source filter collection
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function merges two filter collections into the given destination
|
|
Packit |
56e23f |
* collection. The source filter collection is no longer valid if the function
|
|
Packit |
56e23f |
* returns successfully. Returns zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_merge(struct db_filter_col *col_dst, struct db_filter_col *col_src)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter_a, iter_b;
|
|
Packit |
56e23f |
struct db_filter **dbs;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* verify that the endianess is a match */
|
|
Packit |
56e23f |
if (col_dst->endian != col_src->endian)
|
|
Packit |
56e23f |
return -EDOM;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* make sure we don't have any arch/filter collisions */
|
|
Packit |
56e23f |
for (iter_a = 0; iter_a < col_dst->filter_cnt; iter_a++) {
|
|
Packit |
56e23f |
for (iter_b = 0; iter_b < col_src->filter_cnt; iter_b++) {
|
|
Packit |
56e23f |
if (col_dst->filters[iter_a]->arch->token ==
|
|
Packit |
56e23f |
col_src->filters[iter_b]->arch->token)
|
|
Packit |
56e23f |
return -EEXIST;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* expand the destination */
|
|
Packit |
56e23f |
dbs = realloc(col_dst->filters,
|
|
Packit |
56e23f |
sizeof(struct db_filter *) *
|
|
Packit |
56e23f |
(col_dst->filter_cnt + col_src->filter_cnt));
|
|
Packit |
56e23f |
if (dbs == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
col_dst->filters = dbs;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* transfer the architecture filters */
|
|
Packit |
56e23f |
for (iter_a = col_dst->filter_cnt, iter_b = 0;
|
|
Packit |
56e23f |
iter_b < col_src->filter_cnt; iter_a++, iter_b++) {
|
|
Packit |
56e23f |
col_dst->filters[iter_a] = col_src->filters[iter_b];
|
|
Packit |
56e23f |
col_dst->filter_cnt++;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free the source */
|
|
Packit |
56e23f |
col_src->filter_cnt = 0;
|
|
Packit |
56e23f |
db_col_release(col_src);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Check to see if an architecture filter exists in the filter collection
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
* @param arch_token the architecture token
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Iterate through the given filter collection checking to see if a filter
|
|
Packit |
56e23f |
* exists for the specified architecture. Returns -EEXIST if a filter is found,
|
|
Packit |
56e23f |
* zero if a matching filter does not exist.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_arch_exist(struct db_filter_col *col, uint32_t arch_token)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
for (iter = 0; iter < col->filter_cnt; iter++)
|
|
Packit |
56e23f |
if (col->filters[iter]->arch->token == arch_token)
|
|
Packit |
56e23f |
return -EEXIST;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Get a filter attribute
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
* @param attr the filter attribute
|
|
Packit |
56e23f |
* @param value the filter attribute value
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Get the requested filter attribute and provide it via @value. Returns zero
|
|
Packit |
56e23f |
* on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_attr_get(const struct db_filter_col *col,
|
|
Packit |
56e23f |
enum scmp_filter_attr attr, uint32_t *value)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc = 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
switch (attr) {
|
|
Packit |
56e23f |
case SCMP_FLTATR_ACT_DEFAULT:
|
|
Packit |
56e23f |
*value = col->attr.act_default;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_ACT_BADARCH:
|
|
Packit |
56e23f |
*value = col->attr.act_badarch;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_CTL_NNP:
|
|
Packit |
56e23f |
*value = col->attr.nnp_enable;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_CTL_TSYNC:
|
|
Packit |
56e23f |
*value = col->attr.tsync_enable;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_API_TSKIP:
|
|
Packit |
56e23f |
*value = col->attr.api_tskip;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_CTL_LOG:
|
|
Packit |
56e23f |
*value = col->attr.log_enable;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
rc = -EEXIST;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Set a filter attribute
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
* @param attr the filter attribute
|
|
Packit |
56e23f |
* @param value the filter attribute value
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Set the requested filter attribute with the given value. Returns zero on
|
|
Packit |
56e23f |
* success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_attr_set(struct db_filter_col *col,
|
|
Packit |
56e23f |
enum scmp_filter_attr attr, uint32_t value)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc = 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
switch (attr) {
|
|
Packit |
56e23f |
case SCMP_FLTATR_ACT_DEFAULT:
|
|
Packit |
56e23f |
/* read only */
|
|
Packit |
56e23f |
return -EACCES;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_ACT_BADARCH:
|
|
Packit |
56e23f |
if (db_action_valid(value) == 0)
|
|
Packit |
56e23f |
col->attr.act_badarch = value;
|
|
Packit |
56e23f |
else
|
|
Packit |
56e23f |
return -EINVAL;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_CTL_NNP:
|
|
Packit |
56e23f |
col->attr.nnp_enable = (value ? 1 : 0);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_CTL_TSYNC:
|
|
Packit |
56e23f |
rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC);
|
|
Packit |
56e23f |
if (rc == 1) {
|
|
Packit |
56e23f |
/* supported */
|
|
Packit |
56e23f |
rc = 0;
|
|
Packit |
56e23f |
col->attr.tsync_enable = (value ? 1 : 0);
|
|
Packit |
56e23f |
} else if (rc == 0)
|
|
Packit |
56e23f |
/* unsupported */
|
|
Packit |
56e23f |
rc = -EOPNOTSUPP;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_API_TSKIP:
|
|
Packit |
56e23f |
col->attr.api_tskip = (value ? 1 : 0);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_FLTATR_CTL_LOG:
|
|
Packit |
56e23f |
rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_LOG);
|
|
Packit |
56e23f |
if (rc == 1) {
|
|
Packit |
56e23f |
/* supported */
|
|
Packit |
56e23f |
rc = 0;
|
|
Packit |
56e23f |
col->attr.log_enable = (value ? 1 : 0);
|
|
Packit |
56e23f |
} else if (rc == 0) {
|
|
Packit |
56e23f |
/* unsupported */
|
|
Packit |
56e23f |
rc = -EOPNOTSUPP;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
rc = -EEXIST;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Add a new architecture filter to a filter collection
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
* @param arch the architecture
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function adds a new architecture filter DB to an existing seccomp
|
|
Packit |
56e23f |
* filter collection assuming there isn't a filter DB already present with the
|
|
Packit |
56e23f |
* same architecture. Returns zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_db_new(struct db_filter_col *col, const struct arch_def *arch)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc;
|
|
Packit |
56e23f |
struct db_filter *db;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
db = _db_init(arch);
|
|
Packit |
56e23f |
if (db == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
rc = db_col_db_add(col, db);
|
|
Packit |
56e23f |
if (rc < 0)
|
|
Packit |
56e23f |
_db_release(db);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Add a new filter DB to a filter collection
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
* @param db the seccomp filter DB
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function adds an existing seccomp filter DB to an existing seccomp
|
|
Packit |
56e23f |
* filter collection assuming there isn't a filter DB already present with the
|
|
Packit |
56e23f |
* same architecture. Returns zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_db_add(struct db_filter_col *col, struct db_filter *db)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
struct db_filter **dbs;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (col->endian != 0 && col->endian != db->arch->endian)
|
|
Packit |
56e23f |
return -EDOM;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (db_col_arch_exist(col, db->arch->token))
|
|
Packit |
56e23f |
return -EEXIST;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
dbs = realloc(col->filters,
|
|
Packit |
56e23f |
sizeof(struct db_filter *) * (col->filter_cnt + 1));
|
|
Packit |
56e23f |
if (dbs == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
col->filters = dbs;
|
|
Packit |
56e23f |
col->filter_cnt++;
|
|
Packit |
56e23f |
col->filters[col->filter_cnt - 1] = db;
|
|
Packit |
56e23f |
if (col->endian == 0)
|
|
Packit |
56e23f |
col->endian = db->arch->endian;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Remove a filter DB from a filter collection
|
|
Packit |
56e23f |
* @param col the seccomp filter collection
|
|
Packit |
56e23f |
* @param arch_token the architecture token
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function removes an existing seccomp filter DB from an existing seccomp
|
|
Packit |
56e23f |
* filter collection. Returns zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_db_remove(struct db_filter_col *col, uint32_t arch_token)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
unsigned int found;
|
|
Packit |
56e23f |
struct db_filter **dbs;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if ((col->filter_cnt <= 0) || (db_col_arch_exist(col, arch_token) == 0))
|
|
Packit |
56e23f |
return -EINVAL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
for (found = 0, iter = 0; iter < col->filter_cnt; iter++) {
|
|
Packit |
56e23f |
if (found)
|
|
Packit |
56e23f |
col->filters[iter - 1] = col->filters[iter];
|
|
Packit |
56e23f |
else if (col->filters[iter]->arch->token == arch_token) {
|
|
Packit |
56e23f |
_db_release(col->filters[iter]);
|
|
Packit |
56e23f |
found = 1;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
col->filters[--col->filter_cnt] = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (col->filter_cnt > 0) {
|
|
Packit |
56e23f |
/* NOTE: if we can't do the realloc it isn't fatal, we just
|
|
Packit |
56e23f |
* have some extra space allocated */
|
|
Packit |
56e23f |
dbs = realloc(col->filters,
|
|
Packit |
56e23f |
sizeof(struct db_filter *) * col->filter_cnt);
|
|
Packit |
56e23f |
if (dbs != NULL)
|
|
Packit |
56e23f |
col->filters = dbs;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
/* this was the last filter so free all the associated memory
|
|
Packit |
56e23f |
* and reset the endian token */
|
|
Packit |
56e23f |
free(col->filters);
|
|
Packit |
56e23f |
col->filters = NULL;
|
|
Packit |
56e23f |
col->endian = 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Test if the argument filter can be skipped because it's a tautology
|
|
Packit |
56e23f |
* @param arg argument filter
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* If this argument filter applied to the lower 32 bit can be skipped this
|
|
Packit |
56e23f |
* function returns false.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
if (arg->op == SCMP_CMP_MASKED_EQ && D64_LO(arg->mask) == 0)
|
|
Packit |
56e23f |
return false;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return true;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Test if the argument filter can be skipped because it's a tautology
|
|
Packit |
56e23f |
* @param arg argument filter
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* If this argument filter applied to the upper 32 bit can be skipped this
|
|
Packit |
56e23f |
* function returns false.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
if (arg->op == SCMP_CMP_MASKED_EQ && D64_HI(arg->mask) == 0)
|
|
Packit |
56e23f |
return false;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return true;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Fixup the node based on the op/mask
|
|
Packit |
56e23f |
* @param node the chain node
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* Ensure the datum is masked as well.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static void _db_node_mask_fixup(struct db_arg_chain_tree *node)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
node->datum &= node->mask;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Generate a new filter rule for a 64 bit system
|
|
Packit |
56e23f |
* @param arch the architecture definition
|
|
Packit |
56e23f |
* @param rule the new filter rule
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function generates a new syscall filter for a 64 bit system. Returns
|
|
Packit |
56e23f |
* zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
|
|
Packit |
56e23f |
const struct db_api_rule_list *rule)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
struct db_sys_list *s_new;
|
|
Packit |
56e23f |
const struct db_api_arg *chain = rule->args;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *c_iter[3] = { NULL, NULL, NULL };
|
|
Packit |
56e23f |
struct db_arg_chain_tree *c_prev[3] = { NULL, NULL, NULL };
|
|
Packit |
56e23f |
enum scmp_compare op_prev = _SCMP_CMP_MIN;
|
|
Packit |
56e23f |
unsigned int arg;
|
|
Packit |
56e23f |
scmp_datum_t mask;
|
|
Packit |
56e23f |
scmp_datum_t datum;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
s_new = zmalloc(sizeof(*s_new));
|
|
Packit |
56e23f |
if (s_new == NULL)
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
s_new->num = rule->syscall;
|
|
Packit |
56e23f |
s_new->valid = true;
|
|
Packit |
56e23f |
/* run through the argument chain */
|
|
Packit |
56e23f |
for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
|
|
Packit |
56e23f |
if (chain[iter].valid == 0)
|
|
Packit |
56e23f |
continue;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* TODO: handle the case were either hi or lo isn't needed */
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* skip generating instruction which are no-ops */
|
|
Packit |
56e23f |
if (!_db_arg_cmp_need_hi(&chain[iter]) &&
|
|
Packit |
56e23f |
!_db_arg_cmp_need_lo(&chain[iter]))
|
|
Packit |
56e23f |
continue;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter[0] = zmalloc(sizeof(*c_iter[0]));
|
|
Packit |
56e23f |
if (c_iter[0] == NULL)
|
|
Packit |
56e23f |
goto gen_64_failure;
|
|
Packit |
56e23f |
c_iter[1] = zmalloc(sizeof(*c_iter[1]));
|
|
Packit |
56e23f |
if (c_iter[1] == NULL) {
|
|
Packit |
56e23f |
free(c_iter[0]);
|
|
Packit |
56e23f |
goto gen_64_failure;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
c_iter[2] = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
arg = chain[iter].arg;
|
|
Packit |
56e23f |
mask = chain[iter].mask;
|
|
Packit |
56e23f |
datum = chain[iter].datum;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* NOTE: with the idea that a picture is worth a thousand
|
|
Packit |
56e23f |
* words, i'm presenting the following diagrams which
|
|
Packit |
56e23f |
* show how we should compare 64-bit syscall arguments
|
|
Packit |
56e23f |
* using 32-bit comparisons.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* in the diagrams below "A(x)" is the syscall argument
|
|
Packit |
56e23f |
* being evaluated and "R(x)" is the syscall argument
|
|
Packit |
56e23f |
* value specified in the libseccomp rule. the "ACCEPT"
|
|
Packit |
56e23f |
* verdict indicates a rule match and processing should
|
|
Packit |
56e23f |
* continue on to the rest of the rule, or the final rule
|
|
Packit |
56e23f |
* action should be triggered. the "REJECT" verdict
|
|
Packit |
56e23f |
* indicates that the rule does not match and processing
|
|
Packit |
56e23f |
* should continue to the next rule or the default
|
|
Packit |
56e23f |
* action.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* SCMP_CMP_GT:
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
* +--| Ah(x) > Rh(x) |------+
|
|
Packit |
56e23f |
* | +------------------+ |
|
|
Packit |
56e23f |
* FALSE TRUE A
|
|
Packit |
56e23f |
* | | C
|
|
Packit |
56e23f |
* +-----------+ +----> C
|
|
Packit |
56e23f |
* v +----> E
|
|
Packit |
56e23f |
* +------------------+ | P
|
|
Packit |
56e23f |
* +--| Ah(x) == Rh(x) |--+ | T
|
|
Packit |
56e23f |
* R | +------------------+ | |
|
|
Packit |
56e23f |
* E FALSE TRUE |
|
|
Packit |
56e23f |
* J <----+ | |
|
|
Packit |
56e23f |
* E <----+ +------------+ |
|
|
Packit |
56e23f |
* C FALSE v |
|
|
Packit |
56e23f |
* T | +------------------+ |
|
|
Packit |
56e23f |
* +--| Al(x) > Rl(x) |------+
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* SCMP_CMP_GE:
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
* +--| Ah(x) > Rh(x) |------+
|
|
Packit |
56e23f |
* | +------------------+ |
|
|
Packit |
56e23f |
* FALSE TRUE A
|
|
Packit |
56e23f |
* | | C
|
|
Packit |
56e23f |
* +-----------+ +----> C
|
|
Packit |
56e23f |
* v +----> E
|
|
Packit |
56e23f |
* +------------------+ | P
|
|
Packit |
56e23f |
* +--| Ah(x) == Rh(x) |--+ | T
|
|
Packit |
56e23f |
* R | +------------------+ | |
|
|
Packit |
56e23f |
* E FALSE TRUE |
|
|
Packit |
56e23f |
* J <----+ | |
|
|
Packit |
56e23f |
* E <----+ +------------+ |
|
|
Packit |
56e23f |
* C FALSE v |
|
|
Packit |
56e23f |
* T | +------------------+ |
|
|
Packit |
56e23f |
* +--| Al(x) >= Rl(x) |------+
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* SCMP_CMP_LT:
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
* +--| Ah(x) > Rh(x) |------+
|
|
Packit |
56e23f |
* | +------------------+ |
|
|
Packit |
56e23f |
* FALSE TRUE R
|
|
Packit |
56e23f |
* | | E
|
|
Packit |
56e23f |
* +-----------+ +----> J
|
|
Packit |
56e23f |
* v +----> E
|
|
Packit |
56e23f |
* +------------------+ | C
|
|
Packit |
56e23f |
* +--| Ah(x) == Rh(x) |--+ | T
|
|
Packit |
56e23f |
* A | +------------------+ | |
|
|
Packit |
56e23f |
* C FALSE TRUE |
|
|
Packit |
56e23f |
* C <----+ | |
|
|
Packit |
56e23f |
* E <----+ +------------+ |
|
|
Packit |
56e23f |
* P FALSE v |
|
|
Packit |
56e23f |
* T | +------------------+ |
|
|
Packit |
56e23f |
* +--| Al(x) >= Rl(x) |------+
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* SCMP_CMP_LE:
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
* +--| Ah(x) > Rh(x) |------+
|
|
Packit |
56e23f |
* | +------------------+ |
|
|
Packit |
56e23f |
* FALSE TRUE R
|
|
Packit |
56e23f |
* | | E
|
|
Packit |
56e23f |
* +-----------+ +----> J
|
|
Packit |
56e23f |
* v +----> E
|
|
Packit |
56e23f |
* +------------------+ | C
|
|
Packit |
56e23f |
* +--| Ah(x) == Rh(x) |--+ | T
|
|
Packit |
56e23f |
* A | +------------------+ | |
|
|
Packit |
56e23f |
* C FALSE TRUE |
|
|
Packit |
56e23f |
* C <----+ | |
|
|
Packit |
56e23f |
* E <----+ +------------+ |
|
|
Packit |
56e23f |
* P FALSE v |
|
|
Packit |
56e23f |
* T | +------------------+ |
|
|
Packit |
56e23f |
* +--| Al(x) > Rl(x) |------+
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* SCMP_CMP_EQ:
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
* +--| Ah(x) == Rh(x) |--+
|
|
Packit |
56e23f |
* R | +------------------+ | A
|
|
Packit |
56e23f |
* E FALSE TRUE C
|
|
Packit |
56e23f |
* J <----+ | C
|
|
Packit |
56e23f |
* E <----+ +------------+ +----> E
|
|
Packit |
56e23f |
* C FALSE v | P
|
|
Packit |
56e23f |
* T | +------------------+ | T
|
|
Packit |
56e23f |
* +--| Al(x) == Rl(x) |------+
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* SCMP_CMP_NE:
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
* +--| Ah(x) == Rh(x) |--+
|
|
Packit |
56e23f |
* A | +------------------+ | R
|
|
Packit |
56e23f |
* C FALSE TRUE E
|
|
Packit |
56e23f |
* C <----+ | J
|
|
Packit |
56e23f |
* E <----+ +------------+ +----> E
|
|
Packit |
56e23f |
* P FALSE v | C
|
|
Packit |
56e23f |
* T | +------------------+ | T
|
|
Packit |
56e23f |
* +--| Al(x) == Rl(x) |------+
|
|
Packit |
56e23f |
* +------------------+
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* setup the level */
|
|
Packit |
56e23f |
switch (chain[iter].op) {
|
|
Packit |
56e23f |
case SCMP_CMP_GT:
|
|
Packit |
56e23f |
case SCMP_CMP_GE:
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
c_iter[2] = zmalloc(sizeof(*c_iter[2]));
|
|
Packit |
56e23f |
if (c_iter[2] == NULL) {
|
|
Packit |
56e23f |
free(c_iter[0]);
|
|
Packit |
56e23f |
free(c_iter[1]);
|
|
Packit |
56e23f |
goto gen_64_failure;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter[0]->arg = arg;
|
|
Packit |
56e23f |
c_iter[1]->arg = arg;
|
|
Packit |
56e23f |
c_iter[2]->arg = arg;
|
|
Packit |
56e23f |
c_iter[0]->arg_h_flg = true;
|
|
Packit |
56e23f |
c_iter[1]->arg_h_flg = true;
|
|
Packit |
56e23f |
c_iter[2]->arg_h_flg = false;
|
|
Packit |
56e23f |
c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
|
|
Packit |
56e23f |
c_iter[1]->arg_offset = arch_arg_offset_hi(arch, arg);
|
|
Packit |
56e23f |
c_iter[2]->arg_offset = arch_arg_offset_lo(arch, arg);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter[0]->mask = D64_HI(mask);
|
|
Packit |
56e23f |
c_iter[1]->mask = D64_HI(mask);
|
|
Packit |
56e23f |
c_iter[2]->mask = D64_LO(mask);
|
|
Packit |
56e23f |
c_iter[0]->datum = D64_HI(datum);
|
|
Packit |
56e23f |
c_iter[1]->datum = D64_HI(datum);
|
|
Packit |
56e23f |
c_iter[2]->datum = D64_LO(datum);
|
|
Packit |
56e23f |
c_iter[0]->datum_full = datum;
|
|
Packit |
56e23f |
c_iter[1]->datum_full = datum;
|
|
Packit |
56e23f |
c_iter[2]->datum_full = datum;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
_db_node_mask_fixup(c_iter[0]);
|
|
Packit |
56e23f |
_db_node_mask_fixup(c_iter[1]);
|
|
Packit |
56e23f |
_db_node_mask_fixup(c_iter[2]);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter[0]->op = SCMP_CMP_GT;
|
|
Packit |
56e23f |
c_iter[1]->op = SCMP_CMP_EQ;
|
|
Packit |
56e23f |
switch (chain[iter].op) {
|
|
Packit |
56e23f |
case SCMP_CMP_GT:
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
c_iter[2]->op = SCMP_CMP_GT;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_GE:
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
c_iter[2]->op = SCMP_CMP_GE;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
/* we should never get here */
|
|
Packit |
56e23f |
goto gen_64_failure;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
c_iter[0]->op_orig = chain[iter].op;
|
|
Packit |
56e23f |
c_iter[1]->op_orig = chain[iter].op;
|
|
Packit |
56e23f |
c_iter[2]->op_orig = chain[iter].op;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter[0]->nxt_f = _db_node_get(c_iter[1]);
|
|
Packit |
56e23f |
c_iter[1]->nxt_t = _db_node_get(c_iter[2]);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_MASKED_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_NE:
|
|
Packit |
56e23f |
c_iter[0]->arg = arg;
|
|
Packit |
56e23f |
c_iter[1]->arg = arg;
|
|
Packit |
56e23f |
c_iter[0]->arg_h_flg = true;
|
|
Packit |
56e23f |
c_iter[1]->arg_h_flg = false;
|
|
Packit |
56e23f |
c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg);
|
|
Packit |
56e23f |
c_iter[1]->arg_offset = arch_arg_offset_lo(arch, arg);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter[0]->mask = D64_HI(mask);
|
|
Packit |
56e23f |
c_iter[1]->mask = D64_LO(mask);
|
|
Packit |
56e23f |
c_iter[0]->datum = D64_HI(datum);
|
|
Packit |
56e23f |
c_iter[1]->datum = D64_LO(datum);
|
|
Packit |
56e23f |
c_iter[0]->datum_full = datum;
|
|
Packit |
56e23f |
c_iter[1]->datum_full = datum;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
_db_node_mask_fixup(c_iter[0]);
|
|
Packit |
56e23f |
_db_node_mask_fixup(c_iter[1]);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
switch (chain[iter].op) {
|
|
Packit |
56e23f |
case SCMP_CMP_MASKED_EQ:
|
|
Packit |
56e23f |
c_iter[0]->op = SCMP_CMP_MASKED_EQ;
|
|
Packit |
56e23f |
c_iter[1]->op = SCMP_CMP_MASKED_EQ;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
c_iter[0]->op = SCMP_CMP_EQ;
|
|
Packit |
56e23f |
c_iter[1]->op = SCMP_CMP_EQ;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
c_iter[0]->op_orig = chain[iter].op;
|
|
Packit |
56e23f |
c_iter[1]->op_orig = chain[iter].op;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter[0]->nxt_t = _db_node_get(c_iter[1]);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
/* we should never get here */
|
|
Packit |
56e23f |
goto gen_64_failure;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* link this level to the previous level */
|
|
Packit |
56e23f |
if (c_prev[0] != NULL) {
|
|
Packit |
56e23f |
switch (op_prev) {
|
|
Packit |
56e23f |
case SCMP_CMP_GT:
|
|
Packit |
56e23f |
case SCMP_CMP_GE:
|
|
Packit |
56e23f |
c_prev[0]->nxt_t = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
c_prev[2]->nxt_t = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_MASKED_EQ:
|
|
Packit |
56e23f |
c_prev[1]->nxt_t = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
c_prev[2]->nxt_f = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_NE:
|
|
Packit |
56e23f |
c_prev[0]->nxt_f = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
c_prev[1]->nxt_f = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
/* we should never get here */
|
|
Packit |
56e23f |
goto gen_64_failure;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
s_new->chains = _db_node_get(c_iter[0]);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* update the node count */
|
|
Packit |
56e23f |
switch (chain[iter].op) {
|
|
Packit |
56e23f |
case SCMP_CMP_NE:
|
|
Packit |
56e23f |
case SCMP_CMP_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_MASKED_EQ:
|
|
Packit |
56e23f |
s_new->node_cnt += 2;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
s_new->node_cnt += 3;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* keep pointers to this level */
|
|
Packit |
56e23f |
c_prev[0] = c_iter[0];
|
|
Packit |
56e23f |
c_prev[1] = c_iter[1];
|
|
Packit |
56e23f |
c_prev[2] = c_iter[2];
|
|
Packit |
56e23f |
op_prev = chain[iter].op;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (c_iter[0] != NULL) {
|
|
Packit |
56e23f |
/* set the actions on the last layer */
|
|
Packit |
56e23f |
switch (op_prev) {
|
|
Packit |
56e23f |
case SCMP_CMP_GT:
|
|
Packit |
56e23f |
case SCMP_CMP_GE:
|
|
Packit |
56e23f |
c_iter[0]->act_t_flg = true;
|
|
Packit |
56e23f |
c_iter[0]->act_t = rule->action;
|
|
Packit |
56e23f |
c_iter[2]->act_t_flg = true;
|
|
Packit |
56e23f |
c_iter[2]->act_t = rule->action;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
c_iter[1]->act_f_flg = true;
|
|
Packit |
56e23f |
c_iter[1]->act_f = rule->action;
|
|
Packit |
56e23f |
c_iter[2]->act_f_flg = true;
|
|
Packit |
56e23f |
c_iter[2]->act_f = rule->action;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_MASKED_EQ:
|
|
Packit |
56e23f |
c_iter[1]->act_t_flg = true;
|
|
Packit |
56e23f |
c_iter[1]->act_t = rule->action;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_NE:
|
|
Packit |
56e23f |
c_iter[0]->act_f_flg = true;
|
|
Packit |
56e23f |
c_iter[0]->act_f = rule->action;
|
|
Packit |
56e23f |
c_iter[1]->act_f_flg = true;
|
|
Packit |
56e23f |
c_iter[1]->act_f = rule->action;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
/* we should never get here */
|
|
Packit |
56e23f |
goto gen_64_failure;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
s_new->action = rule->action;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return s_new;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
gen_64_failure:
|
|
Packit |
56e23f |
/* free the new chain and its syscall struct */
|
|
Packit |
56e23f |
_db_tree_put(&s_new->chains);
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Generate a new filter rule for a 32 bit system
|
|
Packit |
56e23f |
* @param arch the architecture definition
|
|
Packit |
56e23f |
* @param rule the new filter rule
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function generates a new syscall filter for a 32 bit system. Returns
|
|
Packit |
56e23f |
* zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch,
|
|
Packit |
56e23f |
const struct db_api_rule_list *rule)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
struct db_sys_list *s_new;
|
|
Packit |
56e23f |
const struct db_api_arg *chain = rule->args;
|
|
Packit |
56e23f |
struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL;
|
|
Packit |
56e23f |
bool tf_flag;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
s_new = zmalloc(sizeof(*s_new));
|
|
Packit |
56e23f |
if (s_new == NULL)
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
s_new->num = rule->syscall;
|
|
Packit |
56e23f |
s_new->valid = true;
|
|
Packit |
56e23f |
/* run through the argument chain */
|
|
Packit |
56e23f |
for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
|
|
Packit |
56e23f |
if (chain[iter].valid == 0)
|
|
Packit |
56e23f |
continue;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* skip generating instructions which are no-ops */
|
|
Packit |
56e23f |
if (!_db_arg_cmp_need_lo(&chain[iter]))
|
|
Packit |
56e23f |
continue;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_iter = zmalloc(sizeof(*c_iter));
|
|
Packit |
56e23f |
if (c_iter == NULL)
|
|
Packit |
56e23f |
goto gen_32_failure;
|
|
Packit |
56e23f |
c_iter->arg = chain[iter].arg;
|
|
Packit |
56e23f |
c_iter->arg_h_flg = false;
|
|
Packit |
56e23f |
c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg);
|
|
Packit |
56e23f |
c_iter->op = chain[iter].op;
|
|
Packit |
56e23f |
c_iter->op_orig = chain[iter].op;
|
|
Packit |
56e23f |
/* implicitly strips off the upper 32 bit */
|
|
Packit |
56e23f |
c_iter->mask = chain[iter].mask;
|
|
Packit |
56e23f |
c_iter->datum = chain[iter].datum;
|
|
Packit |
56e23f |
c_iter->datum_full = chain[iter].datum;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* link in the new node and update the chain */
|
|
Packit |
56e23f |
if (c_prev != NULL) {
|
|
Packit |
56e23f |
if (tf_flag)
|
|
Packit |
56e23f |
c_prev->nxt_t = _db_node_get(c_iter);
|
|
Packit |
56e23f |
else
|
|
Packit |
56e23f |
c_prev->nxt_f = _db_node_get(c_iter);
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
s_new->chains = _db_node_get(c_iter);
|
|
Packit |
56e23f |
s_new->node_cnt++;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* rewrite the op to reduce the op/datum combos */
|
|
Packit |
56e23f |
switch (c_iter->op) {
|
|
Packit |
56e23f |
case SCMP_CMP_NE:
|
|
Packit |
56e23f |
c_iter->op = SCMP_CMP_EQ;
|
|
Packit |
56e23f |
tf_flag = false;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
c_iter->op = SCMP_CMP_GE;
|
|
Packit |
56e23f |
tf_flag = false;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
c_iter->op = SCMP_CMP_GT;
|
|
Packit |
56e23f |
tf_flag = false;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
tf_flag = true;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* fixup the mask/datum */
|
|
Packit |
56e23f |
_db_node_mask_fixup(c_iter);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
c_prev = c_iter;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
if (c_iter != NULL) {
|
|
Packit |
56e23f |
/* set the leaf node */
|
|
Packit |
56e23f |
if (tf_flag) {
|
|
Packit |
56e23f |
c_iter->act_t_flg = true;
|
|
Packit |
56e23f |
c_iter->act_t = rule->action;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
c_iter->act_f_flg = true;
|
|
Packit |
56e23f |
c_iter->act_f = rule->action;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
s_new->action = rule->action;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return s_new;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
gen_32_failure:
|
|
Packit |
56e23f |
/* free the new chain and its syscall struct */
|
|
Packit |
56e23f |
_db_tree_put(&s_new->chains);
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
return NULL;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Add a new rule to the seccomp filter DB
|
|
Packit |
56e23f |
* @param db the seccomp filter db
|
|
Packit |
56e23f |
* @param rule the filter rule
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function adds a new syscall filter to the seccomp filter DB, adding to
|
|
Packit |
56e23f |
* the existing filters for the syscall, unless no argument specific filters
|
|
Packit |
56e23f |
* are present (filtering only on the syscall). When adding new chains, the
|
|
Packit |
56e23f |
* shortest chain, or most inclusive filter match, will be entered into the
|
|
Packit |
56e23f |
* filter DB. Returns zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* It is important to note that in the case of failure the db may be corrupted,
|
|
Packit |
56e23f |
* the caller must use the transaction mechanism if the db integrity is
|
|
Packit |
56e23f |
* important.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc = -ENOMEM;
|
|
Packit |
56e23f |
struct db_sys_list *s_new, *s_iter, *s_prev = NULL;
|
|
Packit |
56e23f |
struct db_iter_state state;
|
|
Packit |
56e23f |
bool rm_flag = false;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
assert(db != NULL);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* do all our possible memory allocation up front so we don't have to
|
|
Packit |
56e23f |
* worry about failure once we get to the point where we start updating
|
|
Packit |
56e23f |
* the filter db */
|
|
Packit |
56e23f |
if (db->arch->size == ARCH_SIZE_64)
|
|
Packit |
56e23f |
s_new = _db_rule_gen_64(db->arch, rule);
|
|
Packit |
56e23f |
else if (db->arch->size == ARCH_SIZE_32)
|
|
Packit |
56e23f |
s_new = _db_rule_gen_32(db->arch, rule);
|
|
Packit |
56e23f |
else
|
|
Packit |
56e23f |
return -EFAULT;
|
|
Packit |
56e23f |
if (s_new == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* find a matching syscall/chain or insert a new one */
|
|
Packit |
56e23f |
s_iter = db->syscalls;
|
|
Packit |
56e23f |
while (s_iter != NULL && s_iter->num < rule->syscall) {
|
|
Packit |
56e23f |
s_prev = s_iter;
|
|
Packit |
56e23f |
s_iter = s_iter->next;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
s_new->priority = _DB_PRI_MASK_CHAIN - s_new->node_cnt;
|
|
Packit |
56e23f |
add_reset:
|
|
Packit |
56e23f |
if (s_iter == NULL || s_iter->num != rule->syscall) {
|
|
Packit |
56e23f |
/* new syscall, add before s_iter */
|
|
Packit |
56e23f |
if (s_prev != NULL) {
|
|
Packit |
56e23f |
s_new->next = s_prev->next;
|
|
Packit |
56e23f |
s_prev->next = s_new;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
s_new->next = db->syscalls;
|
|
Packit |
56e23f |
db->syscalls = s_new;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
} else if (s_iter->chains == NULL) {
|
|
Packit |
56e23f |
if (rm_flag || !s_iter->valid) {
|
|
Packit |
56e23f |
/* we are here because our previous pass cleared the
|
|
Packit |
56e23f |
* entire syscall chain when searching for a subtree
|
|
Packit |
56e23f |
* match or the existing syscall entry is a phantom,
|
|
Packit |
56e23f |
* so either way add the new chain */
|
|
Packit |
56e23f |
s_iter->chains = s_new->chains;
|
|
Packit |
56e23f |
s_iter->action = s_new->action;
|
|
Packit |
56e23f |
s_iter->node_cnt = s_new->node_cnt;
|
|
Packit |
56e23f |
if (s_iter->valid)
|
|
Packit |
56e23f |
s_iter->priority = s_new->priority;
|
|
Packit |
56e23f |
s_iter->valid = true;
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
rc = 0;
|
|
Packit |
56e23f |
goto add_priority_update;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
/* syscall exists without any chains - existing filter
|
|
Packit |
56e23f |
* is at least as large as the new entry so cleanup and
|
|
Packit |
56e23f |
* exit */
|
|
Packit |
56e23f |
_db_tree_put(&s_new->chains);
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
goto add_free_ok;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else if (s_iter->chains != NULL && s_new->chains == NULL) {
|
|
Packit |
56e23f |
/* syscall exists with chains but the new filter has no chains
|
|
Packit |
56e23f |
* so we need to clear the existing chains and exit */
|
|
Packit |
56e23f |
_db_tree_put(&s_iter->chains);
|
|
Packit |
56e23f |
s_iter->chains = NULL;
|
|
Packit |
56e23f |
s_iter->node_cnt = 0;
|
|
Packit |
56e23f |
s_iter->action = rule->action;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* cleanup the new tree and return */
|
|
Packit |
56e23f |
_db_tree_put(&s_new->chains);
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
goto add_free_ok;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* prune any sub-trees that are no longer required */
|
|
Packit |
56e23f |
memset(&state, 0, sizeof(state));
|
|
Packit |
56e23f |
state.sx = s_iter;
|
|
Packit |
56e23f |
state.action = rule->action;
|
|
Packit |
56e23f |
rc = _db_tree_prune(&s_iter->chains, s_new->chains, &state);
|
|
Packit |
56e23f |
if (rc > 0) {
|
|
Packit |
56e23f |
/* we pruned at least some of the existing tree */
|
|
Packit |
56e23f |
rm_flag = true;
|
|
Packit |
56e23f |
s_iter->node_cnt -= rc;
|
|
Packit |
56e23f |
if (s_iter->chains == NULL)
|
|
Packit |
56e23f |
/* we pruned the entire tree */
|
|
Packit |
56e23f |
goto add_reset;
|
|
Packit |
56e23f |
} else if ((state.flags & _DB_IST_M_REDUNDANT) == _DB_IST_M_REDUNDANT) {
|
|
Packit |
56e23f |
/* the existing tree is "shorter", drop the new one */
|
|
Packit |
56e23f |
_db_tree_put(&s_new->chains);
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
goto add_free_ok;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* add the new rule to the existing filter and cleanup */
|
|
Packit |
56e23f |
memset(&state, 0, sizeof(state));
|
|
Packit |
56e23f |
state.sx = s_iter;
|
|
Packit |
56e23f |
rc = _db_tree_add(&s_iter->chains, s_new->chains, &state);
|
|
Packit |
56e23f |
if (rc < 0)
|
|
Packit |
56e23f |
goto add_failure;
|
|
Packit |
56e23f |
s_iter->node_cnt += s_new->node_cnt;
|
|
Packit |
56e23f |
s_iter->node_cnt -= _db_tree_put(&s_new->chains);
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
add_free_ok:
|
|
Packit |
56e23f |
rc = 0;
|
|
Packit |
56e23f |
add_priority_update:
|
|
Packit |
56e23f |
/* update the priority */
|
|
Packit |
56e23f |
if (s_iter != NULL) {
|
|
Packit |
56e23f |
s_iter->priority &= (~_DB_PRI_MASK_CHAIN);
|
|
Packit |
56e23f |
s_iter->priority |= (_DB_PRI_MASK_CHAIN - s_iter->node_cnt);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
add_failure:
|
|
Packit |
56e23f |
/* NOTE: another reminder that we don't do any db error recovery here,
|
|
Packit |
56e23f |
* use the transaction mechanism as previously mentioned */
|
|
Packit |
56e23f |
_db_tree_put(&s_new->chains);
|
|
Packit |
56e23f |
free(s_new);
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Set the priority of a given syscall
|
|
Packit |
56e23f |
* @param col the filter collection
|
|
Packit |
56e23f |
* @param syscall the syscall number
|
|
Packit |
56e23f |
* @param priority priority value, higher value == higher priority
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function sets the priority of the given syscall; this value is used
|
|
Packit |
56e23f |
* when generating the seccomp filter code such that higher priority syscalls
|
|
Packit |
56e23f |
* will incur less filter code overhead than the lower priority syscalls in the
|
|
Packit |
56e23f |
* filter. Returns zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_syscall_priority(struct db_filter_col *col,
|
|
Packit |
56e23f |
int syscall, uint8_t priority)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc = 0, rc_tmp;
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
int sc_tmp;
|
|
Packit |
56e23f |
struct db_filter *filter;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
for (iter = 0; iter < col->filter_cnt; iter++) {
|
|
Packit |
56e23f |
filter = col->filters[iter];
|
|
Packit |
56e23f |
sc_tmp = syscall;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
rc_tmp = arch_syscall_translate(filter->arch, &sc_tmp);
|
|
Packit |
56e23f |
if (rc_tmp < 0)
|
|
Packit |
56e23f |
goto priority_failure;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* if this is a pseudo syscall then we need to rewrite the
|
|
Packit |
56e23f |
* syscall for some arch specific reason, don't forget the
|
|
Packit |
56e23f |
* special handling for syscall -1 */
|
|
Packit |
56e23f |
if (sc_tmp < -1) {
|
|
Packit |
56e23f |
/* we set this as a strict op - we don't really care
|
|
Packit |
56e23f |
* since priorities are a "best effort" thing - as we
|
|
Packit |
56e23f |
* want to catch the -EDOM error and bail on this
|
|
Packit |
56e23f |
* architecture */
|
|
Packit |
56e23f |
rc_tmp = arch_syscall_rewrite(filter->arch, &sc_tmp);
|
|
Packit |
56e23f |
if (rc_tmp == -EDOM)
|
|
Packit |
56e23f |
continue;
|
|
Packit |
56e23f |
if (rc_tmp < 0)
|
|
Packit |
56e23f |
goto priority_failure;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
rc_tmp = _db_syscall_priority(filter, sc_tmp, priority);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
priority_failure:
|
|
Packit |
56e23f |
if (rc == 0 && rc_tmp < 0)
|
|
Packit |
56e23f |
rc = rc_tmp;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Add a new rule to the current filter
|
|
Packit |
56e23f |
* @param col the filter collection
|
|
Packit |
56e23f |
* @param strict the strict flag
|
|
Packit |
56e23f |
* @param action the filter action
|
|
Packit |
56e23f |
* @param syscall the syscall number
|
|
Packit |
56e23f |
* @param arg_cnt the number of argument filters in the argument filter chain
|
|
Packit |
56e23f |
* @param arg_array the argument filter chain, (uint, enum scmp_compare, ulong)
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function adds a new argument/comparison/value to the seccomp filter for
|
|
Packit |
56e23f |
* a syscall; multiple arguments can be specified and they will be chained
|
|
Packit |
56e23f |
* together (essentially AND'd together) in the filter. When the strict flag
|
|
Packit |
56e23f |
* is true the function will fail if the exact rule can not be added to the
|
|
Packit |
56e23f |
* filter, if the strict flag is false the function will not fail if the
|
|
Packit |
56e23f |
* function needs to adjust the rule due to architecture specifics. Returns
|
|
Packit |
56e23f |
* zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_rule_add(struct db_filter_col *col,
|
|
Packit |
56e23f |
bool strict, uint32_t action, int syscall,
|
|
Packit |
56e23f |
unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc = 0, rc_tmp;
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
unsigned int arg_num;
|
|
Packit |
56e23f |
size_t chain_size;
|
|
Packit |
56e23f |
struct db_api_arg *chain = NULL;
|
|
Packit |
56e23f |
struct scmp_arg_cmp arg_data;
|
|
Packit |
56e23f |
struct db_api_rule_list *rule, *rule_tmp;
|
|
Packit |
56e23f |
struct db_filter *db;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* collect the arguments for the filter rule */
|
|
Packit |
56e23f |
chain_size = sizeof(*chain) * ARG_COUNT_MAX;
|
|
Packit |
56e23f |
chain = zmalloc(chain_size);
|
|
Packit |
56e23f |
if (chain == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
for (iter = 0; iter < arg_cnt; iter++) {
|
|
Packit |
56e23f |
arg_data = arg_array[iter];
|
|
Packit |
56e23f |
arg_num = arg_data.arg;
|
|
Packit |
56e23f |
if (arg_num < ARG_COUNT_MAX && chain[arg_num].valid == 0) {
|
|
Packit |
56e23f |
chain[arg_num].valid = 1;
|
|
Packit |
56e23f |
chain[arg_num].arg = arg_num;
|
|
Packit |
56e23f |
chain[arg_num].op = arg_data.op;
|
|
Packit |
56e23f |
/* TODO: we should check datum/mask size against the
|
|
Packit |
56e23f |
* arch definition, e.g. 64 bit datum on x86 */
|
|
Packit |
56e23f |
switch (chain[arg_num].op) {
|
|
Packit |
56e23f |
case SCMP_CMP_NE:
|
|
Packit |
56e23f |
case SCMP_CMP_LT:
|
|
Packit |
56e23f |
case SCMP_CMP_LE:
|
|
Packit |
56e23f |
case SCMP_CMP_EQ:
|
|
Packit |
56e23f |
case SCMP_CMP_GE:
|
|
Packit |
56e23f |
case SCMP_CMP_GT:
|
|
Packit |
56e23f |
chain[arg_num].mask = DATUM_MAX;
|
|
Packit |
56e23f |
chain[arg_num].datum = arg_data.datum_a;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
case SCMP_CMP_MASKED_EQ:
|
|
Packit |
56e23f |
chain[arg_num].mask = arg_data.datum_a;
|
|
Packit |
56e23f |
chain[arg_num].datum = arg_data.datum_b;
|
|
Packit |
56e23f |
break;
|
|
Packit |
56e23f |
default:
|
|
Packit |
56e23f |
rc = -EINVAL;
|
|
Packit |
56e23f |
goto add_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
rc = -EINVAL;
|
|
Packit |
56e23f |
goto add_return;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* create a checkpoint */
|
|
Packit |
56e23f |
rc = db_col_transaction_start(col);
|
|
Packit |
56e23f |
if (rc != 0)
|
|
Packit |
56e23f |
goto add_return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* add the rule to the different filters in the collection */
|
|
Packit |
56e23f |
for (iter = 0; iter < col->filter_cnt; iter++) {
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* TODO: consolidate with db_col_transaction_start() */
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
db = col->filters[iter];
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* create the rule */
|
|
Packit |
56e23f |
rule = _db_rule_new(strict, action, syscall, chain);
|
|
Packit |
56e23f |
if (rule == NULL) {
|
|
Packit |
56e23f |
rc_tmp = -ENOMEM;
|
|
Packit |
56e23f |
goto add_arch_fail;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* add the rule */
|
|
Packit |
56e23f |
rc_tmp = arch_filter_rule_add(db, rule);
|
|
Packit |
56e23f |
if (rc_tmp == 0) {
|
|
Packit |
56e23f |
/* insert the chain to the end of the rule list */
|
|
Packit |
56e23f |
rule_tmp = rule;
|
|
Packit |
56e23f |
while (rule_tmp->next)
|
|
Packit |
56e23f |
rule_tmp = rule_tmp->next;
|
|
Packit |
56e23f |
if (db->rules != NULL) {
|
|
Packit |
56e23f |
rule->prev = db->rules->prev;
|
|
Packit |
56e23f |
rule_tmp->next = db->rules;
|
|
Packit |
56e23f |
db->rules->prev->next = rule;
|
|
Packit |
56e23f |
db->rules->prev = rule_tmp;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
rule->prev = rule_tmp;
|
|
Packit |
56e23f |
rule_tmp->next = rule;
|
|
Packit |
56e23f |
db->rules = rule;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
} else
|
|
Packit |
56e23f |
free(rule);
|
|
Packit |
56e23f |
add_arch_fail:
|
|
Packit |
56e23f |
if (rc_tmp != 0 && rc == 0)
|
|
Packit |
56e23f |
rc = rc_tmp;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* commit the transaction or abort */
|
|
Packit |
56e23f |
if (rc == 0)
|
|
Packit |
56e23f |
db_col_transaction_commit(col);
|
|
Packit |
56e23f |
else
|
|
Packit |
56e23f |
db_col_transaction_abort(col);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
add_return:
|
|
Packit |
56e23f |
if (chain != NULL)
|
|
Packit |
56e23f |
free(chain);
|
|
Packit |
56e23f |
return rc;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Start a new seccomp filter transaction
|
|
Packit |
56e23f |
* @param col the filter collection
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function starts a new seccomp filter transaction for the given filter
|
|
Packit |
56e23f |
* collection. Returns zero on success, negative values on failure.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
int db_col_transaction_start(struct db_filter_col *col)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int rc;
|
|
Packit |
56e23f |
unsigned int iter;
|
|
Packit |
56e23f |
struct db_filter_snap *snap;
|
|
Packit |
56e23f |
struct db_filter *filter_o, *filter_s;
|
|
Packit |
56e23f |
struct db_api_rule_list *rule_o, *rule_s = NULL, *rule_tmp;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* allocate the snapshot */
|
|
Packit |
56e23f |
snap = zmalloc(sizeof(*snap));
|
|
Packit |
56e23f |
if (snap == NULL)
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
snap->filters = zmalloc(sizeof(struct db_filter *) * col->filter_cnt);
|
|
Packit |
56e23f |
if (snap->filters == NULL) {
|
|
Packit |
56e23f |
free(snap);
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
snap->filter_cnt = col->filter_cnt;
|
|
Packit |
56e23f |
for (iter = 0; iter < snap->filter_cnt; iter++)
|
|
Packit |
56e23f |
snap->filters[iter] = NULL;
|
|
Packit |
56e23f |
snap->next = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* create a snapshot of the current filter state */
|
|
Packit |
56e23f |
for (iter = 0; iter < col->filter_cnt; iter++) {
|
|
Packit |
56e23f |
/* allocate a new filter */
|
|
Packit |
56e23f |
filter_o = col->filters[iter];
|
|
Packit |
56e23f |
filter_s = _db_init(filter_o->arch);
|
|
Packit |
56e23f |
if (filter_s == NULL)
|
|
Packit |
56e23f |
goto trans_start_failure;
|
|
Packit |
56e23f |
snap->filters[iter] = filter_s;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* create a filter snapshot from existing rules */
|
|
Packit |
56e23f |
rule_o = filter_o->rules;
|
|
Packit |
56e23f |
if (rule_o == NULL)
|
|
Packit |
56e23f |
continue;
|
|
Packit |
56e23f |
do {
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* TODO: consolidate with db_col_rule_add() */
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* duplicate the rule */
|
|
Packit |
56e23f |
rule_s = db_rule_dup(rule_o);
|
|
Packit |
56e23f |
if (rule_s == NULL)
|
|
Packit |
56e23f |
goto trans_start_failure;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* add the rule */
|
|
Packit |
56e23f |
rc = arch_filter_rule_add(filter_s, rule_s);
|
|
Packit |
56e23f |
if (rc != 0)
|
|
Packit |
56e23f |
goto trans_start_failure;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* insert the chain to the end of the rule list */
|
|
Packit |
56e23f |
rule_tmp = rule_s;
|
|
Packit |
56e23f |
while (rule_tmp->next)
|
|
Packit |
56e23f |
rule_tmp = rule_tmp->next;
|
|
Packit |
56e23f |
if (filter_s->rules != NULL) {
|
|
Packit |
56e23f |
rule_s->prev = filter_s->rules->prev;
|
|
Packit |
56e23f |
rule_tmp->next = filter_s->rules;
|
|
Packit |
56e23f |
filter_s->rules->prev->next = rule_s;
|
|
Packit |
56e23f |
filter_s->rules->prev = rule_tmp;
|
|
Packit |
56e23f |
} else {
|
|
Packit |
56e23f |
rule_s->prev = rule_tmp;
|
|
Packit |
56e23f |
rule_tmp->next = rule_s;
|
|
Packit |
56e23f |
filter_s->rules = rule_s;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
rule_s = NULL;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* next rule */
|
|
Packit |
56e23f |
rule_o = rule_o->next;
|
|
Packit |
56e23f |
} while (rule_o != filter_o->rules);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* add the snapshot to the list */
|
|
Packit |
56e23f |
snap->next = col->snapshots;
|
|
Packit |
56e23f |
col->snapshots = snap;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
return 0;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
trans_start_failure:
|
|
Packit |
56e23f |
if (rule_s != NULL)
|
|
Packit |
56e23f |
free(rule_s);
|
|
Packit |
56e23f |
_db_snap_release(snap);
|
|
Packit |
56e23f |
return -ENOMEM;
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Abort the top most seccomp filter transaction
|
|
Packit |
56e23f |
* @param col the filter collection
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function aborts the most recent seccomp filter transaction.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
void db_col_transaction_abort(struct db_filter_col *col)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
int iter;
|
|
Packit |
56e23f |
unsigned int filter_cnt;
|
|
Packit |
56e23f |
struct db_filter **filters;
|
|
Packit |
56e23f |
struct db_filter_snap *snap;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
if (col->snapshots == NULL)
|
|
Packit |
56e23f |
return;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* replace the current filter with the last snapshot */
|
|
Packit |
56e23f |
snap = col->snapshots;
|
|
Packit |
56e23f |
col->snapshots = snap->next;
|
|
Packit |
56e23f |
filter_cnt = col->filter_cnt;
|
|
Packit |
56e23f |
filters = col->filters;
|
|
Packit |
56e23f |
col->filter_cnt = snap->filter_cnt;
|
|
Packit |
56e23f |
col->filters = snap->filters;
|
|
Packit |
56e23f |
free(snap);
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/* free the filter we swapped out */
|
|
Packit |
56e23f |
for (iter = 0; iter < filter_cnt; iter++)
|
|
Packit |
56e23f |
_db_release(filters[iter]);
|
|
Packit |
56e23f |
free(filters);
|
|
Packit |
56e23f |
}
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
/**
|
|
Packit |
56e23f |
* Commit the top most seccomp filter transaction
|
|
Packit |
56e23f |
* @param col the filter collection
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
* This function commits the most recent seccomp filter transaction.
|
|
Packit |
56e23f |
*
|
|
Packit |
56e23f |
*/
|
|
Packit |
56e23f |
void db_col_transaction_commit(struct db_filter_col *col)
|
|
Packit |
56e23f |
{
|
|
Packit |
56e23f |
struct db_filter_snap *snap;
|
|
Packit |
56e23f |
|
|
Packit |
56e23f |
snap = col->snapshots;
|
|
Packit |
56e23f |
col->snapshots = snap->next;
|
|
Packit |
56e23f |
_db_snap_release(snap);
|
|
Packit |
56e23f |
}
|