From c38fccbc867019d6c063be1c1d8137edfe52f8de Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Wed, 7 Jun 2023 23:02:23 +0200 Subject: mknod/mknodat values, initial support for MASK flag, OP_BITWISE Signed-off-by: Stefano Brivio --- common/gluten.h | 26 ++++++++++++++++------- common/util.c | 2 ++ cooker/call.c | 41 ++++++++++++++++++++++++++---------- cooker/calls/fs.c | 63 ++++++++++++++++++++++++++++++++++++++++++++----------- cooker/cooker.h | 1 + cooker/emit.c | 56 ++++++++++++++++++++++++++++++------------------- cooker/emit.h | 9 +++++--- cooker/match.c | 44 ++++++++++++++++++++++++++------------ demo/mknod.hjson | 33 +++++++++++++++++++++++------ operations.c | 35 ++++++++++++++++++------------- operations.h | 2 ++ 11 files changed, 225 insertions(+), 87 deletions(-) diff --git a/common/gluten.h b/common/gluten.h index 2f82509..101b92e 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -45,6 +45,8 @@ enum gluten_offset_type { OFFSET_TYPE_MAX = OFFSET_INSTRUCTION, }; +#define NULL_OFFSET ((struct gluten_offset){ .type = OFFSET_NULL }) + extern const char *gluten_offset_name[OFFSET_TYPE_MAX + 1]; struct gluten_offset { @@ -66,7 +68,7 @@ enum op_type { OP_FD, OP_RETURN, OP_LOAD, - OP_MASK, + OP_BITWISE, OP_CMP, OP_RESOLVEDFD, }; @@ -210,15 +212,24 @@ struct op_cmp { struct gluten_offset desc; /* struct cmp_desc */ }; -struct mask_desc { +enum bitwise_type { + BITWISE_AND, + BITWISE_OR, + BITWISE_MAX = BITWISE_OR, +}; + +extern const char *bitwise_type_str[BITWISE_MAX + 1]; + +struct bitwise_desc { size_t size; + enum bitwise_type type; struct gluten_offset dst; - struct gluten_offset src; - struct gluten_offset mask; + struct gluten_offset x; + struct gluten_offset y; }; -struct op_mask { - struct gluten_offset desc; /* struct mask_desc */ +struct op_bitwise { + struct gluten_offset desc; /* struct bitwise_desc */ }; struct resolvefd_desc { @@ -246,7 +257,7 @@ struct op { struct op_return ret; struct op_fd fd; struct op_load load; - struct op_mask mask; + struct op_bitwise bitwise; struct op_cmp cmp; struct op_resolvefd resfd; struct op_copy copy; @@ -352,6 +363,7 @@ static inline int gluten_write(struct gluten *g, struct gluten_offset dst, const void *src, size_t size) { void *p = gluten_write_ptr(g, dst); + if (p == NULL || !check_gluten_limits(dst, size)) return -1; memcpy(p, src, size); diff --git a/common/util.c b/common/util.c index 4cb7b7a..a03b73e 100644 --- a/common/util.c +++ b/common/util.c @@ -37,3 +37,5 @@ const char *gluten_offset_name[OFFSET_TYPE_MAX + 1] = { const char *ns_type_name[NS_TYPE_MAX + 1] = { "mnt", "cgroup", "uts", "ipc", "user", "pid", "net", "time", }; + +const char *bitwise_type_str[BITWISE_MAX + 1] = { "&", "|" }; diff --git a/cooker/call.c b/cooker/call.c index 7aef157..7ae3d48 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -113,10 +113,14 @@ static union value parse_field(struct gluten_ctx *g, struct arg *args, if (tag_offset.type == OFFSET_NULL) die(" tag not found"); - if (base_offset->type == OFFSET_NULL) + if (base_offset->type == OFFSET_NULL) { *base_offset = tag_offset; - else + } else if (f->flags & MASK || add) { + emit_bitwise(g, f->type, BITWISE_OR, offset, + offset, tag_offset); + } else { emit_copy_field(g, f, offset, tag_offset); + } } if (json_object_get_count(tmp2) > count) @@ -252,9 +256,10 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, case INT: case LONG: case U32: + return false; case GNU_DEV_MAJOR: case GNU_DEV_MINOR: - return false; + return true; case SELECT: f_inner = f->desc.d_select->field; if (arg_needs_temp(f_inner, pos, jvalue, top_level_tag, level)) @@ -300,7 +305,7 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, } static struct gluten_offset parse_arg(struct gluten_ctx *g, struct arg *args, - struct arg *a, + struct arg *a, bool multi_field, struct gluten_offset offset, JSON_Value *jvalue) { @@ -314,10 +319,15 @@ static struct gluten_offset parse_arg(struct gluten_ctx *g, struct arg *args, return offset; } - if (arg_needs_temp(&a->f, a->pos, jvalue, &top_level_tag, 0)) - offset = gluten_rw_alloc(g, a->f.size); - else if (a->f.size && !top_level_tag) + if (arg_needs_temp(&a->f, a->pos, jvalue, &top_level_tag, 0) || + multi_field) { + if (a->f.size) + offset = gluten_rw_alloc(g, a->f.size); + else + offset = gluten_rw_alloc_type(g, a->f.type); + } else if ((a->f.size && !top_level_tag)) { offset = gluten_ro_alloc(g, a->f.size); + } parse_field(g, args, &offset, a->pos, &a->f, jvalue, false, false); return offset; @@ -336,6 +346,8 @@ static void parse_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, struct { bool needs_fd; bool has_fd; + bool found; + bool multi_field; } arg_check[6] = { 0 }; int arg_max_pos = -1; unsigned count = 0; @@ -349,6 +361,10 @@ static void parse_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, a = g->selected_arg[a->pos]; } + if (arg_check[a->pos].found) + arg_check[a->pos].multi_field = true; + arg_check[a->pos].found = true; + if (a->f.type == FDPATH) arg_check[a->pos].needs_fd = true; @@ -362,10 +378,13 @@ static void parse_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, /* TODO: Factor this out into a function in... parse.c? */ for (a = args; a->f.name; a++) { JSON_Value *jvalue; + bool multi_field; if (a->f.type == SELECTED) a = g->selected_arg[a->pos]; + multi_field = arg_check[a->pos].multi_field; + /* Not common with parse_match(), though */ if ((jvalue = json_object_get_value(obj, a->f.name))) { if (arg_check[a->pos].has_fd) @@ -373,15 +392,15 @@ static void parse_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, else if (arg_check[a->pos].needs_fd) arg_check[a->pos].has_fd = true; - offset[a->pos] = parse_arg(g, args, a, offset[a->pos], - jvalue); + offset[a->pos] = parse_arg(g, args, a, multi_field, + offset[a->pos], jvalue); count++; } else if (arg_check[a->pos].needs_fd && arg_check[a->pos].has_fd) { ; } else if (a->f.flags & SIZE) { - offset[a->pos] = parse_arg(g, args, a, offset[a->pos], - jvalue); + offset[a->pos] = parse_arg(g, args, a, multi_field, + offset[a->pos], jvalue); } else { die(" No specification for argument %s", a->f.name); } diff --git a/cooker/calls/fs.c b/cooker/calls/fs.c index cfc0091..469dd88 100644 --- a/cooker/calls/fs.c +++ b/cooker/calls/fs.c @@ -58,13 +58,44 @@ swapoff #include #include -#include +#define _GNU_SOURCE #include +#include #include #include "../cooker.h" #include "../calls.h" +static struct num modes[] = { + { "S_ISUID", S_ISUID }, + { "S_ISGID", S_ISGID }, + { "S_IRWXU", S_IRWXU }, + { "S_IRUSR", S_IRUSR }, + { "S_IWUSR", S_IWUSR }, + { "S_IXUSR", S_IXUSR }, + { "S_IRWXG", S_IRWXG }, + { "S_IRGRP", S_IRGRP }, + { "S_IWGRP", S_IWGRP }, + { "S_IXGRP", S_IXGRP }, + { "S_IRWXO", S_IRWXO }, + { "S_IROTH", S_IROTH }, + { "S_IWOTH", S_IWOTH }, + { "S_IXOTH", S_IXOTH }, + { "S_ISVTX", S_ISVTX }, + { 0 }, +}; + +static struct num types[] = { + { "S_IFSOCK", S_IFSOCK }, + { "S_IFLNK", S_IFLNK }, + { "S_IFREG", S_IFREG }, + { "S_IFBLK", S_IFBLK }, + { "S_IFDIR", S_IFDIR }, + { "S_IFCHR", S_IFCHR }, + { "S_IFIFO", S_IFIFO }, + { 0 }, +}; + static struct arg mknod_args[] = { { 0, { @@ -75,9 +106,16 @@ static struct arg mknod_args[] = { }, { 1, { - "mode", U32 /* TODO */, 0, + "mode", INT, FLAGS | MASK, 0, 0, - { 0 /* TODO */ }, + { .d_num = modes }, + } + }, + { 1, + { + "type", INT, FLAGS | MASK, + 0, 0, + { .d_num = types }, } }, { 2, @@ -98,13 +136,7 @@ static struct arg mknod_args[] = { }; static struct arg mknodat_args[] = { - { 0, - { - "dirfd", UNDEF, 0, - 0, 1 /* TODO: PATH_MAX */, - { 0 } - } - }, + /* No dirfd: we only support absolute paths at the moment */ { 1, { "path", STRING, 0, @@ -114,9 +146,16 @@ static struct arg mknodat_args[] = { }, { 2, { - "mode", UNDEF /* TODO */, FLAGS, + "mode", INT, FLAGS | MASK, + 0, 0, + { .d_num = modes }, + } + }, + { 2, + { + "type", INT, FLAGS | MASK, 0, 0, - { 0 /* TODO */ }, + { .d_num = types }, } }, { 3, diff --git a/cooker/cooker.h b/cooker/cooker.h index b26ea2d..24d8f7f 100644 --- a/cooker/cooker.h +++ b/cooker/cooker.h @@ -18,6 +18,7 @@ #include #include +#include #include #define TAGS_MAX 256 diff --git a/cooker/emit.c b/cooker/emit.c index 506a561..107b2ce 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -198,40 +198,49 @@ void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd, } /** - * emit_mask(): Emit OP_MASK instruction: mask and store + * emit_bitwise(): Emit OP_BITWISE instruction: bitwise operation and store * @g: gluten context * @type: Type of operands - * @src: gluten pointer to source operand - * @mask: gluten pointer to mask + * @op_type: Type of bitwise operation + * @dst: gluten pointer to destination operand, can be OFFSET_NULL + * @x: gluten pointer to first source operand + * @y: gluten pointer to second source operand * - * Return: offset to destination operand, allocated here + * Return: offset to destination operand, allocated here if not given */ -struct gluten_offset emit_mask(struct gluten_ctx *g, enum type type, - struct gluten_offset src, - struct gluten_offset mask) +struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, + enum bitwise_type op_type, + struct gluten_offset dst, + struct gluten_offset x, + struct gluten_offset y) { struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); - struct op_mask *op_mask = &op->op.mask; + struct op_bitwise *op_bitwise = &op->op.bitwise; struct gluten_offset o; - struct mask_desc *desc; + struct bitwise_desc *desc; - op->type = OP_MASK; + op->type = OP_BITWISE; - o = gluten_ro_alloc(g, sizeof(struct mask_desc)); - desc = (struct mask_desc *)gluten_ptr(&g->g, o); + o = gluten_ro_alloc(g, sizeof(struct bitwise_desc)); + desc = (struct bitwise_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; + desc->type = op_type; + if (dst.type == OFFSET_NULL) + desc->dst = gluten_rw_alloc(g, desc->size); + else + desc->dst = dst; + desc->x = x; + desc->y = y; - op_mask->desc = o; + op_bitwise->desc = o; - debug(" %i: OP_MASK: %s: #%lu (size: %lu) := %s: #%lu & %s: #%lu", + debug(" %i: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %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); + gluten_offset_name[desc->dst.type], desc->dst.offset, desc->size, + gluten_offset_name[desc->x.type], desc->x.offset, + bitwise_type_str[op_type], + gluten_offset_name[desc->y.type], desc->y.offset); if (++g->ip.offset > INST_MAX) die("Too many instructions"); @@ -537,5 +546,10 @@ 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)); +} + +void link_matches(struct gluten_ctx *g) +{ + debug(" Linking matches..."); + gluten_link(g, JUMP_NEXT_ACTION, (struct op *)gluten_ptr(&g->g, g->lr)); } diff --git a/cooker/emit.h b/cooker/emit.h index 81f947e..978c9e0 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -13,10 +13,12 @@ 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); struct gluten_offset emit_seccomp_data(int index); +struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, + enum bitwise_type op_type, + struct gluten_offset dst, + struct gluten_offset x, + struct gluten_offset y); 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); @@ -43,5 +45,6 @@ struct gluten_offset emit_data_or(struct gluten_ctx *g, enum type type, union value *value); void link_block(struct gluten_ctx *g); void link_match(struct gluten_ctx *g); +void link_matches(struct gluten_ctx *g); #endif /* EMIT_H */ diff --git a/cooker/match.c b/cooker/match.c index 3c7650c..d32bf0a 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -78,7 +78,7 @@ static union value parse_field(struct gluten_ctx *g, enum op_cmp_type cmp, enum jump_type jump, int index, struct field *f, JSON_Value *jvalue) { - struct gluten_offset const_offset, mask_offset, data_offset, seccomp_offset; + struct gluten_offset const_offset, mask_offset, seccomp_offset; union value v = { .v_num = 0 }; struct field *f_inner; const char *tag_name; @@ -90,6 +90,32 @@ static union value parse_field(struct gluten_ctx *g, if (f->name) debug(" parsing field name %s", f->name); + /* Some types need pre-tagging preparation */ + switch (f->type) { + 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); + offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET, + offset, mask_offset); + 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); + offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET, + offset, mask_offset); + break; + default: + break; + } + if (json_value_get_type(jvalue) == JSONObject && (tmp = json_value_get_object(jvalue)) && (tag_name = json_object_get_string(tmp, "tag"))) { @@ -153,30 +179,20 @@ static union value parse_field(struct gluten_ctx *g, 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); + emit_cmp_field(g, cmp, f, 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); + emit_cmp_field(g, cmp, f, offset, const_offset, jump); break; case SELECT: f_inner = f->desc.d_select->field; @@ -329,4 +345,6 @@ void handle_matches(struct gluten_ctx *g, JSON_Value *value) link_match(g); } + + link_matches(g); } diff --git a/demo/mknod.hjson b/demo/mknod.hjson index b61fb7a..9660e0d 100644 --- a/demo/mknod.hjson +++ b/demo/mknod.hjson @@ -1,13 +1,34 @@ [ { "match": [ /* Giuseppe's example */ - { "mknodat": { "path": { "tag": "path" }, "mode": 8630, "major": 1, "minor": { "value": { "in": [ 3, 5, 7, 8, 9 ] }, "tag": "minor" } } } + { "mknodat": + { "path": { "tag": "path" }, + "mode": { "tag": "mode" }, + "type": { "tag": "type" }, + "major": 1, + "minor": { "value": { "in": [ 3, 5, 7, 8, 9 ] }, "tag": "minor" } + } + }, + { "mknod": + { "path": { "tag": "path" }, + "mode": { "tag": "mode" }, + "type": { "tag": "type" }, + "major": 1, + "minor": { "value": { "in": [ 3, 5, 7, 8, 9 ] }, "tag": "minor" } + } + } ], - "call": { - "mknod": { "path": { "tag": { "get": "path" } }, "mode": 8630, "major": 1, "minor": 7 /* { "tag": { "get": "minor" } }*/ }, - "ret": "x"/*, - "context": { "user": "init", "mnt": "caller" }*/ - }, + "call": + { "mknod": + { "path": { "tag": { "get": "path" } }, + "mode": { "tag": { "get": "mode" } }, + "type": { "tag": { "get": "type" } }, + "major": 1, + "minor": { "tag": { "get": "minor" } } + }, + "ret": "x"/*, + "context": { "user": "init", "mnt": "caller" }*/ + }, "return": { "tag": "x" } } ] diff --git a/operations.c b/operations.c index eefc746..120b142 100644 --- a/operations.c +++ b/operations.c @@ -322,11 +322,11 @@ 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) +int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g, + struct op_bitwise *op) { - const struct mask_desc *desc = gluten_ptr(&req->data, g, op->desc); - const unsigned char *src, *mask; + const struct bitwise_desc *desc = gluten_ptr(&req->data, g, op->desc); + const unsigned char *x, *y; unsigned char *dst; unsigned i; @@ -335,9 +335,9 @@ int op_mask(const struct seccomp_notif *req, int notifier, struct gluten *g, 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); + dst = gluten_write_ptr( g, desc->dst); + x = gluten_ptr(&req->data, g, desc->x); + y = gluten_ptr(&req->data, g, desc->y); /* if (!dst || !src || !mask || @@ -346,14 +346,21 @@ int op_mask(const struct seccomp_notif *req, int notifier, struct gluten *g, !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, + debug(" op_bitwise: dst=(%s %d) := x=(%s %d) %s y=(%s %d) size=%d", + gluten_offset_name[desc->dst.type], desc->dst.offset, + gluten_offset_name[desc->x.type], desc->x.offset, + bitwise_type_str[desc->type], + gluten_offset_name[desc->y.type], desc->y.offset, desc->size); - for (i = 0; i < desc->size; i++) - dst[i] = src[i] & mask[i]; + for (i = 0; i < desc->size; i++) { + if (desc->type == BITWISE_AND) + dst[i] = x[i] & y[i]; + else if (desc->type == BITWISE_OR) + dst[i] = x[i] | y[i]; + else + return -1; + } return 0; } @@ -465,7 +472,7 @@ int eval(struct gluten *g, const struct seccomp_notif *req, HANDLE_OP(OP_RETURN, op_return, ret, 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_BITWISE, op_bitwise, bitwise, 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); diff --git a/operations.h b/operations.h index f07b86f..ec06a15 100644 --- a/operations.h +++ b/operations.h @@ -46,6 +46,8 @@ int op_call(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_call *op); int op_return(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_return *op); +int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g, + struct op_bitwise *op); int op_cmp(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_cmp *op); int op_resolve_fd(const struct seccomp_notif *req, int notifier, -- cgit v1.2.3