diff options
Diffstat (limited to 'cooker/emit.c')
-rw-r--r-- | cooker/emit.c | 150 |
1 files changed, 126 insertions, 24 deletions
diff --git a/cooker/emit.c b/cooker/emit.c index 8c35f1d..c233b0a 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -14,8 +14,10 @@ #include "emit.h" static const char *type_str[] = { + "UNDEF", "NONE", "INT", "INTMASK", "INTFLAGS", "U32", "U32MASK", "U32FLAGS", + "U64", "U64MASK", "U64FLAGS", "LONG", "LONGMASK", "LONGFLAGS", "STRING", "STRUCT", "SELECT", @@ -25,83 +27,183 @@ static const char *type_str[] = { NULL }; -static const char *cmp_type_str[] = { "EQ", "GT", "GE", "LT", "LE", NULL }; +static const char *cmp_type_str[] = { + "EQ", "NE", "GT", "GE", "LT", "LE", NULL +}; -void emit_nr(struct gluten_ctx *g, long number) +/** + * emit_nr() - Emit OP_NR instruction: jump on syscall mismatch + * @g: gluten context + * @number: Pointer to system call number + */ +void emit_nr(struct gluten_ctx *g, struct gluten_offset number) { - struct op_nr *nr = (struct op_nr *)gluten_ptr(&g->g, g->ip); + struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); + struct op_nr *nr = &op->op.nr; + + op->type = OP_NR; nr->nr = number; nr->no_match.type = OFFSET_INSTRUCTION; - nr->no_match.offset = NEXT_BLOCK; + nr->no_match.offset = JUMP_NEXT_BLOCK; - debug(" %i: OP_NR %li, < >", g->ip.offset, number); + debug(" %i: OP_NR: if syscall number is not %li, jump to %s", + g->ip.offset, number, jump_name[nr->no_match.offset]); if (++g->ip.offset > INST_MAX) die("Too many instructions"); } +/** + * emit_load() - Emit OP_LOAD instruction: dereference and copy syscall argument + * @g: gluten context + * @dst: gluten destination to copy dereferenced data + * @index: Index of system call argument + * @len: Length of data item pointed by reference + */ void emit_load(struct gluten_ctx *g, struct gluten_offset dst, int index, size_t len) { - struct op_load *load = (struct op_load *)gluten_ptr(&g->g, g->ip); + struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); + struct op_load *load = &op->op.load; + + op->type = OP_LOAD; load->src.type = OFFSET_SECCOMP_DATA; load->src.offset = index; load->dst = dst; - debug(" %i: OP_LOAD #%i < %i (%lu)", g->ip.offset, dst.offset, - index, len); + debug(" %i: OP_LOAD: #%i < args[%i] (size: %lu)", + g->ip.offset, dst.offset, index, len); if (++g->ip.offset > INST_MAX) die("Too many instructions"); } -void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, +/** + * emit_cmp(): Emit OP_CMP instruction: compare data from two offsets + * @g: gluten context + * @cmp_type: Type of comparison + * @x: gluten pointer to first operand of comparison + * @y: gluten pointer to second operand of comparison + * @size: Size of comparison + * @jmp: Jump direction if comparison is true + */ +void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, struct gluten_offset x, struct gluten_offset y, size_t size, enum jump_type jmp) { - struct op_cmp *op = (struct op_cmp *)gluten_ptr(&g->g, g->ip); + struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); + struct op_cmp *cmp = &op->op.cmp; - op->x = x; - op->y = y; - op->size = size; - op->cmp = cmp; - op->jmp = jmp; + op->type = OP_CMP; - debug(" %i: OP_CMP (#%lu) %%%lu %s %%%lu", g->ip.offset, size, - x.offset, cmp_type_str[cmp], y.offset); + cmp->x = x; + cmp->y = y; + cmp->size = size; + cmp->cmp = cmp_type; + cmp->jmp.type = OFFSET_INSTRUCTION; + cmp->jmp.offset = jmp; + + debug(" %i: OP_CMP: if %s: #%lu %s (size: %lu) %s: #%lu, jump to %s", + g->ip.offset, + gluten_offset_name[x.type], x.offset, + cmp_type_str[cmp_type], size, + gluten_offset_name[y.type], y.offset, + jump_name[jmp]); if (++g->ip.offset > INST_MAX) die("Too many instructions"); } +/** + * emit_cmp_field() - Emit OP_CMP for a given field type + * @g: gluten context + * @cmp: Type of comparison + * @field: Description of field from system call model + * @x: gluten pointer to first operand of comparison + * @y: gluten pointer to second operand of comparison + * @jmp: Jump direction if comparison is true + */ void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, struct field *field, - struct gluten_offset base, struct gluten_offset match, + struct gluten_offset x, struct gluten_offset y, enum jump_type jmp) { - base.offset += field->offset; - - emit_cmp(g, cmp, base, match, + emit_cmp(g, cmp, x, y, field->strlen ? field->strlen : gluten_size[field->type], jmp); } struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, - union value *value) + size_t str_len, union value *value) { void *p = gluten_ptr(&g->g, g->cp); struct gluten_offset ret = g->cp; - if (type == INT) { + switch (type) { + case INT: + if (g->cp.offset + sizeof(int) > RO_DATA_SIZE) + die(" Read-only data section exceeded"); + *(int *)p = value->v_int; debug(" C#%i: (%s) %i", g->cp.offset, type_str[type], value->v_int); - if ((g->cp.offset += sizeof(int)) > RO_DATA_SIZE) + + g->cp.offset += sizeof(int); + break; + case STRING: + if (g->cp.offset + str_len > RO_DATA_SIZE) die(" Read-only data section exceeded"); + + strncpy(p, value->v_str, str_len); + debug(" C#%i: (%s:%i) %s", g->cp.offset, type_str[type], + str_len, value->v_str); + + g->cp.offset += str_len; + break; + default: + ; } return ret; } + +static void gluten_link(struct gluten_ctx *g, enum jump_type type, + struct op *start) +{ + struct gluten_offset *jmp; + struct op *op; + + for (op = (struct op *)start; op->type; op++) { + switch (op->type) { + case OP_NR: + jmp = &op->op.nr.no_match; + break; + case OP_CMP: + jmp = &op->op.cmp.jmp; + break; + default: + continue; + } + + if (jmp->offset == type) { + jmp->offset = g->ip.offset; + debug(" linked jump of instruction #%i to #%i", + op - (struct op *)g->g.inst, g->ip.offset); + } + } +} + +void link_block(struct gluten_ctx *g) +{ + debug(" Linking block..."); + gluten_link(g, JUMP_NEXT_BLOCK, (struct op *)gluten_ptr(&g->g, g->lr)); +} + +void link_match(struct gluten_ctx *g) +{ + debug(" Linking match..."); + gluten_link(g, JUMP_NEXT_MATCH, (struct op *)gluten_ptr(&g->g, g->mr)); +} |