From e5a1983e4384a44e45486fb9a48bdba375a529b6 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Tue, 6 Jun 2023 11:56:21 +0200 Subject: cooker: Draft quality: mknod/mknodat, sets of values with "in" While at it: - directly assign 'fd' in eater from install_filter() - turn op_cmp into a description-style thing Signed-off-by: Stefano Brivio --- common/gluten.h | 23 ++++++++++++-- cooker/call.c | 4 +++ cooker/calls/fs.c | 48 ++++++++++++++++++++++++++--- cooker/cooker.h | 8 ++++- cooker/emit.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++----- cooker/emit.h | 3 ++ cooker/example.hjson | 2 +- cooker/gluten.c | 5 ++- cooker/gluten.h | 1 + cooker/match.c | 73 ++++++++++++++++++++++++++++++++++++++----- eater/eater.c | 5 +-- operations.c | 76 ++++++++++++++++++++++++++++++++++++--------- 12 files changed, 293 insertions(+), 42 deletions(-) diff --git a/common/gluten.h b/common/gluten.h index 53782db..edb447c 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -68,6 +68,7 @@ enum op_type { OP_FD, OP_RETURN, OP_LOAD, + OP_MASK, OP_CMP, OP_RESOLVEDFD, }; @@ -193,14 +194,29 @@ enum op_cmp_type { CMP_LE, }; -struct op_cmp { +struct cmp_desc { + enum op_cmp_type cmp; + size_t size; struct gluten_offset x; struct gluten_offset y; - size_t size; - enum op_cmp_type cmp; struct gluten_offset jmp; }; +struct op_cmp { + struct gluten_offset desc; /* struct cmp_desc */ +}; + +struct mask_desc { + size_t size; + struct gluten_offset dst; + struct gluten_offset src; + struct gluten_offset mask; +}; + +struct op_mask { + struct gluten_offset desc; /* struct mask_desc */ +}; + struct op_resolvedfd { struct gluten_offset fd; struct gluten_offset path; @@ -223,6 +239,7 @@ struct op { struct op_return ret; struct op_fd fd; struct op_load load; + struct op_mask mask; struct op_cmp cmp; struct op_resolvedfd resfd; struct op_copy copy; diff --git a/cooker/call.c b/cooker/call.c index 1c29db8..289a0cb 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -143,6 +143,8 @@ static union value parse_field(struct gluten_ctx *g, struct arg *args, case INT: case LONG: case U32: + case GNU_DEV_MAJOR: + case GNU_DEV_MINOR: if (f->flags == SIZE) { v.v_num = value_get_size(g, f->desc.d_size); } else if (f->flags == FLAGS) { @@ -250,6 +252,8 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, case INT: case LONG: case U32: + case GNU_DEV_MAJOR: + case GNU_DEV_MINOR: return false; case SELECT: f_inner = f->desc.d_select->field; diff --git a/cooker/calls/fs.c b/cooker/calls/fs.c index 013dacf..cfc0091 100644 --- a/cooker/calls/fs.c +++ b/cooker/calls/fs.c @@ -69,27 +69,66 @@ static struct arg mknod_args[] = { { 0, { "path", STRING, 0, - 0, 1 /* TODO: PATH_MAX */, + 0, PATH_MAX, { 0 } } }, { 1, { - "mode", UNDEF /* TODO */, FLAGS, + "mode", U32 /* TODO */, 0, 0, 0, { 0 /* TODO */ }, } }, { 2, { - "major", UNDEF /* TODO */, 0, + "major", GNU_DEV_MAJOR, 0, 0, 0, { 0 }, } }, { 2, { - "minor", UNDEF /* TODO */, 0, + "minor", GNU_DEV_MINOR, 0, + 0, 0, + { 0 }, + } + }, + { 0 } +}; + +static struct arg mknodat_args[] = { + { 0, + { + "dirfd", UNDEF, 0, + 0, 1 /* TODO: PATH_MAX */, + { 0 } + } + }, + { 1, + { + "path", STRING, 0, + 0, PATH_MAX, + { 0 } + } + }, + { 2, + { + "mode", UNDEF /* TODO */, FLAGS, + 0, 0, + { 0 /* TODO */ }, + } + }, + { 3, + { + "major", GNU_DEV_MAJOR, 0, + 0, 0, + { 0 }, + } + }, + { 3, + { + "minor", GNU_DEV_MINOR, 0, 0, 0, { 0 }, } @@ -99,5 +138,6 @@ static struct arg mknod_args[] = { struct call syscalls_fs[] = { { __NR_mknod, "mknod", mknod_args }, + { __NR_mknodat, "mknodat", mknodat_args }, { 0 }, }; diff --git a/cooker/cooker.h b/cooker/cooker.h index 7a14221..b26ea2d 100644 --- a/cooker/cooker.h +++ b/cooker/cooker.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,9 @@ enum type { IPV4, IPV6, + GNU_DEV_MAJOR, + GNU_DEV_MINOR, + FDPATH, TYPE_END, @@ -108,7 +112,9 @@ enum flags { #define TYPE_IS_COMPOUND(t) \ ((t) == STRUCT || (t) == SELECT) #define TYPE_IS_NUM(t) \ - ((t) == INT || (t) == U32 || (t) == U64 || (t) == LONG || (t) == USHORT) + ((t) == USHORT || (t) == INT || (t) == U32 || \ + (t) == U64 || (t) == LONG || \ + (t) == GNU_DEV_MAJOR || (t) == GNU_DEV_MINOR) /** * struct num - A numeric value and its label diff --git a/cooker/emit.c b/cooker/emit.c index 4be5d35..d0928e3 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -18,9 +18,10 @@ static const char *type_str[] = { "UNDEF", "NONE", "USHORT", "INT", "U32", "U64", "LONG", "STRING", - "STRUCT", "SELECT", + "STRUCT", "SELECT", "SELECTED", "PID", "PORT", "IPV4", "IPV6", + "GNU_DEV_MAJOR", "GNU_DEV_MINOR", "FDPATH", NULL }; @@ -162,6 +163,48 @@ void emit_load(struct gluten_ctx *g, struct gluten_offset dst, die("Too many instructions"); } +/** + * emit_mask(): Emit OP_MASK instruction: mask and store + * @g: gluten context + * @type: Type of operands + * @src: gluten pointer to source operand + * @mask: gluten pointer to mask + * + * Return: offset to destination operand, allocated here + */ +struct gluten_offset emit_mask(struct gluten_ctx *g, enum type type, + struct gluten_offset src, + struct gluten_offset mask) +{ + struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); + struct op_mask *op_mask = &op->op.mask; + struct gluten_offset o; + struct mask_desc *desc; + + op->type = OP_MASK; + + o = gluten_ro_alloc(g, sizeof(struct mask_desc)); + desc = (struct mask_desc *)gluten_ptr(&g->g, o); + + desc->size = gluten_size[type]; + desc->dst = gluten_rw_alloc(g, desc->size); + desc->src = src; + desc->mask = mask; + + op_mask->desc = o; + + debug(" %i: OP_MASK: %s: #%lu (size: %lu) := %s: #%lu & %s: #%lu", + g->ip.offset, + gluten_offset_name[desc->dst.type], desc->dst.offset, desc->size, + gluten_offset_name[desc->src.type], desc->src.offset, + gluten_offset_name[desc->mask.type], desc->mask.offset); + + if (++g->ip.offset > INST_MAX) + die("Too many instructions"); + + return desc->dst; +} + /** * emit_cmp(): Emit OP_CMP instruction: compare data from two offsets * @g: gluten context @@ -177,15 +220,22 @@ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, { struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); struct op_cmp *cmp = &op->op.cmp; + struct gluten_offset o; + struct cmp_desc *desc; op->type = OP_CMP; - cmp->x = x; - cmp->y = y; - cmp->size = size; - cmp->cmp = cmp_type; - cmp->jmp.type = OFFSET_INSTRUCTION; - cmp->jmp.offset = jmp; + o = gluten_ro_alloc(g, sizeof(struct cmp_desc)); + desc = (struct cmp_desc *)gluten_ptr(&g->g, o); + + desc->x = x; + desc->y = y; + desc->size = size; + desc->cmp = cmp_type; + desc->jmp.type = OFFSET_INSTRUCTION; + desc->jmp.offset = jmp; + + cmp->desc = o; debug(" %i: OP_CMP: if %s: #%lu %s (size: %lu) %s: #%lu, jump to %s", g->ip.offset, @@ -333,6 +383,7 @@ static struct gluten_offset emit_data_do(struct gluten_ctx *g, switch (type) { case USHORT: case INT: + case U32: if (add) { *(int *)p |= value->v_int; debug(" C#%i |= (%s) %i", @@ -356,6 +407,18 @@ static struct gluten_offset emit_data_do(struct gluten_ctx *g, ret.offset, type_str[type], value->v_num); } + break; + case GNU_DEV_MAJOR: + *(unsigned long long int *)p |= makedev(value->v_num, 0); + debug(" C#%i |= (%s) %llu", + ret.offset, type_str[type], value->v_num); + + break; + case GNU_DEV_MINOR: + *(unsigned long long int *)p |= makedev(0, value->v_num); + debug(" C#%i |= (%s) %llu", + ret.offset, type_str[type], value->v_num); + break; case STRING: strncpy(p, value->v_str, str_len); @@ -400,6 +463,7 @@ static void gluten_link(struct gluten_ctx *g, enum jump_type type, struct op *start) { struct gluten_offset *jmp; + struct cmp_desc *desc; struct op *op; for (op = (struct op *)start; op->type; op++) { @@ -408,7 +472,9 @@ static void gluten_link(struct gluten_ctx *g, enum jump_type type, jmp = &op->op.nr.no_match; break; case OP_CMP: - jmp = &op->op.cmp.jmp; + desc = (struct cmp_desc *)gluten_ptr(&g->g, + op->op.cmp.desc); + jmp = &desc->jmp; break; default: continue; @@ -416,6 +482,10 @@ static void gluten_link(struct gluten_ctx *g, enum jump_type type, if (jmp->offset == type) { jmp->offset = g->ip.offset; + + if (jmp->offset >= INST_MAX) + die("jump after end of instruction area"); + debug(" linked jump of instruction #%i to #%i", op - (struct op *)g->g.inst, g->ip.offset); } @@ -432,4 +502,5 @@ void link_match(struct gluten_ctx *g) { debug(" Linking match..."); gluten_link(g, JUMP_NEXT_MATCH, (struct op *)gluten_ptr(&g->g, g->mr)); + gluten_link(g, JUMP_NEXT_ACTION, (struct op *)gluten_ptr(&g->g, g->mr)); } diff --git a/cooker/emit.h b/cooker/emit.h index 70ffd72..9ec64e0 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -13,6 +13,9 @@ void emit_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, struct gluten_offset offset[6], struct gluten_offset ret_offset); void emit_load(struct gluten_ctx *g, struct gluten_offset dst, int index, size_t len); +struct gluten_offset emit_mask(struct gluten_ctx *g, enum type type, + struct gluten_offset src, + struct gluten_offset mask); void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, struct gluten_offset x, struct gluten_offset y, size_t size, enum jump_type jmp); diff --git a/cooker/example.hjson b/cooker/example.hjson index b161449..9e08163 100644 --- a/cooker/example.hjson +++ b/cooker/example.hjson @@ -32,7 +32,7 @@ }, { "match": [ /* Giuseppe's example */ - { "mknod": { "path": { "tag": "path" }, "mode": "c", "major": 1, "minor": { "in": [ 3, 5, 7, 8, 9 ], "tag": "minor" } } } + { "mknodat": { "path": { "tag": "path" }, "mode": "c", "major": 1, "minor": { "value": { "in": [ 3, 5, 7, 8, 9 ] }, "tag": "minor" } } } ], "call": { "mknod": { "path": { "tag": { "get": "path" } }, "mode": "c", "major": 1, "minor": { "tag": { "get": "minor" } } }, diff --git a/cooker/gluten.c b/cooker/gluten.c index cb9ecfb..4d3aea5 100644 --- a/cooker/gluten.c +++ b/cooker/gluten.c @@ -24,9 +24,12 @@ size_t gluten_size[TYPE_COUNT] = { [IPV4] = sizeof(struct in_addr), [IPV6] = sizeof(struct in6_addr), + [GNU_DEV_MAJOR] = sizeof(unsigned long long int), + [GNU_DEV_MINOR] = sizeof(unsigned long long int), }; -const char *jump_name[JUMP_COUNT] = { "next block", "next match", "end" }; +const char *jump_name[JUMP_COUNT] = { "next block", "next match", "next action", + "end" }; /** * gluten_rw_alloc() - Allocate in temporary (seitan read-write) data area diff --git a/cooker/gluten.h b/cooker/gluten.h index 9474700..e0ea54d 100644 --- a/cooker/gluten.h +++ b/cooker/gluten.h @@ -45,6 +45,7 @@ struct gluten_ctx { enum jump_type { JUMP_NEXT_BLOCK, JUMP_NEXT_MATCH, + JUMP_NEXT_ACTION, JUMP_END, JUMP_COUNT, }; diff --git a/cooker/match.c b/cooker/match.c index c53a456..e9aed16 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -75,13 +75,15 @@ static struct gluten_offset arg_load(struct gluten_ctx *g, struct arg *a) */ static union value parse_field(struct gluten_ctx *g, struct gluten_offset offset, + enum op_cmp_type cmp, enum jump_type jump, int index, struct field *f, JSON_Value *jvalue) { - struct gluten_offset const_offset; + struct gluten_offset const_offset, mask_offset, data_offset; union value v = { .v_num = 0 }; struct field *f_inner; const char *tag_name; JSON_Object *tmp; + JSON_Array *set; JSON_Value *sel; if (f->name) @@ -96,6 +98,30 @@ static union value parse_field(struct gluten_ctx *g, jvalue = json_object_get_value(tmp, "value"); } + if (json_value_get_type(jvalue) == JSONObject && + (tmp = json_value_get_object(jvalue)) && + (set = json_object_get_array(tmp, "in"))) { + unsigned i, count = json_array_get_count(set); + + if (cmp != CMP_NE || jump != JUMP_NEXT_BLOCK) + die("unsupported nested set"); + + for (i = 0; i < count; i++) { + if (i == count - 1) { + cmp = CMP_NE; + jump = JUMP_NEXT_BLOCK; + } else { + cmp = CMP_EQ; + jump = JUMP_NEXT_ACTION; + } + + jvalue = json_array_get_value(set, i); + parse_field(g, offset, cmp, jump, index, f, jvalue); + } + + return v; /* No SELECT based on sets... of course */ + } + /* Nothing to match on: just store as reference */ if (!jvalue) return v; @@ -115,11 +141,41 @@ static union value parse_field(struct gluten_ctx *g, /* calculate mask first */ ; } - + /* Falls through */ v.v_num = value_get_num(f->desc.d_num, jvalue); const_offset = emit_data(g, f->type, 0, &v); - emit_cmp(g, CMP_NE, offset, const_offset, gluten_size[f->type], - JUMP_NEXT_BLOCK); + emit_cmp(g, cmp, offset, const_offset, gluten_size[f->type], + jump); + break; + case GNU_DEV_MAJOR: + /* +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx +______________________ _____________ + */ + v.v_num = ((long long)0xfff << 44) | (0xfff << 8); + mask_offset = emit_data(g, U64, 0, &v); + + v.v_num = value_get_num(f->desc.d_num, jvalue); + v.v_num = (v.v_num & 0xfff) << 8 | (v.v_num & ~0xfff) << 32; + const_offset = emit_data(g, U64, 0, &v); + + data_offset = emit_mask(g, U64, offset, mask_offset); + emit_cmp_field(g, cmp, f, data_offset, const_offset, jump); + break; + case GNU_DEV_MINOR: + /* +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx + ____________________________ ________ + */ + v.v_num = 0xff | ((long long)0xffffff << 12); + mask_offset = emit_data(g, U64, 0, &v); + + v.v_num = value_get_num(f->desc.d_num, jvalue); + v.v_num = (v.v_num & 0xff) | (v.v_num & ~0xfff) << 12; + const_offset = emit_data(g, U64, 0, &v); + + data_offset = emit_mask(g, U64, offset, mask_offset); + emit_cmp_field(g, cmp, f, data_offset, const_offset, jump); break; case SELECT: f_inner = f->desc.d_select->field; @@ -131,11 +187,11 @@ static union value parse_field(struct gluten_ctx *g, sel = jvalue; } - v = parse_field(g, offset, index, f_inner, sel); + v = parse_field(g, offset, cmp, jump, index, f_inner, sel); f = select_field(g, index, f->desc.d_select, v); if (f) - parse_field(g, offset, index, f, jvalue); + parse_field(g, offset, cmp, jump, index, f, jvalue); break; case STRING: v.v_str = json_value_get_string(jvalue); @@ -155,7 +211,8 @@ static union value parse_field(struct gluten_ctx *g, if (!field_value) continue; - parse_field(g, offset, index, f_inner, field_value); + parse_field(g, offset, cmp, jump, index, f_inner, + field_value); } break; default: @@ -179,7 +236,7 @@ static void parse_arg(struct gluten_ctx *g, JSON_Value *jvalue, struct arg *a) offset = arg_load(g, a); - parse_field(g, offset, a->pos, &a->f, jvalue); + parse_field(g, offset, CMP_NE, JUMP_NEXT_BLOCK, a->pos, &a->f, jvalue); } /** diff --git a/eater/eater.c b/eater/eater.c index 567bffa..af1df5f 100644 --- a/eater/eater.c +++ b/eater/eater.c @@ -98,18 +98,19 @@ int main(int argc, char **argv) n = read(fd, filter, sizeof(filter)); close(fd); - install_filter(filter, (unsigned short)(n / sizeof(filter[0]))); + fd = install_filter(filter, (unsigned short)(n / sizeof(filter[0]))); + /* * close-on-exec flag is set for the file descriptor by seccomp. * We want to preserve the fd on the exec in this way we are able * to easly find the notifier fd if seitan restarts. */ - fd = find_fd_seccomp_notifier("/proc/self/fd"); flags = fcntl(fd, F_GETFD); if (fcntl(fd, F_SETFD, flags & !FD_CLOEXEC) < 0) { perror("fcntl"); exit(EXIT_FAILURE); } + act.sa_handler = signal_handler; sigaction(SIGCONT, &act, NULL); pause(); diff --git a/operations.c b/operations.c index ba9820b..78206bd 100644 --- a/operations.c +++ b/operations.c @@ -349,31 +349,77 @@ int op_fd(const struct seccomp_notif *req, int notifier, return 0; } +int op_mask(const struct seccomp_notif *req, int notifier, struct gluten *g, + struct op_mask *op) +{ + const struct mask_desc *desc = gluten_ptr(&req->data, g, op->desc); + const unsigned char *src, *mask; + unsigned char *dst; + unsigned i; + + (void)notifier; + + if (!desc) + return -1; + + dst = gluten_write_ptr( g, desc->dst); + src = gluten_ptr(&req->data, g, desc->src); + mask = gluten_ptr(&req->data, g, desc->mask); + +/* + if (!dst || !src || !mask || + !check_gluten_limits(desc->dst, desc->size) || + !check_gluten_limits(desc->src, desc->size) || + !check_gluten_limits(desc->mask, desc->size)) + return -1; +*/ + debug(" op_mask: dst=(%s %d) src=(%s %d) mask=(%s %d) size=%d", + gluten_offset_name[desc->dst.type], desc->dst.offset, + gluten_offset_name[desc->src.type], desc->src.offset, + gluten_offset_name[desc->mask.type], desc->mask.offset, + desc->size); + + for (i = 0; i < desc->size; i++) + dst[i] = src[i] & mask[i]; + + return 0; +} + int op_cmp(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_cmp *op) { - const void *px = gluten_ptr(&req->data, g, op->x); - const void *py = gluten_ptr(&req->data, g, op->y); - enum op_cmp_type cmp = op->cmp; + const struct cmp_desc *desc = gluten_ptr(&req->data, g, op->desc); + enum op_cmp_type cmp; + const void *px, *py; int res; (void)notifier; - if (px == NULL || py == NULL || !check_gluten_limits(op->x, op->size) || - !check_gluten_limits(op->y, op->size)) + if (!desc) + return -1; + + px = gluten_ptr(&req->data, g, desc->x); + py = gluten_ptr(&req->data, g, desc->y); + cmp = desc->cmp; + + if (!px || !py || + !check_gluten_limits(desc->x, desc->size) || + !check_gluten_limits(desc->y, desc->size)) return -1; debug(" op_cmp: operands x=(%s %d) y=(%s %d) size=%d", - gluten_offset_name[op->x.type], op->x.offset, - gluten_offset_name[op->y.type], op->y.offset, op->size); - res = memcmp(px, py, op->size); - if ((res == 0 && (cmp == CMP_EQ || cmp == CMP_LE || cmp == CMP_GE)) || - (res < 0 && (cmp == CMP_LT || cmp == CMP_LE)) || - (res > 0 && (cmp == CMP_GT || cmp == CMP_GE)) || + gluten_offset_name[desc->x.type], desc->x.offset, + gluten_offset_name[desc->y.type], desc->y.offset, desc->size); + + res = memcmp(px, py, desc->size); + + if ((res == 0 && (cmp == CMP_EQ || cmp == CMP_LE || cmp == CMP_GE)) || + (res < 0 && (cmp == CMP_LT || cmp == CMP_LE)) || + (res > 0 && (cmp == CMP_GT || cmp == CMP_GE)) || (res != 0 && (cmp == CMP_NE))) { - debug(" op_cmp: successful comparison jump to %d", - op->jmp.offset); - return op->jmp.offset; + debug(" op_cmp: successful comparison, jump to %d", + desc->jmp.offset); + return desc->jmp.offset; } debug(" op_cmp: comparison is false"); @@ -435,6 +481,7 @@ int eval(struct gluten *g, const struct seccomp_notif *req, struct op *op = (struct op *)g->inst; while (op->type != OP_END) { + debug("at instruction %i", op - (struct op *)g->inst); switch (op->type) { HANDLE_OP(OP_CALL, op_call, call, g); HANDLE_OP(OP_BLOCK, op_block, block, g); @@ -442,6 +489,7 @@ int eval(struct gluten *g, const struct seccomp_notif *req, HANDLE_OP(OP_CONT, op_continue, NO_FIELD, g); HANDLE_OP(OP_FD, op_fd, fd, g); HANDLE_OP(OP_LOAD, op_load, load, g); + HANDLE_OP(OP_MASK, op_mask, mask, g); HANDLE_OP(OP_CMP, op_cmp, cmp, g); HANDLE_OP(OP_RESOLVEDFD, op_resolve_fd, resfd, g); HANDLE_OP(OP_NR, op_nr, nr, g); -- cgit v1.2.3