Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFE: syscall argument range checking ops #95

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <[email protected]>
* Author: Paul Moore <[email protected]>
*/

/*
* 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