Skip to content
3 changes: 3 additions & 0 deletions include/seccomp.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still working my way through this PR, but I think we want to move SCMP_CMP_OPMASK and SCMP_CMP_32BIT out of the enum and have them be standalone preprocessor macros/constants.

Copy link
Copy Markdown
Member

@pcmoore pcmoore May 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess if we do that we need to add a "filler" value to cause the enum to be a certain size, which feels a little icky but I think it's okay if we make sure to represent all of the flag space, e.g.

enum scmp_compare {
	_SCMP_CMP_MIN = 0,
	SCMP_CMP_NE = 1,
	SCMP_CMP_LT = 2,
	SCMP_CMP_LE = 3,
	SCMP_CMP_EQ = 4,
	SCMP_CMP_GE = 5,
	SCMP_CMP_GT = 6,
	SCMP_CMP_MASKED_EQ = 7,
	_SCMP_CMP_MAX,
	_SCMP_CMP_OPMASK = 0x0000FFFF,
	_SCMP_CMP_FLAGMASK = 0xFFFF0000,
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... which somewhat questions the value of moving the flag definitions out of the enum and into their own macros/constants, but that still seems like a goodish idea to me at this point.

};

/**
Expand Down
143 changes: 81 additions & 62 deletions src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -1944,6 +1944,72 @@ static struct db_sys_list *_db_rule_gen_64(const struct arch_def *arch,
return NULL;
}

static int _db_rule_gen_arg_32(struct db_sys_list *s_new,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self as much as anything - I don't see this function having any code coverage even though it's getting called 500,000+ times.

https://coveralls.io/builds/49252361/source?filename=src%2Fdb.c#L1918

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think something is broken for me and coveralls.io, I'm not able to see the marked-up source anymore ... :/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, it looks like I just needed to re-auth to coveralls.io.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Back to the original comment ... yes, something is odd here, both the 64-bit and 32-bit variants of this function appear to have zero code coverage, which is not right ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function also needs a comment block at the top like all the other functions in libseccomp.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with the 64-bit variant, it looks like the comment block is added in 0936a72 ("db: merge _db_rule_gen_64 and _db_rule_gen_32"); it would be best to add the comment block in the first appearance of the function.

const struct arch_def *arch,
const struct db_api_arg *api_arg,
struct db_arg_chain_tree *prev_nodes[4],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking out-loud here, but I wonder if the readability would be better if the prev_nodes[] array was expanded into individual variables with more descriptive names?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this function only appears to ever use one prev_nodes[] array slot, right?

bool *tf_flag)
{
struct db_arg_chain_tree *c_iter = NULL;
struct db_arg_chain_tree **node;

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 (node = prev_nodes; *node != NULL; node++)
if (*tf_flag)
(*node)->nxt_t = _db_node_get(c_iter);
else
(*node)->nxt_f = _db_node_get(c_iter);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know braces ("{ ... }") aren't strictly required for this for-loop body, but please add them to help readability and prevent silly mistakes in the future.

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
* @param arch the architecture definition
Expand All @@ -1959,8 +2025,8 @@ static struct db_sys_list *_db_rule_gen_32(const struct arch_def *arch,
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, };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another note to self - the prev_nodes[] change will become important in later commits. This commit is (slightly) more than just a refactoring of code to a separate function.

Copy link
Copy Markdown
Member

@pcmoore pcmoore Jul 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fixed 4 is very "magic" too, which makes me uneasy. At the very least that needs a comment (why 4?), but a much better approach would be a macro definition and and comment with the macro.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I now understand where the 4 comes from, and to be fair we have similar constants in the current _db_rule_gen_64() function, but the important difference is that in the current implementation the magic numbers are contained as local variables within the function, here they are part of the function's signature and are thus much more important.

Copy link
Copy Markdown
Member

@pcmoore pcmoore Jul 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm hoping that as I get farther into this review we can revisit how this code was extracted, because I'm not really liking this right now.

bool tf_flag = false;

s_new = zmalloc(sizeof(*s_new));
if (s_new == NULL)
Expand All @@ -1969,67 +2035,19 @@ 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)
if (_db_rule_gen_arg_32(s_new, arch, &chain[iter],
prev_nodes, &tf_flag) < 0)
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;
}

/* 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;
Expand Down Expand Up @@ -2316,7 +2334,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) {
Expand Down
1 change: 1 addition & 0 deletions src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct db_api_arg {
scmp_datum_t datum;

bool valid;
bool is_32bit;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer to drop the is_32bit flag and just preserve the SCMP_CMP_XXX flags in the db_api_arg::op field; there aren't that many places that check the comparison op so masking them shouldn't be too terrible. If necessary we can always wrap it in a preprocessor macro, e.g.

#define CMP_OP(x) (x & __SCMP_CMP_OPMASK)
#define CMP_FLAGS(x) (x & __SCMP_CMP_FLAGMASK)

};

struct db_api_rule_list {
Expand Down