Skip to content

Commit

Permalink
Range ops
Browse files Browse the repository at this point in the history
Add ops for checking whether arguments are in a specified range,
optionally negated and/or with mask: SCMP_CMP_{MASKED_}{NOT_}IN_RANGE.

Closes: seccomp#94

Signed-off-by: Topi Miettinen <toiwoton@gmail.com>
  • Loading branch information
topimiettinen committed Sep 17, 2017
1 parent 9e61fd7 commit 363dfca
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 19 deletions.
5 changes: 5 additions & 0 deletions include/seccomp.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ enum scmp_compare {
SCMP_CMP_GE = 5, /**< greater than or equal */
SCMP_CMP_GT = 6, /**< greater than */
SCMP_CMP_MASKED_EQ = 7, /**< masked equality */
SCMP_CMP_IN_RANGE = 8, /**< in range */
SCMP_CMP_NOT_IN_RANGE = 9, /**< not in range */
SCMP_CMP_MASKED_IN_RANGE = 10, /**< in range, masked */
SCMP_CMP_MASKED_NOT_IN_RANGE = 11, /**< not in range, masked */
_SCMP_CMP_MAX,
};

Expand All @@ -95,6 +99,7 @@ struct scmp_arg_cmp {
enum scmp_compare op; /**< the comparison op, e.g. SCMP_CMP_* */
scmp_datum_t datum_a;
scmp_datum_t datum_b;
scmp_datum_t datum_c;
};

/*
Expand Down
32 changes: 30 additions & 2 deletions src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,10 @@ int db_col_db_remove(struct db_filter_col *col, uint32_t arch_token)
*/
static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg)
{
if (arg->op == SCMP_CMP_MASKED_EQ && D64_LO(arg->mask) == 0)
if ((arg->op == SCMP_CMP_MASKED_EQ ||
arg->op == SCMP_CMP_MASKED_IN_RANGE ||
arg->op == SCMP_CMP_MASKED_NOT_IN_RANGE)&&
D64_LO(arg->mask) == 0)
return false;

return true;
Expand All @@ -980,7 +983,10 @@ static bool _db_arg_cmp_need_lo(const struct db_api_arg *arg)
*/
static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg)
{
if (arg->op == SCMP_CMP_MASKED_EQ && D64_HI(arg->mask) == 0)
if ((arg->op == SCMP_CMP_MASKED_EQ ||
arg->op == SCMP_CMP_MASKED_IN_RANGE ||
arg->op == SCMP_CMP_MASKED_NOT_IN_RANGE) &&
D64_HI(arg->mask) == 0)
return false;

return true;
Expand All @@ -996,6 +1002,7 @@ static bool _db_arg_cmp_need_hi(const struct db_api_arg *arg)
static void _db_node_mask_fixup(struct db_arg_chain_tree *node)
{
node->datum &= node->mask;
node->datum_b &= node->mask;
}

/**
Expand Down Expand Up @@ -1088,6 +1095,12 @@ static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
c_iter_lo->op = SCMP_CMP_GT;
tf_flag = false;
break;
case SCMP_CMP_NOT_IN_RANGE:
case SCMP_CMP_MASKED_NOT_IN_RANGE:
c_iter_hi->op = chain[iter].op;
c_iter_lo->op = chain[iter].op;
tf_flag = false;
break;
default:
c_iter_hi->op = chain[iter].op;
c_iter_lo->op = chain[iter].op;
Expand All @@ -1097,6 +1110,8 @@ static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
c_iter_lo->mask = D64_LO(chain[iter].mask);
c_iter_hi->datum = D64_HI(chain[iter].datum);
c_iter_lo->datum = D64_LO(chain[iter].datum);
c_iter_hi->datum_b = D64_HI(chain[iter].datum_b);
c_iter_lo->datum_b = D64_LO(chain[iter].datum_b);

/* fixup the mask/datum */
_db_node_mask_fixup(c_iter_hi);
Expand Down Expand Up @@ -1176,6 +1191,7 @@ static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch,
/* implicitly strips off the upper 32 bit */
c_iter->mask = chain[iter].mask;
c_iter->datum = chain[iter].datum;
c_iter->datum_b = chain[iter].datum_b;

/* link in the new node and update the chain */
if (c_prev != NULL) {
Expand Down Expand Up @@ -1612,6 +1628,18 @@ int db_col_rule_add(struct db_filter_col *col,
chain[arg_num].mask = arg_data.datum_a;
chain[arg_num].datum = arg_data.datum_b;
break;
case SCMP_CMP_IN_RANGE:
case SCMP_CMP_NOT_IN_RANGE:
chain[arg_num].mask = DATUM_MAX;
chain[arg_num].datum = arg_data.datum_a;
chain[arg_num].datum_b = arg_data.datum_b;
break;
case SCMP_CMP_MASKED_IN_RANGE:
case SCMP_CMP_MASKED_NOT_IN_RANGE:
chain[arg_num].mask = arg_data.datum_a;
chain[arg_num].datum = arg_data.datum_b;
chain[arg_num].datum_b = arg_data.datum_c;
break;
default:
rc = -EINVAL;
goto add_return;
Expand Down
4 changes: 3 additions & 1 deletion src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct db_api_arg {
enum scmp_compare op;
scmp_datum_t mask;
scmp_datum_t datum;
scmp_datum_t datum_b;

bool valid;
};
Expand All @@ -59,6 +60,7 @@ struct db_arg_chain_tree {
/* syscall argument value */
uint32_t mask;
uint32_t datum;
uint32_t datum_b;

/* actions */
bool act_t_flg;
Expand All @@ -83,7 +85,7 @@ struct db_arg_chain_tree {
#define db_chain_eq(x,y) \
(((x)->arg == (y)->arg) && \
((x)->op == (y)->op) && ((x)->datum == (y)->datum) && \
((x)->mask == (y)->mask))
((x)->datum_b == (y)->datum_b) && ((x)->mask == (y)->mask))
#define db_chain_gt(x,y) \
(((x)->arg > (y)->arg) || \
(((x)->arg == (y)->arg) && \
Expand Down
87 changes: 73 additions & 14 deletions src/gen_bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,38 @@ static struct bpf_blk *_gen_bpf_action_hsh(struct bpf_state *state,
return blk;
}

/**
* Fixup the jump target for jump if true
* @param node the filter chain node
* @param act_t_hash hash of action
*/
static struct bpf_jump _gen_bpf_fixup_t(const struct db_arg_chain_tree *node,
uint64_t act_t_hash)
{
if (node->nxt_t != NULL)
return _BPF_JMP_DB(node->nxt_t);
else if (node->act_t_flg)
return _BPF_JMP_HSH(act_t_hash);
else
return _BPF_JMP_NXT(0);
}

/**
* Fixup the jump target for jump if false
* @param node the filter chain node
* @param act_f_hash hash of action for jump if false
*/
static struct bpf_jump _gen_bpf_fixup_f(const struct db_arg_chain_tree *node,
uint64_t act_f_hash)
{
if (node->nxt_f != NULL)
return _BPF_JMP_DB(node->nxt_f);
else if (node->act_f_flg)
return _BPF_JMP_HSH(act_f_hash);
else
return _BPF_JMP_NXT(0);
}

/**
* Generate a BPF instruction block for a given chain node
* @param state the BPF state
Expand All @@ -807,6 +839,7 @@ static struct bpf_blk *_gen_bpf_node(struct bpf_state *state,
uint64_t act_t_hash = 0, act_f_hash = 0;
struct bpf_blk *blk, *b_act;
struct bpf_instr instr;
bool fixup = true;

blk = _blk_alloc();
if (blk == NULL)
Expand Down Expand Up @@ -875,27 +908,53 @@ static struct bpf_blk *_gen_bpf_node(struct bpf_state *state,
_BPF_JMP_NO, _BPF_JMP_NO,
_BPF_K(state->arch, node->datum));
break;
case SCMP_CMP_MASKED_IN_RANGE:
case SCMP_CMP_IN_RANGE:
_BPF_INSTR(instr, _BPF_OP(state->arch, BPF_JMP + BPF_JGE),
_BPF_JMP_NO, _BPF_JMP_NO,
_BPF_K(state->arch, node->datum));
instr.jt = _BPF_JMP_IMM(0);
instr.jf = _gen_bpf_fixup_f(node, act_f_hash);
blk = _blk_append(state, blk, &instr);
if (blk == NULL)
goto node_failure;
_BPF_INSTR(instr, _BPF_OP(state->arch, BPF_JMP + BPF_JGT),
_BPF_JMP_NO, _BPF_JMP_NO,
_BPF_K(state->arch, node->datum_b));
/* swap true and false paths */
instr.jf = _gen_bpf_fixup_t(node, act_t_hash);
instr.jt = _gen_bpf_fixup_f(node, act_f_hash);
fixup = false;
break;
case SCMP_CMP_MASKED_NOT_IN_RANGE:
case SCMP_CMP_NOT_IN_RANGE:
_BPF_INSTR(instr, _BPF_OP(state->arch, BPF_JMP + BPF_JGE),
_BPF_JMP_NO, _BPF_JMP_NO,
_BPF_K(state->arch, node->datum));
instr.jt = _BPF_JMP_IMM(0);
instr.jf = _gen_bpf_fixup_f(node, act_f_hash);
blk = _blk_append(state, blk, &instr);
if (blk == NULL)
goto node_failure;
_BPF_INSTR(instr, _BPF_OP(state->arch, BPF_JMP + BPF_JGT),
_BPF_JMP_NO, _BPF_JMP_NO,
_BPF_K(state->arch, node->datum_b));
/* swap true and false paths */
instr.jf = _gen_bpf_fixup_t(node, act_t_hash);
instr.jt = _gen_bpf_fixup_f(node, act_f_hash);
fixup = false;
break;
case SCMP_CMP_NE:
case SCMP_CMP_LT:
case SCMP_CMP_LE:
default:
/* fatal error, we should never get here */
goto node_failure;
}

/* fixup the jump targets */
if (node->nxt_t != NULL)
instr.jt = _BPF_JMP_DB(node->nxt_t);
else if (node->act_t_flg)
instr.jt = _BPF_JMP_HSH(act_t_hash);
else
instr.jt = _BPF_JMP_NXT(0);
if (node->nxt_f != NULL)
instr.jf = _BPF_JMP_DB(node->nxt_f);
else if (node->act_f_flg)
instr.jf = _BPF_JMP_HSH(act_f_hash);
else
instr.jf = _BPF_JMP_NXT(0);
if (fixup) {
instr.jt = _gen_bpf_fixup_t(node, act_t_hash);
instr.jf = _gen_bpf_fixup_f(node, act_f_hash);
}
blk = _blk_append(state, blk, &instr);
if (blk == NULL)
goto node_failure;
Expand Down
32 changes: 31 additions & 1 deletion src/gen_pfc.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@ static void _gen_pfc_chain(const struct arch_def *arch,
/* comparison operation */
_indent(fds, lvl);
fprintf(fds, "if (");
switch (c_iter->op) {
case SCMP_CMP_IN_RANGE:
case SCMP_CMP_MASKED_IN_RANGE:
fprintf(fds, "%u <= ", c_iter->datum);
break;
case SCMP_CMP_NOT_IN_RANGE:
case SCMP_CMP_MASKED_NOT_IN_RANGE:
fprintf(fds, "!(%u <= ", c_iter->datum);
break;
default:
break;
}
_pfc_arg(fds, arch, c_iter);
switch (c_iter->op) {
case SCMP_CMP_EQ:
Expand All @@ -192,13 +204,31 @@ static void _gen_pfc_chain(const struct arch_def *arch,
case SCMP_CMP_MASKED_EQ:
fprintf(fds, " & 0x%.8x == ", c_iter->mask);
break;
case SCMP_CMP_MASKED_IN_RANGE:
case SCMP_CMP_MASKED_NOT_IN_RANGE:
fprintf(fds, " & 0x%.8x", c_iter->mask);
break;
case SCMP_CMP_IN_RANGE:
case SCMP_CMP_NOT_IN_RANGE:
break;
case SCMP_CMP_NE:
case SCMP_CMP_LT:
case SCMP_CMP_LE:
default:
fprintf(fds, " ??? ");
}
fprintf(fds, "%u)\n", c_iter->datum);
switch (c_iter->op) {
case SCMP_CMP_IN_RANGE:
case SCMP_CMP_MASKED_IN_RANGE:
fprintf(fds, " <= %u)\n", c_iter->datum_b);
break;
case SCMP_CMP_NOT_IN_RANGE:
case SCMP_CMP_MASKED_NOT_IN_RANGE:
fprintf(fds, " <= %u))\n", c_iter->datum_b);
break;
default:
fprintf(fds, "%u)\n", c_iter->datum);
}

/* true result */
if (c_iter->act_t_flg) {
Expand Down
80 changes: 80 additions & 0 deletions tests/39-sim-range_ops.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Seccomp Library test program
*
* Copyright (c) 2017 Red Hat <pmoore@redhat.com>
* Author: Paul Moore <paul@paul-moore.com>
*/

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

#include <errno.h>
#include <unistd.h>

#include <seccomp.h>

#include "util.h"

int main(int argc, char *argv[])
{
int rc;
struct util_options opts;
scmp_filter_ctx ctx = NULL;

rc = util_getopt(argc, argv, &opts);
if (rc < 0)
goto out;

ctx = seccomp_init(SCMP_ACT_KILL);
if (ctx == NULL)
return ENOMEM;

/* the syscall and argument numbers are all fake to make the test
* simpler */
rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1000, 3,
SCMP_A0(SCMP_CMP_EQ, 0),
SCMP_A1(SCMP_CMP_IN_RANGE, 0xf0, 0xff),
SCMP_A2(SCMP_CMP_EQ, 2));
if (rc != 0)
goto out;

rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1001, 3,
SCMP_A0(SCMP_CMP_EQ, 0),
SCMP_A1(SCMP_CMP_NOT_IN_RANGE, 0xf0, 0xff),
SCMP_A2(SCMP_CMP_EQ, 2));
if (rc != 0)
goto out;

rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1002, 3,
SCMP_A0(SCMP_CMP_EQ, 0),
SCMP_A1(SCMP_CMP_MASKED_IN_RANGE, 0xff00, 0xf000, 0xfe00),
SCMP_A2(SCMP_CMP_EQ, 2));
if (rc != 0)
goto out;

rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1003, 3,
SCMP_A0(SCMP_CMP_EQ, 0),
SCMP_A1(SCMP_CMP_MASKED_NOT_IN_RANGE, 0xff00, 0xf000, 0xfe00),
SCMP_A2(SCMP_CMP_EQ, 2));
if (rc != 0)
goto out;

rc = util_filter_output(&opts, ctx);
if (rc)
goto out;

out:
seccomp_release(ctx);
return (rc < 0 ? -rc : rc);
}
Loading

0 comments on commit 363dfca

Please sign in to comment.