diff --git a/include/seccomp.h.in b/include/seccomp.h.in index 6f836fb7..5c65e371 100644 --- a/include/seccomp.h.in +++ b/include/seccomp.h.in @@ -94,6 +94,9 @@ enum scmp_compare { SCMP_CMP_GT = 6, /**< greater than */ SCMP_CMP_MASKED_EQ = 7, /**< masked equality */ _SCMP_CMP_MAX, + + SCMP_CMP_OPMASK = 0xFFFF, + SCMP_CMP_32BIT = 1 << 16, /**< operation is 32-bit */ }; /** diff --git a/src/db.c b/src/db.c index 2dc9733a..f9af89b3 100644 --- a/src/db.c +++ b/src/db.c @@ -1588,379 +1588,417 @@ static void _db_node_mask_fixup(struct db_arg_chain_tree *node) } /** - * Generate a new filter rule for a 64 bit system + * Generate tree nodes for a 64 bit argument comparison + * @param s_new the syscall filter * @param arch the architecture definition - * @param rule the new filter rule + * @param api_arg the argument comparison + * @param prev_nodes a NULL terminated array of tree nodes to link to + * @param tf_flag whether to link to the nxt_t or nxt_f branch of prev_nodes * - * This function generates a new syscall filter for a 64 bit system. Returns - * zero on success, negative values on failure. + * This function generates tree nodes representing a 64 bit argument + * comparison. Returns zero on success, negative values on failure. * */ -static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch, - const struct db_api_rule_list *rule) +static int _db_rule_gen_arg_64(struct db_sys_list *s_new, + const struct arch_def *arch, + const struct db_api_arg *api_arg, + struct db_arg_chain_tree *prev_nodes[4], + bool *tf_flag) { unsigned int iter; - struct db_sys_list *s_new; - const struct db_api_arg *chain = rule->args; struct db_arg_chain_tree *c_iter[3] = { NULL, NULL, NULL }; - struct db_arg_chain_tree *c_prev[3] = { NULL, NULL, NULL }; - enum scmp_compare op_prev = _SCMP_CMP_MIN; unsigned int arg; scmp_datum_t mask; scmp_datum_t datum; - s_new = zmalloc(sizeof(*s_new)); - if (s_new == NULL) - return NULL; - s_new->num = rule->syscall; - s_new->valid = true; - /* run through the argument chain */ - for (iter = 0; iter < ARG_COUNT_MAX; iter++) { - if (chain[iter].valid == 0) - continue; + if (api_arg->valid == 0) + return 0; - /* TODO: handle the case were either hi or lo isn't needed */ + /* TODO: handle the case were either hi or lo isn't needed */ - /* skip generating instruction which are no-ops */ - if (!_db_arg_cmp_need_hi(&chain[iter]) && - !_db_arg_cmp_need_lo(&chain[iter])) - continue; + /* skip generating instruction which are no-ops */ + if (!_db_arg_cmp_need_hi(api_arg) && + !_db_arg_cmp_need_lo(api_arg)) + return 0; - c_iter[0] = zmalloc(sizeof(*c_iter[0])); - if (c_iter[0] == NULL) - goto gen_64_failure; - c_iter[1] = zmalloc(sizeof(*c_iter[1])); - if (c_iter[1] == NULL) { - free(c_iter[0]); - goto gen_64_failure; - } - c_iter[2] = NULL; + c_iter[0] = zmalloc(sizeof(*c_iter[0])); + if (c_iter[0] == NULL) + return -1; + c_iter[1] = zmalloc(sizeof(*c_iter[1])); + if (c_iter[1] == NULL) { + free(c_iter[0]); + return -1; + } + c_iter[2] = zmalloc(sizeof(*c_iter[2])); + if (c_iter[2] == NULL) { + free(c_iter[1]); + free(c_iter[0]); + return -1; + } + + arg = api_arg->arg; + mask = api_arg->mask; + datum = api_arg->datum; - arg = chain[iter].arg; - mask = chain[iter].mask; - datum = chain[iter].datum; - - /* NOTE: with the idea that a picture is worth a thousand - * words, i'm presenting the following diagrams which - * show how we should compare 64-bit syscall arguments - * using 32-bit comparisons. - * - * in the diagrams below "A(x)" is the syscall argument - * being evaluated and "R(x)" is the syscall argument - * value specified in the libseccomp rule. the "ACCEPT" - * verdict indicates a rule match and processing should - * continue on to the rest of the rule, or the final rule - * action should be triggered. the "REJECT" verdict - * indicates that the rule does not match and processing - * should continue to the next rule or the default - * action. - * - * SCMP_CMP_GT: - * +------------------+ - * +--| Ah(x) > Rh(x) |------+ - * | +------------------+ | - * FALSE TRUE A - * | | C - * +-----------+ +----> C - * v +----> E - * +------------------+ | P - * +--| Ah(x) == Rh(x) |--+ | T - * R | +------------------+ | | - * E FALSE TRUE | - * J <----+ | | - * E <----+ +------------+ | - * C FALSE v | - * T | +------------------+ | - * +--| Al(x) > Rl(x) |------+ - * +------------------+ - * - * SCMP_CMP_GE: - * +------------------+ - * +--| Ah(x) > Rh(x) |------+ - * | +------------------+ | - * FALSE TRUE A - * | | C - * +-----------+ +----> C - * v +----> E - * +------------------+ | P - * +--| Ah(x) == Rh(x) |--+ | T - * R | +------------------+ | | - * E FALSE TRUE | - * J <----+ | | - * E <----+ +------------+ | - * C FALSE v | - * T | +------------------+ | - * +--| Al(x) >= Rl(x) |------+ - * +------------------+ - * - * SCMP_CMP_LT: - * +------------------+ - * +--| Ah(x) > Rh(x) |------+ - * | +------------------+ | - * FALSE TRUE R - * | | E - * +-----------+ +----> J - * v +----> E - * +------------------+ | C - * +--| Ah(x) == Rh(x) |--+ | T - * A | +------------------+ | | - * C FALSE TRUE | - * C <----+ | | - * E <----+ +------------+ | - * P FALSE v | - * T | +------------------+ | - * +--| Al(x) >= Rl(x) |------+ - * +------------------+ - * - * SCMP_CMP_LE: - * +------------------+ - * +--| Ah(x) > Rh(x) |------+ - * | +------------------+ | - * FALSE TRUE R - * | | E - * +-----------+ +----> J - * v +----> E - * +------------------+ | C - * +--| Ah(x) == Rh(x) |--+ | T - * A | +------------------+ | | - * C FALSE TRUE | - * C <----+ | | - * E <----+ +------------+ | - * P FALSE v | - * T | +------------------+ | - * +--| Al(x) > Rl(x) |------+ - * +------------------+ - * - * SCMP_CMP_EQ: - * +------------------+ - * +--| Ah(x) == Rh(x) |--+ - * R | +------------------+ | A - * E FALSE TRUE C - * J <----+ | C - * E <----+ +------------+ +----> E - * C FALSE v | P - * T | +------------------+ | T - * +--| Al(x) == Rl(x) |------+ - * +------------------+ - * - * SCMP_CMP_NE: - * +------------------+ - * +--| Ah(x) == Rh(x) |--+ - * A | +------------------+ | R - * C FALSE TRUE E - * C <----+ | J - * E <----+ +------------+ +----> E - * P FALSE v | C - * T | +------------------+ | T - * +--| Al(x) == Rl(x) |------+ - * +------------------+ - * - */ - - /* setup the level */ - switch (chain[iter].op) { + /* link this level to the previous level */ + for (iter = 0; prev_nodes[iter] != NULL; iter++) + if (*tf_flag) + prev_nodes[iter]->nxt_t = _db_node_get(c_iter[0]); + else + prev_nodes[iter]->nxt_f = _db_node_get(c_iter[0]); + prev_nodes[0] = prev_nodes[1] = prev_nodes[2] = prev_nodes[3] = NULL; + + /* NOTE: with the idea that a picture is worth a thousand + * words, i'm presenting the following diagrams which + * show how we should compare 64-bit syscall arguments + * using 32-bit comparisons. + * + * in the diagrams below "A(x)" is the syscall argument + * being evaluated and "R(x)" is the syscall argument + * value specified in the libseccomp rule. the "ACCEPT" + * verdict indicates a rule match and processing should + * continue on to the rest of the rule, or the final rule + * action should be triggered. the "REJECT" verdict + * indicates that the rule does not match and processing + * should continue to the next rule or the default + * action. + * + * SCMP_CMP_GT: + * +------------------+ + * +--| Ah(x) > Rh(x) |------+ + * | +------------------+ | + * FALSE TRUE A + * | | C + * +-----------+ +----> C + * v +----> E + * +------------------+ | P + * +--| Ah(x) == Rh(x) |--+ | T + * R | +------------------+ | | + * E FALSE TRUE | + * J <----+ | | + * E <----+ +------------+ | + * C FALSE v | + * T | +------------------+ | + * +--| Al(x) > Rl(x) |------+ + * +------------------+ + * + * SCMP_CMP_GE: + * +------------------+ + * +--| Ah(x) > Rh(x) |------+ + * | +------------------+ | + * FALSE TRUE A + * | | C + * +-----------+ +----> C + * v +----> E + * +------------------+ | P + * +--| Ah(x) == Rh(x) |--+ | T + * R | +------------------+ | | + * E FALSE TRUE | + * J <----+ | | + * E <----+ +------------+ | + * C FALSE v | + * T | +------------------+ | + * +--| Al(x) >= Rl(x) |------+ + * +------------------+ + * + * SCMP_CMP_LT: + * +------------------+ + * +--| Ah(x) > Rh(x) |------+ + * | +------------------+ | + * FALSE TRUE R + * | | E + * +-----------+ +----> J + * v +----> E + * +------------------+ | C + * +--| Ah(x) == Rh(x) |--+ | T + * A | +------------------+ | | + * C FALSE TRUE | + * C <----+ | | + * E <----+ +------------+ | + * P FALSE v | + * T | +------------------+ | + * +--| Al(x) >= Rl(x) |------+ + * +------------------+ + * + * SCMP_CMP_LE: + * +------------------+ + * +--| Ah(x) > Rh(x) |------+ + * | +------------------+ | + * FALSE TRUE R + * | | E + * +-----------+ +----> J + * v +----> E + * +------------------+ | C + * +--| Ah(x) == Rh(x) |--+ | T + * A | +------------------+ | | + * C FALSE TRUE | + * C <----+ | | + * E <----+ +------------+ | + * P FALSE v | + * T | +------------------+ | + * +--| Al(x) > Rl(x) |------+ + * +------------------+ + * + * SCMP_CMP_EQ: + * +------------------+ + * +--| Ah(x) == Rh(x) |--+ + * R | +------------------+ | A + * E FALSE TRUE C + * J <----+ | C + * E <----+ +------------+ +----> E + * C FALSE v | P + * T | +------------------+ | T + * +--| Al(x) == Rl(x) |------+ + * +------------------+ + * + * SCMP_CMP_NE: + * +------------------+ + * +--| Ah(x) == Rh(x) |--+ + * A | +------------------+ | R + * C FALSE TRUE E + * C <----+ | J + * E <----+ +------------+ +----> E + * P FALSE v | C + * T | +------------------+ | T + * +--| Al(x) == Rl(x) |------+ + * +------------------+ + * + */ + + /* setup the level */ + switch (api_arg->op) { + case SCMP_CMP_GT: + case SCMP_CMP_GE: + case SCMP_CMP_LE: + case SCMP_CMP_LT: + c_iter[0]->arg = arg; + c_iter[1]->arg = arg; + c_iter[2]->arg = arg; + c_iter[0]->arg_h_flg = true; + c_iter[1]->arg_h_flg = true; + c_iter[2]->arg_h_flg = false; + c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg); + c_iter[1]->arg_offset = arch_arg_offset_hi(arch, arg); + c_iter[2]->arg_offset = arch_arg_offset_lo(arch, arg); + + c_iter[0]->mask = D64_HI(mask); + c_iter[1]->mask = D64_HI(mask); + c_iter[2]->mask = D64_LO(mask); + c_iter[0]->datum = D64_HI(datum); + c_iter[1]->datum = D64_HI(datum); + c_iter[2]->datum = D64_LO(datum); + c_iter[0]->datum_full = datum; + c_iter[1]->datum_full = datum; + c_iter[2]->datum_full = datum; + + _db_node_mask_fixup(c_iter[0]); + _db_node_mask_fixup(c_iter[1]); + _db_node_mask_fixup(c_iter[2]); + + c_iter[0]->op = SCMP_CMP_GT; + c_iter[1]->op = SCMP_CMP_EQ; + switch (api_arg->op) { case SCMP_CMP_GT: - case SCMP_CMP_GE: case SCMP_CMP_LE: - case SCMP_CMP_LT: - c_iter[2] = zmalloc(sizeof(*c_iter[2])); - if (c_iter[2] == NULL) { - free(c_iter[0]); - free(c_iter[1]); - goto gen_64_failure; - } - - c_iter[0]->arg = arg; - c_iter[1]->arg = arg; - c_iter[2]->arg = arg; - c_iter[0]->arg_h_flg = true; - c_iter[1]->arg_h_flg = true; - c_iter[2]->arg_h_flg = false; - c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg); - c_iter[1]->arg_offset = arch_arg_offset_hi(arch, arg); - c_iter[2]->arg_offset = arch_arg_offset_lo(arch, arg); - - c_iter[0]->mask = D64_HI(mask); - c_iter[1]->mask = D64_HI(mask); - c_iter[2]->mask = D64_LO(mask); - c_iter[0]->datum = D64_HI(datum); - c_iter[1]->datum = D64_HI(datum); - c_iter[2]->datum = D64_LO(datum); - c_iter[0]->datum_full = datum; - c_iter[1]->datum_full = datum; - c_iter[2]->datum_full = datum; - - _db_node_mask_fixup(c_iter[0]); - _db_node_mask_fixup(c_iter[1]); - _db_node_mask_fixup(c_iter[2]); - - c_iter[0]->op = SCMP_CMP_GT; - c_iter[1]->op = SCMP_CMP_EQ; - switch (chain[iter].op) { - case SCMP_CMP_GT: - case SCMP_CMP_LE: - c_iter[2]->op = SCMP_CMP_GT; - break; - case SCMP_CMP_GE: - case SCMP_CMP_LT: - c_iter[2]->op = SCMP_CMP_GE; - break; - default: - /* we should never get here */ - goto gen_64_failure; - } - c_iter[0]->op_orig = chain[iter].op; - c_iter[1]->op_orig = chain[iter].op; - c_iter[2]->op_orig = chain[iter].op; - - c_iter[0]->nxt_f = _db_node_get(c_iter[1]); - c_iter[1]->nxt_t = _db_node_get(c_iter[2]); + c_iter[2]->op = SCMP_CMP_GT; break; - case SCMP_CMP_EQ: - case SCMP_CMP_MASKED_EQ: - case SCMP_CMP_NE: - c_iter[0]->arg = arg; - c_iter[1]->arg = arg; - c_iter[0]->arg_h_flg = true; - c_iter[1]->arg_h_flg = false; - c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg); - c_iter[1]->arg_offset = arch_arg_offset_lo(arch, arg); - - c_iter[0]->mask = D64_HI(mask); - c_iter[1]->mask = D64_LO(mask); - c_iter[0]->datum = D64_HI(datum); - c_iter[1]->datum = D64_LO(datum); - c_iter[0]->datum_full = datum; - c_iter[1]->datum_full = datum; - - _db_node_mask_fixup(c_iter[0]); - _db_node_mask_fixup(c_iter[1]); - - switch (chain[iter].op) { - case SCMP_CMP_MASKED_EQ: - c_iter[0]->op = SCMP_CMP_MASKED_EQ; - c_iter[1]->op = SCMP_CMP_MASKED_EQ; - break; - default: - c_iter[0]->op = SCMP_CMP_EQ; - c_iter[1]->op = SCMP_CMP_EQ; - } - c_iter[0]->op_orig = chain[iter].op; - c_iter[1]->op_orig = chain[iter].op; - - c_iter[0]->nxt_t = _db_node_get(c_iter[1]); + case SCMP_CMP_GE: + case SCMP_CMP_LT: + c_iter[2]->op = SCMP_CMP_GE; break; default: /* we should never get here */ - goto gen_64_failure; + return -1; } + c_iter[0]->op_orig = api_arg->op; + c_iter[1]->op_orig = api_arg->op; + c_iter[2]->op_orig = api_arg->op; - /* link this level to the previous level */ - if (c_prev[0] != NULL) { - switch (op_prev) { - case SCMP_CMP_GT: - case SCMP_CMP_GE: - c_prev[0]->nxt_t = _db_node_get(c_iter[0]); - c_prev[2]->nxt_t = _db_node_get(c_iter[0]); - break; - case SCMP_CMP_EQ: - case SCMP_CMP_MASKED_EQ: - c_prev[1]->nxt_t = _db_node_get(c_iter[0]); - break; - case SCMP_CMP_LE: - case SCMP_CMP_LT: - c_prev[1]->nxt_f = _db_node_get(c_iter[0]); - c_prev[2]->nxt_f = _db_node_get(c_iter[0]); - break; - case SCMP_CMP_NE: - c_prev[0]->nxt_f = _db_node_get(c_iter[0]); - c_prev[1]->nxt_f = _db_node_get(c_iter[0]); - break; - default: - /* we should never get here */ - goto gen_64_failure; - } - } else - s_new->chains = _db_node_get(c_iter[0]); - - /* update the node count */ - switch (chain[iter].op) { - case SCMP_CMP_NE: - case SCMP_CMP_EQ: + c_iter[0]->nxt_f = _db_node_get(c_iter[1]); + c_iter[1]->nxt_t = _db_node_get(c_iter[2]); + break; + case SCMP_CMP_EQ: + case SCMP_CMP_MASKED_EQ: + case SCMP_CMP_NE: + free(c_iter[2]); + c_iter[2] = NULL; + c_iter[0]->arg = arg; + c_iter[1]->arg = arg; + c_iter[0]->arg_h_flg = true; + c_iter[1]->arg_h_flg = false; + c_iter[0]->arg_offset = arch_arg_offset_hi(arch, arg); + c_iter[1]->arg_offset = arch_arg_offset_lo(arch, arg); + + c_iter[0]->mask = D64_HI(mask); + c_iter[1]->mask = D64_LO(mask); + c_iter[0]->datum = D64_HI(datum); + c_iter[1]->datum = D64_LO(datum); + c_iter[0]->datum_full = datum; + c_iter[1]->datum_full = datum; + + _db_node_mask_fixup(c_iter[0]); + _db_node_mask_fixup(c_iter[1]); + + switch (api_arg->op) { case SCMP_CMP_MASKED_EQ: - s_new->node_cnt += 2; + c_iter[0]->op = SCMP_CMP_MASKED_EQ; + c_iter[1]->op = SCMP_CMP_MASKED_EQ; break; default: - s_new->node_cnt += 3; + c_iter[0]->op = SCMP_CMP_EQ; + c_iter[1]->op = SCMP_CMP_EQ; } + c_iter[0]->op_orig = api_arg->op; + c_iter[1]->op_orig = api_arg->op; - /* keep pointers to this level */ - c_prev[0] = c_iter[0]; - c_prev[1] = c_iter[1]; - c_prev[2] = c_iter[2]; - op_prev = chain[iter].op; + c_iter[0]->nxt_t = _db_node_get(c_iter[1]); + break; + default: + /* we should never get here */ + return -1; } - if (c_iter[0] != NULL) { - /* set the actions on the last layer */ - switch (op_prev) { - case SCMP_CMP_GT: - case SCMP_CMP_GE: - c_iter[0]->act_t_flg = true; - c_iter[0]->act_t = rule->action; - c_iter[2]->act_t_flg = true; - c_iter[2]->act_t = rule->action; - break; - case SCMP_CMP_LE: - case SCMP_CMP_LT: - c_iter[1]->act_f_flg = true; - c_iter[1]->act_f = rule->action; - c_iter[2]->act_f_flg = true; - c_iter[2]->act_f = rule->action; - break; - case SCMP_CMP_EQ: - case SCMP_CMP_MASKED_EQ: - c_iter[1]->act_t_flg = true; - c_iter[1]->act_t = rule->action; - break; - case SCMP_CMP_NE: - c_iter[0]->act_f_flg = true; - c_iter[0]->act_f = rule->action; - c_iter[1]->act_f_flg = true; - c_iter[1]->act_f = rule->action; - break; - default: - /* we should never get here */ - goto gen_64_failure; - } - } else - s_new->action = rule->action; - return s_new; + /* Allow the next level to link to the new nodes */ + if (s_new->chains == NULL) + s_new->chains = _db_node_get(c_iter[0]); + switch (api_arg->op) { + case SCMP_CMP_GT: + case SCMP_CMP_GE: + prev_nodes[0] = c_iter[0]; + prev_nodes[1] = c_iter[2]; + *tf_flag = true; + break; + case SCMP_CMP_EQ: + case SCMP_CMP_MASKED_EQ: + prev_nodes[0] = c_iter[1]; + *tf_flag = true; + break; + case SCMP_CMP_LE: + case SCMP_CMP_LT: + prev_nodes[0] = c_iter[1]; + prev_nodes[1] = c_iter[2]; + *tf_flag = false; + break; + case SCMP_CMP_NE: + prev_nodes[0] = c_iter[0]; + prev_nodes[1] = c_iter[1]; + *tf_flag = false; + break; + default: + /* we should never get here */ + return -1; + } -gen_64_failure: - /* free the new chain and its syscall struct */ - _db_tree_put(&s_new->chains); - free(s_new); - return NULL; + /* update the node count */ + switch (api_arg->op) { + case SCMP_CMP_NE: + case SCMP_CMP_EQ: + case SCMP_CMP_MASKED_EQ: + s_new->node_cnt += 2; + break; + default: + s_new->node_cnt += 3; + } + + return 0; +} + +/** + * Generate tree nodes for a 32 bit argument comparison + * @param s_new the syscall filter + * @param arch the architecture definition + * @param api_arg the argument comparison + * @param prev_nodes a NULL terminated array of tree nodes to link to + * @param tf_flag whether to link to the nxt_t or nxt_f branch of prev_nodes + * + * This function generates tree nodes representing a 32 bit argument + * comparison. Returns zero on success, negative values on failure. + * + */ +static int _db_rule_gen_arg_32(struct db_sys_list *s_new, + const struct arch_def *arch, + const struct db_api_arg *api_arg, + struct db_arg_chain_tree *prev_nodes[4], + bool *tf_flag) +{ + struct db_arg_chain_tree *c_iter = NULL; + unsigned int iter; + + if (api_arg->valid == 0) + return 0; + + /* skip generating instructions which are no-ops */ + if (!_db_arg_cmp_need_lo(api_arg)) + return 0; + + c_iter = zmalloc(sizeof(*c_iter)); + if (c_iter == NULL) + return -1; + + c_iter->arg = api_arg->arg; + c_iter->arg_h_flg = false; + c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg); + c_iter->op = api_arg->op; + c_iter->op_orig = api_arg->op; + /* implicitly strips off the upper 32 bit */ + c_iter->mask = api_arg->mask; + c_iter->datum = api_arg->datum; + c_iter->datum_full = api_arg->datum; + + /* link in the new node and update the chain */ + for (iter = 0; prev_nodes[iter] != NULL; iter++) + if (*tf_flag) + prev_nodes[iter]->nxt_t = _db_node_get(c_iter); + else + prev_nodes[iter]->nxt_f = _db_node_get(c_iter); + prev_nodes[0] = c_iter; + prev_nodes[1] = NULL; + if (s_new->chains == NULL) + s_new->chains = _db_node_get(c_iter); + s_new->node_cnt++; + + /* rewrite the op to reduce the op/datum combos */ + switch (c_iter->op) { + case SCMP_CMP_NE: + c_iter->op = SCMP_CMP_EQ; + *tf_flag = false; + break; + case SCMP_CMP_LT: + c_iter->op = SCMP_CMP_GE; + *tf_flag = false; + break; + case SCMP_CMP_LE: + c_iter->op = SCMP_CMP_GT; + tf_flag = false; + break; + default: + *tf_flag = true; + } + + /* fixup the mask/datum */ + _db_node_mask_fixup(c_iter); + + return 0; } /** - * Generate a new filter rule for a 32 bit system + * Generate a new filter rule * @param arch the architecture definition * @param rule the new filter rule * - * This function generates a new syscall filter for a 32 bit system. Returns - * zero on success, negative values on failure. + * This function generates a new syscall filter. Returns + * an allocated db_sys_struct on success, NULL on failure. * */ -static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch, - const struct db_api_rule_list *rule) +static struct db_sys_list *_db_rule_gen(const struct arch_def *arch, + const struct db_api_rule_list *rule) { unsigned int iter; struct db_sys_list *s_new; const struct db_api_arg *chain = rule->args; - struct db_arg_chain_tree *c_iter = NULL, *c_prev = NULL; - bool tf_flag; + struct db_arg_chain_tree *prev_nodes[4] = { NULL, }; + bool arch_32bit = arch->size == ARCH_SIZE_32; + bool tf_flag = false; s_new = zmalloc(sizeof(*s_new)); if (s_new == NULL) @@ -1969,74 +2007,32 @@ static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch, s_new->valid = true; /* run through the argument chain */ for (iter = 0; iter < ARG_COUNT_MAX; iter++) { - if (chain[iter].valid == 0) - continue; - - /* skip generating instructions which are no-ops */ - if (!_db_arg_cmp_need_lo(&chain[iter])) - continue; - - c_iter = zmalloc(sizeof(*c_iter)); - if (c_iter == NULL) - goto gen_32_failure; - c_iter->arg = chain[iter].arg; - c_iter->arg_h_flg = false; - c_iter->arg_offset = arch_arg_offset(arch, c_iter->arg); - c_iter->op = chain[iter].op; - c_iter->op_orig = chain[iter].op; - /* implicitly strips off the upper 32 bit */ - c_iter->mask = chain[iter].mask; - c_iter->datum = chain[iter].datum; - c_iter->datum_full = chain[iter].datum; - - /* link in the new node and update the chain */ - if (c_prev != NULL) { - if (tf_flag) - c_prev->nxt_t = _db_node_get(c_iter); - else - c_prev->nxt_f = _db_node_get(c_iter); - } else - s_new->chains = _db_node_get(c_iter); - s_new->node_cnt++; - - /* rewrite the op to reduce the op/datum combos */ - switch (c_iter->op) { - case SCMP_CMP_NE: - c_iter->op = SCMP_CMP_EQ; - tf_flag = false; - break; - case SCMP_CMP_LT: - c_iter->op = SCMP_CMP_GE; - tf_flag = false; - break; - case SCMP_CMP_LE: - c_iter->op = SCMP_CMP_GT; - tf_flag = false; - break; - default: - tf_flag = true; + if (arch_32bit || chain[iter].is_32bit) { + if (_db_rule_gen_arg_32(s_new, arch, &chain[iter], + prev_nodes, &tf_flag) < 0) + goto gen_failure; + } else { + if (_db_rule_gen_arg_64(s_new, arch, &chain[iter], + prev_nodes, &tf_flag) < 0) + goto gen_failure; } - - /* fixup the mask/datum */ - _db_node_mask_fixup(c_iter); - - c_prev = c_iter; } - if (c_iter != NULL) { - /* set the leaf node */ - if (tf_flag) { - c_iter->act_t_flg = true; - c_iter->act_t = rule->action; - } else { - c_iter->act_f_flg = true; - c_iter->act_f = rule->action; + if (s_new->chains != NULL) { + for (iter = 0; prev_nodes[iter] != NULL; iter++) { + if (tf_flag) { + prev_nodes[iter]->act_t_flg = true; + prev_nodes[iter]->act_t = rule->action; + } else { + prev_nodes[iter]->act_f_flg = true; + prev_nodes[iter]->act_f = rule->action; + } } } else s_new->action = rule->action; return s_new; -gen_32_failure: +gen_failure: /* free the new chain and its syscall struct */ _db_tree_put(&s_new->chains); free(s_new); @@ -2071,12 +2067,9 @@ int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule) /* do all our possible memory allocation up front so we don't have to * worry about failure once we get to the point where we start updating * the filter db */ - if (db->arch->size == ARCH_SIZE_64) - s_new = _db_rule_gen_64(db->arch, rule); - else if (db->arch->size == ARCH_SIZE_32) - s_new = _db_rule_gen_32(db->arch, rule); - else + if (db->arch->size != ARCH_SIZE_64 && db->arch->size != ARCH_SIZE_32) return -EFAULT; + s_new = _db_rule_gen(db->arch, rule); if (s_new == NULL) return -ENOMEM; @@ -2316,7 +2309,8 @@ int db_col_rule_add(struct db_filter_col *col, if (arg_num < ARG_COUNT_MAX && chain[arg_num].valid == 0) { chain[arg_num].valid = 1; chain[arg_num].arg = arg_num; - chain[arg_num].op = arg_data.op; + chain[arg_num].op = arg_data.op & SCMP_CMP_OPMASK; + chain[arg_num].is_32bit = (arg_data.op & SCMP_CMP_32BIT) != 0; /* TODO: we should check datum/mask size against the * arch definition, e.g. 64 bit datum on x86 */ switch (chain[arg_num].op) { @@ -2337,6 +2331,11 @@ int db_col_rule_add(struct db_filter_col *col, rc = -EINVAL; goto add_return; } + /* Check that no unknown flags are specified in the op */ + if ((arg_data.op & ~(SCMP_CMP_OPMASK | SCMP_CMP_32BIT)) != 0) { + rc = -EINVAL; + goto add_return; + } } else { rc = -EINVAL; goto add_return; diff --git a/src/db.h b/src/db.h index 765c607e..ea1e133f 100644 --- a/src/db.h +++ b/src/db.h @@ -38,6 +38,7 @@ struct db_api_arg { scmp_datum_t datum; bool valid; + bool is_32bit; }; struct db_api_rule_list { diff --git a/src/python/libseccomp.pxd b/src/python/libseccomp.pxd index 6175c8ae..e1a13d88 100644 --- a/src/python/libseccomp.pxd +++ b/src/python/libseccomp.pxd @@ -72,6 +72,7 @@ cdef extern from "seccomp.h": SCMP_CMP_GE SCMP_CMP_GT SCMP_CMP_MASKED_EQ + SCMP_CMP_32BIT cdef enum: SCMP_ACT_KILL_PROCESS diff --git a/src/python/seccomp.pyx b/src/python/seccomp.pyx index 3551aace..41b3b33b 100644 --- a/src/python/seccomp.pyx +++ b/src/python/seccomp.pyx @@ -133,6 +133,7 @@ EQ = libseccomp.SCMP_CMP_EQ GE = libseccomp.SCMP_CMP_GE GT = libseccomp.SCMP_CMP_GT MASKED_EQ = libseccomp.SCMP_CMP_MASKED_EQ +CMP_32BIT = libseccomp.SCMP_CMP_32BIT def system_arch(): """ Return the system architecture value. diff --git a/tests/.gitignore b/tests/.gitignore index 249acc91..bdb61d74 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -67,3 +67,4 @@ util.pyc 57-basic-rawsysrc 58-live-tsync_notify 59-basic-empty_binary_tree +60-sim-32b_args_on_64b diff --git a/tests/60-sim-32b_args_on_64b.c b/tests/60-sim-32b_args_on_64b.c new file mode 100644 index 00000000..9923e226 --- /dev/null +++ b/tests/60-sim-32b_args_on_64b.c @@ -0,0 +1,86 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2022 Canonical Ltd. + * Author: James Henstridge + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include +#include + +#include + +#include "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; + + rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1001, 1, + SCMP_A0(SCMP_CMP_NE | SCMP_CMP_32BIT, 0x10)); + if (rc != 0) + goto out; + + rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1002, 1, + SCMP_A0(SCMP_CMP_LT | SCMP_CMP_32BIT, 0x10)); + if (rc != 0) + goto out; + + rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1003, 1, + SCMP_A0(SCMP_CMP_LE | SCMP_CMP_32BIT, 0x10)); + if (rc != 0) + goto out; + + rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1004, 1, + SCMP_A0(SCMP_CMP_EQ | SCMP_CMP_32BIT, 0x10)); + if (rc != 0) + goto out; + + rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1005, 1, + SCMP_A0(SCMP_CMP_GE | SCMP_CMP_32BIT, 0x10)); + if (rc != 0) + goto out; + + rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1006, 1, + SCMP_A0(SCMP_CMP_GT | SCMP_CMP_32BIT, 0x10)); + if (rc != 0) + goto out; + + rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, 1007, 1, + SCMP_A0(SCMP_CMP_MASKED_EQ | SCMP_CMP_32BIT, 0xff, 0x10)); + 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/60-sim-32b_args_on_64b.py b/tests/60-sim-32b_args_on_64b.py new file mode 100755 index 00000000..3952379f --- /dev/null +++ b/tests/60-sim-32b_args_on_64b.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +# +# Seccomp Library test program +# +# Copyright (c) 2022 Canonical Ltd. +# Author: James Henstridge +# + +# +# 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 . +# + +import argparse +import sys + +import util + +from seccomp import * + +def test(args): + f = SyscallFilter(KILL) + f.add_rule_exactly(ALLOW, 1001, Arg(0, NE | CMP_32BIT, 0x10)) + f.add_rule_exactly(ALLOW, 1002, Arg(0, LT | CMP_32BIT, 0x10)) + f.add_rule_exactly(ALLOW, 1003, Arg(0, LE | CMP_32BIT, 0x10)) + f.add_rule_exactly(ALLOW, 1004, Arg(0, EQ | CMP_32BIT, 0x10)) + f.add_rule_exactly(ALLOW, 1005, Arg(0, GE | CMP_32BIT, 0x10)) + f.add_rule_exactly(ALLOW, 1006, Arg(0, GT | CMP_32BIT, 0x10)) + f.add_rule_exactly(ALLOW, 1007, Arg(0, MASKED_EQ | CMP_32BIT, 0xff, 0x10)) + return f + +args = util.get_opt() +ctx = test(args) +util.filter_output(args, ctx) + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/tests/60-sim-32b_args_on_64b.tests b/tests/60-sim-32b_args_on_64b.tests new file mode 100644 index 00000000..03359462 --- /dev/null +++ b/tests/60-sim-32b_args_on_64b.tests @@ -0,0 +1,54 @@ +# +# libseccomp regression test automation data +# +# Copyright (c) 2022 Canonical Ltd. +# Author: James Henstridge +# + +test type: bpf-sim + +# Testname Arch Syscall Arg0 Arg1 Arg2 Arg3 Arg4 Arg5 Result +60-sim-32b_args_on_64b all 1001 0x1 N N N N N ALLOW +60-sim-32b_args_on_64b all 1001 0x10 N N N N N KILL +60-sim-32b_args_on_64b all 1001 0xffffffff00000001 N N N N N ALLOW +60-sim-32b_args_on_64b all 1001 0xffffffff00000010 N N N N N KILL + +60-sim-32b_args_on_64b all 1002 0x1 N N N N N ALLOW +60-sim-32b_args_on_64b all 1002 0x20 N N N N N KILL +60-sim-32b_args_on_64b all 1002 0xffffffff00000001 N N N N N ALLOW +60-sim-32b_args_on_64b all 1002 0xffffffff00000020 N N N N N KILL + +60-sim-32b_args_on_64b all 1003 0x10 N N N N N ALLOW +60-sim-32b_args_on_64b all 1003 0x20 N N N N N KILL +60-sim-32b_args_on_64b all 1003 0xffffffff00000010 N N N N N ALLOW +60-sim-32b_args_on_64b all 1003 0xffffffff00000020 N N N N N KILL + +60-sim-32b_args_on_64b all 1004 0x10 N N N N N ALLOW +60-sim-32b_args_on_64b all 1004 0x20 N N N N N KILL +60-sim-32b_args_on_64b all 1004 0xffffffff00000010 N N N N N ALLOW +60-sim-32b_args_on_64b all 1004 0xffffffff00000020 N N N N N KILL + +60-sim-32b_args_on_64b all 1005 0x10 N N N N N ALLOW +60-sim-32b_args_on_64b all 1005 0x01 N N N N N KILL +60-sim-32b_args_on_64b all 1005 0xffffffff00000010 N N N N N ALLOW +60-sim-32b_args_on_64b all 1005 0xffffffff00000001 N N N N N KILL + +60-sim-32b_args_on_64b all 1006 0x20 N N N N N ALLOW +60-sim-32b_args_on_64b all 1006 0x01 N N N N N KILL +60-sim-32b_args_on_64b all 1006 0xffffffff00000020 N N N N N ALLOW +60-sim-32b_args_on_64b all 1006 0xffffffff00000001 N N N N N KILL + +60-sim-32b_args_on_64b all 1007 0xff10 N N N N N ALLOW +60-sim-32b_args_on_64b all 1007 0x01 N N N N N KILL +60-sim-32b_args_on_64b all 1007 0xffffffff0000ff10 N N N N N ALLOW +60-sim-32b_args_on_64b all 1007 0xffffffff00000001 N N N N N KILL + +test type: bpf-sim-fuzz + +# Testname StressCount +60-sim-32b_args_on_64b 5 + +test type: bpf-valgrind + +# Testname +60-sim-32b_args_on_64b diff --git a/tests/Makefile.am b/tests/Makefile.am index f0a1f8eb..ab9fc38d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -94,7 +94,8 @@ check_PROGRAMS = \ 56-basic-iterate_syscalls \ 57-basic-rawsysrc \ 58-live-tsync_notify \ - 59-basic-empty_binary_tree + 59-basic-empty_binary_tree \ + 60-sim-32b_args_on_64b EXTRA_DIST_TESTPYTHON = \ util.py \ @@ -154,7 +155,8 @@ EXTRA_DIST_TESTPYTHON = \ 56-basic-iterate_syscalls.py \ 57-basic-rawsysrc.py \ 58-live-tsync_notify.py \ - 59-basic-empty_binary_tree.py + 59-basic-empty_binary_tree.py \ + 60-sim-32b_args_on_64b.tests.py EXTRA_DIST_TESTCFGS = \ 01-sim-allow.tests \ @@ -215,7 +217,8 @@ EXTRA_DIST_TESTCFGS = \ 56-basic-iterate_syscalls.tests \ 57-basic-rawsysrc.tests \ 58-live-tsync_notify.tests \ - 59-basic-empty_binary_tree.tests + 59-basic-empty_binary_tree.tests \ + 60-sim-32b_args_on_64b.tests EXTRA_DIST_TESTSCRIPTS = \ 38-basic-pfc_coverage.sh 38-basic-pfc_coverage.pfc \