Skip to content

Commit

Permalink
Merge pull request #15 from sethterashima/user/rterashima/bitwise-or-…
Browse files Browse the repository at this point in the history
…support

Add bitwise or support
  • Loading branch information
happyCoder92 authored Aug 13, 2019
2 parents 0afa549 + 3d702c6 commit 74117f1
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 56 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,11 @@ arithmetic operators.
some_syscall(first_arg, my_arg_name) { first_arg == 42 && my_arg_name != 42 }
```

Bitwise and (`&`) operator can be used to test for flags.
Bitwise and (`&`) and or ('|') operators can be used to test for flags.

```
mmap { (prot & PROT_EXEC) == 0 }
mmap { (prot & PROT_EXEC) == 0 },
open { flags == O_RDONLY|O_CLOEXEC }
```

You don't have to declare arguments for well-known syscalls but can just use
Expand Down
13 changes: 13 additions & 0 deletions samples/sample_or_flags.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 64

POLICY sample {
ALLOW {
open {
(flags == O_RDWR|O_CREAT || flags == O_WRONLY|O_CREAT)
}
}
}

USE sample DEFAULT KILL
134 changes: 91 additions & 43 deletions src/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,14 @@ struct codegen_ctxt {
} cache[MAX_JUMP];
size_t cache_size;
} locations;
size_t max_stack_ptr;
};

static struct codegen_ctxt *context_create(void) {
struct codegen_ctxt *ctxt = calloc(1, sizeof(*ctxt));
ctxt->buffer.capacity = CODEGEN_INITAL_BUFFER_SIZE;
ctxt->buffer.data = calloc(ctxt->buffer.capacity, sizeof(*ctxt->buffer.data));
ctxt->max_stack_ptr = 0;
for (int i = 0; i <= ACTION_BASIC_MAX; ++i) {
ctxt->locations.basic_actions[i] = INVALID_LOCATION;
}
Expand Down Expand Up @@ -280,77 +282,112 @@ static int add_jump_set(struct codegen_ctxt *ctxt, __u32 what, int tloc,

#define ARG_WORD(arg, word) ((word == HIGH_WORD) ? ARG_HIGH(arg) : ARG_LOW(arg))
#define NUM_WORD(num, word) ((word == HIGH_WORD) ? NUM_HIGH(num) : NUM_LOW(num))

#define BPF_LOAD_ARCH \
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, arch))
#define BPF_LOAD_SYSCALL \
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, nr))
#define BPF_LOAD_ARG_WORD(arg, high) \
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, ARG_WORD(arg, high))

static uint32_t value_of(struct expr_tree *expr, int word);

static bool is_const_value(struct expr_tree *expr, int word) {
switch (expr->type) {
case EXPR_NUMBER:
return true;
case EXPR_VAR:
return false;
case EXPR_BIT_AND:
if (is_const_value(expr->right, word) &&
value_of(expr->right, word) == 0) {
return true;
}
if (is_const_value(expr->left, word) && value_of(expr->left, word) == 0) {
return true;
}
return false;
default:
ASSERT(0); // should not happen
}
return word == HIGH_WORD ? expr->high.is_const : expr->low.is_const;
}

static uint32_t value_of(struct expr_tree *expr, int word) {
return word == HIGH_WORD ? expr->high.value : expr->low.value;
}

static void cache_constants_by_word(struct expr_tree *expr, int word) {
struct cached_value *cached = (word == HIGH_WORD) ? &expr->high : &expr->low;
uint32_t clobber_value = 0;
switch (expr->type) {
case EXPR_NUMBER:
return NUM_WORD(expr->number, word);
cached->is_const = true;
cached->value = NUM_WORD(expr->number, word);
return;
case EXPR_VAR:
cached->is_const = false;
return;
case EXPR_BIT_OR:
clobber_value = UINT32_MAX;
/* fall-through */
case EXPR_BIT_AND:
cache_constants_by_word(expr->right, word);
cache_constants_by_word(expr->left, word);
if (is_const_value(expr->right, word) &&
value_of(expr->right, word) == 0) {
return 0;
value_of(expr->right, word) == clobber_value) {
cached->is_const = true;
cached->value = clobber_value;
} else if (is_const_value(expr->left, word) &&
value_of(expr->left, word) == clobber_value) {
cached->is_const = true;
cached->value = clobber_value;
} else {
cached->is_const = false;
}
if (is_const_value(expr->left, word) && value_of(expr->left, word) == 0) {
return 0;
}
// fall-through
return;
default:
ASSERT(0); // should not happen
ASSERT(expr->type >= EXPR_BINARY_MIN && expr->type <= EXPR_BINARY_MAX);
cache_constants_by_word(expr->right, word);
cache_constants_by_word(expr->left, word);
return;
}
}

static void cache_constants(struct expr_tree *expr) {
cache_constants_by_word(expr, HIGH_WORD);
cache_constants_by_word(expr, LOW_WORD);
}

// Returns 1 if we need to push the result of executing the right sub-tree onto
// the stack (as opposed to the index register) before evaluating the left
// sub-tree.
static bool should_use_stack(struct expr_tree *left) {
return left->type != EXPR_VAR;
}

static int generate_load(struct codegen_ctxt *ctxt, struct expr_tree *expr,
int word) {
int word, size_t stack_ptr) {
ASSERT(ctxt != NULL);
ASSERT(expr != NULL);

if (stack_ptr > ctxt->max_stack_ptr) {
ctxt->max_stack_ptr = stack_ptr;
}
if (is_const_value(expr, word)) {
ASSERT(0); /* valid but should not happen */
return ADD_INSTR(BPF_STMT(BPF_LD | BPF_IMM, value_of(expr, word)));
}

int op = BPF_OR;
uint32_t identity_element = 0;

switch (expr->type) {
case EXPR_VAR:
return ADD_INSTR(BPF_LOAD_ARG_WORD(expr->var, word));
case EXPR_BIT_AND:
op = BPF_AND;
identity_element = UINT32_MAX;
// fall-through
case EXPR_BIT_OR:
if (is_const_value(expr->right, word)) {
ADD_INSTR(
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, value_of(expr->right, word)));
return generate_load(ctxt, expr->left, word);
if (value_of(expr->right, word) != identity_element) {
ADD_INSTR(
BPF_STMT(BPF_ALU | op | BPF_K, value_of(expr->right, word)));
}
return generate_load(ctxt, expr->left, word, stack_ptr);
}
ADD_INSTR(BPF_STMT(BPF_ALU | BPF_AND | BPF_X, 0));
generate_load(ctxt, expr->left, word);
ADD_INSTR(BPF_STMT(BPF_MISC | BPF_TAX, 0));
return generate_load(ctxt, expr->right, word);
bool use_stack = should_use_stack(expr->left);
ADD_INSTR(BPF_STMT(BPF_ALU | op | BPF_X, 0));
if (use_stack) {
ADD_INSTR(BPF_STMT(BPF_LDX | BPF_MEM, stack_ptr));
generate_load(ctxt, expr->left, word, stack_ptr + 1);
ADD_INSTR(BPF_STMT(BPF_ST, stack_ptr));
} else {
generate_load(ctxt, expr->left, word, stack_ptr);
ADD_INSTR(BPF_STMT(BPF_MISC | BPF_TAX, 0));
}
return generate_load(ctxt, expr->right, word, stack_ptr);
default:
ASSERT(0); // should not happen
}
Expand Down Expand Up @@ -399,7 +436,6 @@ static int generate_cmp32(struct codegen_ctxt *ctxt, __u32 type,
ASSERT(expr != NULL);

int next, begin = CURRENT_LOC;

struct expr_tree *left = expr->left;
struct expr_tree *right = expr->right;

Expand Down Expand Up @@ -438,14 +474,21 @@ static int generate_cmp32(struct codegen_ctxt *ctxt, __u32 type,
if (is_const_value(right, word)) {
next = ADD_JUMP_K(type, value_of(right, word), tloc, floc);
if (load == ALWAYS || (load != NEVER && next > begin)) {
begin = next = generate_load(ctxt, left, word);
begin = next = generate_load(ctxt, left, word, 0);
}
} else {
next = ADD_JUMP_X(type, tloc, floc);
if (load == ALWAYS || (load != NEVER && next > begin)) {
generate_load(ctxt, left, word);
ADD_INSTR(BPF_STMT(BPF_MISC | BPF_TAX, 0));
begin = next = generate_load(ctxt, right, word);
bool use_stack = should_use_stack(left);
if (use_stack) {
ADD_INSTR(BPF_STMT(BPF_LDX | BPF_MEM, 0));
generate_load(ctxt, left, word, 1);
ADD_INSTR(BPF_STMT(BPF_ST, 0));
} else {
generate_load(ctxt, left, word, 0);
ADD_INSTR(BPF_STMT(BPF_MISC | BPF_TAX, 0));
}
begin = next = generate_load(ctxt, right, word, 0);
}
}

Expand Down Expand Up @@ -523,6 +566,7 @@ static int generate_action(struct codegen_ctxt *ctxt,
ASSERT(mapping->expr == NULL || mapping->expr->type == EXPR_TRUE);
last_loc = -mapping->action;
} else {
cache_constants(mapping->expr);
last_loc = generate_expr(ctxt, mapping->expr, -mapping->action, last_loc);
}
}
Expand Down Expand Up @@ -557,7 +601,7 @@ static void reverse_instruction_buffer(struct codegen_ctxt *ctxt) {
int compile_policy(struct kafel_ctxt *kafel_ctxt, struct sock_fprog *prog) {
ASSERT(kafel_ctxt != NULL);
ASSERT(prog != NULL);

int rv = 0;
if (kafel_ctxt->main_policy == NULL) {
kafel_ctxt->main_policy = policy_create("@main", NULL);
}
Expand Down Expand Up @@ -588,6 +632,10 @@ int compile_policy(struct kafel_ctxt *kafel_ctxt, struct sock_fprog *prog) {
*prog = ((struct sock_fprog){.filter = ctxt->buffer.data,
.len = ctxt->buffer.len});
ctxt->buffer.data = NULL;
if (ctxt->max_stack_ptr >= BPF_MEMWORDS) {
append_error(kafel_ctxt, "Required stack size exceeds available BPF memory\n");
rv = 1;
}
context_destroy(&ctxt);
return 0;
return rv;
}
21 changes: 20 additions & 1 deletion src/expression.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ static void expr_sort_operands(struct expr_tree *expr) {
[EXPR_GE] = EXPR_LE, [EXPR_GT] = EXPR_LT,
[EXPR_LE] = EXPR_GE, [EXPR_LT] = EXPR_GT,
[EXPR_EQ] = EXPR_EQ, [EXPR_NEQ] = EXPR_NEQ,
[EXPR_BIT_AND] = EXPR_BIT_AND};
[EXPR_BIT_OR] = EXPR_BIT_OR, [EXPR_BIT_AND] = EXPR_BIT_AND};
expr->type = swapped[expr->type];
SWAP(expr->left, expr->right);
}
Expand Down Expand Up @@ -168,6 +168,11 @@ static void expr_precompute_eliminate(struct expr_tree **expr) {
expr_destroy(&(*expr)->right);
*expr = (*expr)->left;
free(original_expr);
} else if ((*expr)->type == EXPR_BIT_OR) {
(*expr)->left->number |= (*expr)->right->number;
expr_destroy(&(*expr)->right);
*expr = (*expr)->left;
free(original_expr);
} else {
(*expr)->type = expr_eval((*expr)->type, (*expr)->left->number,
(*expr)->right->number);
Expand Down Expand Up @@ -213,6 +218,20 @@ static void expr_precompute_eliminate(struct expr_tree **expr) {
expr_destroy(&(*expr)->right);
}
break;
case EXPR_BIT_OR:
if ((*expr)->right->type == EXPR_NUMBER) {
if ((*expr)->right->number == UINT64_MAX) {
expr_destroy(&(*expr)->left);
expr_destroy(&(*expr)->right);
(*expr)->type = EXPR_NUMBER;
(*expr)->number = UINT64_MAX;
} else if ((*expr)->right->number == 0) {
expr_destroy(&(*expr)->right);
*expr = (*expr)->left;
free(original_expr);
}
}
break;
case EXPR_BIT_AND:
if ((*expr)->right->type == EXPR_NUMBER) {
if ((*expr)->right->number == 0) {
Expand Down
8 changes: 8 additions & 0 deletions src/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,17 @@ enum {
EXPR_LE,
EXPR_EQ,
EXPR_NEQ,
EXPR_BIT_OR,
EXPR_BIT_AND,
EXPR_BINARY_MAX = EXPR_BIT_AND,
EXPR_MAX = EXPR_BINARY_MAX,
};

struct cached_value {
bool is_const;
uint32_t value;
};

struct expr_tree {
int type;
union {
Expand All @@ -62,6 +68,8 @@ struct expr_tree {
struct expr_tree *right;
};
};
struct cached_value low;
struct cached_value high;
};

struct expr_tree *expr_create_number(uint64_t value);
Expand Down
25 changes: 15 additions & 10 deletions src/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ YY_DECL;
%type <syscall_desc> syscall_id
%type <syscall_nr> syscall

%type <expr> bool_expr or_bool_expr and_bool_expr primary_bool_expr masked_op
%type <expr> bool_expr or_bool_expr and_bool_expr primary_bool_expr bit_or_expr bit_and_expr
%type <expr> operand

%destructor { policy_destroy(&$$); } <policy>
Expand Down Expand Up @@ -383,22 +383,27 @@ and_bool_expr
primary_bool_expr
: '(' bool_expr ')' { $$ = $2; }
| '!' primary_bool_expr { $$ = expr_create_unary(EXPR_NOT, $2); }
| masked_op GT masked_op { $$ = expr_create_binary(EXPR_GT, $1, $3); }
| masked_op LT masked_op { $$ = expr_create_binary(EXPR_LT, $1, $3); }
| masked_op GE masked_op { $$ = expr_create_binary(EXPR_GE, $1, $3); }
| masked_op LE masked_op { $$ = expr_create_binary(EXPR_LE, $1, $3); }
| masked_op EQ masked_op { $$ = expr_create_binary(EXPR_EQ, $1, $3); }
| masked_op NEQ masked_op { $$ = expr_create_binary(EXPR_NEQ, $1, $3); }
| bit_or_expr GT bit_or_expr { $$ = expr_create_binary(EXPR_GT, $1, $3); }
| bit_or_expr LT bit_or_expr { $$ = expr_create_binary(EXPR_LT, $1, $3); }
| bit_or_expr GE bit_or_expr { $$ = expr_create_binary(EXPR_GE, $1, $3); }
| bit_or_expr LE bit_or_expr { $$ = expr_create_binary(EXPR_LE, $1, $3); }
| bit_or_expr EQ bit_or_expr { $$ = expr_create_binary(EXPR_EQ, $1, $3); }
| bit_or_expr NEQ bit_or_expr { $$ = expr_create_binary(EXPR_NEQ, $1, $3); }
;

masked_op
: operand BIT_AND operand { $$ = expr_create_binary(EXPR_BIT_AND, $1, $3); }
bit_or_expr
: bit_or_expr BIT_OR bit_and_expr { $$ = expr_create_binary(EXPR_BIT_OR, $1, $3); }
| bit_and_expr { $$ = $1; }
;

bit_and_expr
: bit_and_expr BIT_AND operand { $$ = expr_create_binary(EXPR_BIT_AND, $1, $3); }
| operand { $$ = $1; }
| '(' masked_op ')' { $$ = $2; }
;

operand
: NUMBER { $$ = expr_create_number($1); }
| '(' bit_or_expr ')' { $$ = $2; }
| IDENTIFIER
{
uint64_t value = 0;
Expand Down
Loading

0 comments on commit 74117f1

Please sign in to comment.