From 363dfcad45aa4a9a579e068856e0da76b2aefd64 Mon Sep 17 00:00:00 2001 From: Topi Miettinen Date: Mon, 18 Sep 2017 00:48:28 +0300 Subject: [PATCH] Range ops Add ops for checking whether arguments are in a specified range, optionally negated and/or with mask: SCMP_CMP_{MASKED_}{NOT_}IN_RANGE. Closes: #94 Signed-off-by: Topi Miettinen --- include/seccomp.h.in | 5 +++ src/db.c | 32 ++++++++++++- src/db.h | 4 +- src/gen_bpf.c | 87 ++++++++++++++++++++++++++++++------ src/gen_pfc.c | 32 ++++++++++++- tests/39-sim-range_ops.c | 80 +++++++++++++++++++++++++++++++++ tests/39-sim-range_ops.tests | 44 ++++++++++++++++++ tests/Makefile.am | 3 +- 8 files changed, 268 insertions(+), 19 deletions(-) create mode 100644 tests/39-sim-range_ops.c create mode 100644 tests/39-sim-range_ops.tests diff --git a/include/seccomp.h.in b/include/seccomp.h.in index 5843639a..2843eb5e 100644 --- a/include/seccomp.h.in +++ b/include/seccomp.h.in @@ -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, }; @@ -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; }; /* diff --git a/src/db.c b/src/db.c index 3ea8d64c..b39febfa 100644 --- a/src/db.c +++ b/src/db.c @@ -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; @@ -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; @@ -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; } /** @@ -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; @@ -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); @@ -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) { @@ -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; diff --git a/src/db.h b/src/db.h index c4ad5490..7ca5ec68 100644 --- a/src/db.h +++ b/src/db.h @@ -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; }; @@ -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; @@ -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) && \ diff --git a/src/gen_bpf.c b/src/gen_bpf.c index dfaef4e9..09150eee 100644 --- a/src/gen_bpf.c +++ b/src/gen_bpf.c @@ -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 @@ -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) @@ -875,6 +908,42 @@ 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: @@ -882,20 +951,10 @@ static struct bpf_blk *_gen_bpf_node(struct bpf_state *state, /* 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; diff --git a/src/gen_pfc.c b/src/gen_pfc.c index 0215ee8a..334b4d41 100644 --- a/src/gen_pfc.c +++ b/src/gen_pfc.c @@ -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: @@ -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) { diff --git a/tests/39-sim-range_ops.c b/tests/39-sim-range_ops.c new file mode 100644 index 00000000..16b40e9b --- /dev/null +++ b/tests/39-sim-range_ops.c @@ -0,0 +1,80 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2017 Red Hat + * Author: Paul Moore + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include + +#include + +#include "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); +} diff --git a/tests/39-sim-range_ops.tests b/tests/39-sim-range_ops.tests new file mode 100644 index 00000000..a554ea41 --- /dev/null +++ b/tests/39-sim-range_ops.tests @@ -0,0 +1,44 @@ +# +# libseccomp regression test automation data +# +# Copyright Topi Miettinen 2017 +# Author: Topi Miettinen +# + +test type: bpf-sim + +# Testname Arch Syscall Arg0 Arg1 Arg2 Arg3 Arg4 Arg5 Result +39-sim-range_ops all 1000 0 0 2 N N N KILL +39-sim-range_ops all 1000 0 0xef 2 N N N KILL +39-sim-range_ops all 1000 0 0xf0 2 N N N ALLOW +39-sim-range_ops all 1000 0 0xf1 2 N N N ALLOW +39-sim-range_ops all 1000 0 0xff 2 N N N ALLOW +39-sim-range_ops all 1000 0 0x100 2 N N N KILL +39-sim-range_ops all 1001 0 0 2 N N N ALLOW +39-sim-range_ops all 1001 0 0xef 2 N N N ALLOW +39-sim-range_ops all 1001 0 0xf0 2 N N N KILL +39-sim-range_ops all 1001 0 0xf1 2 N N N KILL +39-sim-range_ops all 1001 0 0xff 2 N N N KILL +39-sim-range_ops all 1001 0 0x100 2 N N N ALLOW +39-sim-range_ops all 1002 0 0 2 N N N KILL +39-sim-range_ops all 1002 0 0xaefbb 2 N N N KILL +39-sim-range_ops all 1002 0 0xaf0bb 2 N N N ALLOW +39-sim-range_ops all 1002 0 0xaf1bb 2 N N N ALLOW +39-sim-range_ops all 1002 0 0xafebb 2 N N N ALLOW +39-sim-range_ops all 1002 0 0xaffbb 2 N N N KILL +39-sim-range_ops all 1003 0 0 2 N N N ALLOW +39-sim-range_ops all 1003 0 0xaefbb 2 N N N ALLOW +39-sim-range_ops all 1003 0 0xaf0bb 2 N N N KILL +39-sim-range_ops all 1003 0 0xaf1bb 2 N N N KILL +39-sim-range_ops all 1003 0 0xafebb 2 N N N KILL +39-sim-range_ops all 1003 0 0xaffbb 2 N N N ALLOW + +test type: bpf-sim-fuzz + +# Testname StressCount +39-sim-range_ops 50 + +test type: bpf-valgrind + +# Testname +39-sim-range_ops diff --git a/tests/Makefile.am b/tests/Makefile.am index 3251bf6b..7482e4c1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -77,7 +77,8 @@ check_PROGRAMS = \ 35-sim-negative_one \ 36-sim-ipc_syscalls \ 37-sim-ipc_syscalls_be \ - 38-basic-pfc_coverage + 38-basic-pfc_coverage \ + 39-sim-range_ops EXTRA_DIST_TESTPYTHON = \ util.py \