From 15b54482241083d52b6e9857a66fecbf915d467d Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Thu, 8 Jun 2023 20:05:18 +0200 Subject: cooker: Full support for flags and masks, assorted fixes Signed-off-by: Stefano Brivio --- common/gluten.h | 2 +- cooker/call.c | 29 +++++++++++++++--------- cooker/calls/ioctl.c | 2 +- cooker/calls/net.c | 8 +++---- cooker/calls/process.c | 20 +++++++++++++++-- cooker/emit.c | 14 ++++++------ cooker/example.hjson | 24 ++++++++++++-------- cooker/match.c | 61 ++++++++++++++++++++++++++++++++++++++++---------- cooker/parse.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ cooker/parse.h | 4 ++++ demo/socket.hjson | 11 +++++++++ operations.c | 10 ++++----- 12 files changed, 193 insertions(+), 52 deletions(-) create mode 100644 demo/socket.hjson diff --git a/common/gluten.h b/common/gluten.h index 101b92e..a90cf4a 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -352,7 +352,7 @@ static inline const void *gluten_ptr(const struct seccomp_data *s, static inline bool check_gluten_limits(struct gluten_offset v, size_t size) { struct gluten_offset off = { v.type, v.offset + size }; - if (is_offset_valid(off)) + if (v.type == OFFSET_SECCOMP_DATA || is_offset_valid(off)) return true; err(" offset limits are invalid"); diff --git a/cooker/call.c b/cooker/call.c index 7ae3d48..e2a5777 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -149,14 +149,19 @@ static union value parse_field(struct gluten_ctx *g, struct arg *args, case U32: case GNU_DEV_MAJOR: case GNU_DEV_MINOR: - if (f->flags == SIZE) { + if (json_value_get_type(jvalue) == JSONArray) { + JSON_Array *array = json_value_get_array(jvalue); + unsigned i; + + if (!(f->flags & FLAGS)) + die("multiple values for non-FLAGS argument"); + + for (i = 0; i < json_array_get_count(array); i++) { + jvalue = json_array_get_value(array, i); + v.v_num |= value_get_num(f->desc.d_num, jvalue); + } + } else if (f->flags == SIZE) { v.v_num = value_get_size(g, f->desc.d_size); - } else if (f->flags == FLAGS) { - /* fetch/combine expr algebra loop */ - v.v_num = value_get_num(f->desc.d_num, jvalue); - } else if (f->flags == MASK) { - /* calculate mask first */ - v.v_num = value_get_num(f->desc.d_num, jvalue); } else { v.v_num = value_get_num(f->desc.d_num, jvalue); } @@ -164,12 +169,14 @@ static union value parse_field(struct gluten_ctx *g, struct arg *args, if (dry_run) break; - if (base_offset->type == OFFSET_NULL) - *base_offset = emit_data(g, f->type, 0, &v); - else if (add) + if (base_offset->type == OFFSET_NULL) { + *base_offset = gluten_ro_alloc_type(g, U64); + emit_data_at(g, *base_offset, f->type, &v); + } else if (add) { emit_data_or(g, offset, f->type, &v); - else + } else { emit_data_at(g, offset, f->type, &v); + } break; case SELECT: diff --git a/cooker/calls/ioctl.c b/cooker/calls/ioctl.c index 1609541..336d3c3 100644 --- a/cooker/calls/ioctl.c +++ b/cooker/calls/ioctl.c @@ -76,7 +76,7 @@ static struct field tun_ifr[] = { /* netdevice(7) */ IFNAMSIZ, { 0 }, }, { - "flags", INT, /* One allowed at a time? */ 0, + "flags", INT, FLAGS, offsetof(struct ifreq, ifr_flags), 0, { .d_num = tun_ifr_flags }, }, diff --git a/cooker/calls/net.c b/cooker/calls/net.c index 7a7fd33..74ea668 100644 --- a/cooker/calls/net.c +++ b/cooker/calls/net.c @@ -85,25 +85,25 @@ static struct num protocols[] = { static struct arg socket_args[] = { { 0, { - "family", INT, 0, 0, 0, + "family", INT, 0, 0, 0, { .d_num = af } } }, { 1, { - "type", INT, MASK, 0, 0, + "type", INT, MASK, 0, 0, { .d_num = socket_types } } }, { 1, { - "flags", INT, FLAGS, 0, 0, + "flags", INT, MASK | FLAGS, 0, 0, { .d_num = socket_flags } } }, { 2, { - "protocol", INT, 0, 0, 0, + "protocol", INT, 0, 0, 0, { .d_num = protocols } } }, diff --git a/cooker/calls/process.c b/cooker/calls/process.c index b7a92f0..647f136 100644 --- a/cooker/calls/process.c +++ b/cooker/calls/process.c @@ -27,6 +27,7 @@ clone3 #include #include +#define _GNU_SOURCE #include #include #include @@ -35,11 +36,26 @@ clone3 #include "../cooker.h" #include "../calls.h" +static struct num unshare_flags[] = { + { "CLONE_FILES", CLONE_FILES }, + { "CLONE_FS", CLONE_FS }, + { "CLONE_NEWCGROUP", CLONE_NEWCGROUP }, + { "CLONE_NEWIPC", CLONE_NEWIPC }, + { "CLONE_NEWNET", CLONE_NEWNET }, + { "CLONE_NEWNS", CLONE_NEWNS }, + { "CLONE_NEWPID", CLONE_NEWPID }, + { "CLONE_NEWTIME", CLONE_NEWTIME }, + { "CLONE_NEWUSER", CLONE_NEWUSER }, + { "CLONE_NEWUTS", CLONE_NEWUTS }, + { "CLONE_SYSVSEM", CLONE_SYSVSEM }, + { 0 } +}; + static struct arg unshare_args[] = { { 0, { - "flags", UNDEF /* TODO */, FLAGS, 0, 0, - { 0 /* TODO */ } + "flags", INT, FLAGS, 0, 0, + { .d_num = unshare_flags } } } }; diff --git a/cooker/emit.c b/cooker/emit.c index 107b2ce..d4ca97b 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -425,12 +425,14 @@ static struct gluten_offset emit_data_do(struct gluten_ctx *g, case U32: if (add) { *(int *)p |= value->v_int; - debug(" C#%i |= (%s) %i", - ret.offset, type_str[type], value->v_int); + debug(" C#%i |= (%s) %i (0x%04x)", + ret.offset, type_str[type], + value->v_num, value->v_num); } else { *(int *)p = value->v_int; - debug(" C#%i := (%s) %i", - ret.offset, type_str[type], value->v_int); + debug(" C#%i := (%s) %i (0x%04x)", + ret.offset, type_str[type], + value->v_num, value->v_num); } break; @@ -475,9 +477,7 @@ static struct gluten_offset emit_data_do(struct gluten_ctx *g, struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, size_t str_len, union value *value) { - struct gluten_offset offset = { .type = OFFSET_NULL, .offset = 0 }; - - return emit_data_do(g, offset, type, str_len, value, false); + return emit_data_do(g, NULL_OFFSET, type, str_len, value, false); } struct gluten_offset emit_data_at(struct gluten_ctx *g, diff --git a/cooker/example.hjson b/cooker/example.hjson index 9e08163..458961c 100644 --- a/cooker/example.hjson +++ b/cooker/example.hjson @@ -44,30 +44,36 @@ ] /* - * INTFLAGS, LONGFLAGS, U32FLAGS + * FLAGS * - * "field": { "in": [ "ipc", "mount", "uts" ] } + * "field": { "some": [ "ipc", "mount", "uts" ] } * flags & set * !!(flags & (ipc | mount | ns)) + * OP_BITWISE field AND set + * OP_CMP result EQ 0 -> next match * * "field": { "all": [ "ipc", "mount", "uts" ] } * flags & set == set * flags & (ipc | mount | ns) == (ipc | mount | ns) + * OP_BITWISE field AND set + * OP_CMP result NE set -> next match * - * "field": { "not": [ "ipc", "mount", "uts" ] } + * "field": { "none": [ "ipc", "mount", "uts" ] } * !(flags & set) + * OP_BITWISE field AND set + * OP_CMP result NE 0 -> next match * - * "field": { "ipc": false, "mount": true, "uts": false } - * flags & set == set - * !(flags & ipc) && (flags & mount) && !(flags & utc) + * "field": { [ "ipc", "mount", "uts" ] } + * flags == set + * flags == (ipc | mount | ns) * - * "field": { "ipc" } + * "field": "ipc" * flags == ipc * - * INTMASK + * MASK * value = (target value & known values) * - * INT, LONG, U32 + * NUMBERS * "arg": { "in": [ 0, 1 ] } * arg == 0 || arg == 1 */ diff --git a/cooker/match.c b/cooker/match.c index d32bf0a..5fd46db 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -78,7 +78,8 @@ 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, seccomp_offset; + struct gluten_offset const_offset, mask_offset, data_offset; + struct gluten_offset seccomp_offset; union value v = { .v_num = 0 }; struct field *f_inner; const char *tag_name; @@ -125,7 +126,8 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx jvalue = json_object_get_value(tmp, "value"); } - if (json_value_get_type(jvalue) == JSONObject && + if (!(f->flags & FLAGS) && /* For FLAGS, it's a single operation */ + 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); @@ -160,19 +162,54 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx case INT: case LONG: case U32: - if (f->flags == FLAGS) { - /* fetch/combine expr algebra loop */ - ; + data_offset = offset; + + if ((f->flags & FLAGS) && + json_value_get_type(jvalue) == JSONObject && + (tmp = json_value_get_object(jvalue))) { + struct gluten_offset set_offset, cmp_offset, masked; + union value set, cmpterm; + + value_get_flags(f->desc.d_num, tmp, + &set, &cmp, &cmpterm); + + set_offset = emit_data(g, f->type, 0, &set); + masked = emit_bitwise(g, f->type, BITWISE_AND, + NULL_OFFSET, + offset, set_offset); + + cmp_offset = emit_data(g, f->type, 0, &cmpterm); + emit_cmp(g, cmp, masked, cmp_offset, + gluten_size[f->type], jump); + + break; } - if (f->flags == MASK) { - /* calculate mask first */ - ; + + if (json_value_get_type(jvalue) == JSONArray) { + JSON_Array *array = json_value_get_array(jvalue); + unsigned i; + + if (!(f->flags & FLAGS)) + die("multiple values for non-FLAGS argument"); + + for (i = 0; i < json_array_get_count(array); i++) { + jvalue = json_array_get_value(array, i); + v.v_num |= value_get_num(f->desc.d_num, jvalue); + } + } else if (f->flags & MASK) { + v.v_num = value_get_mask(f->desc.d_num); + mask_offset = emit_data(g, f->type, 0, &v); + data_offset = emit_bitwise(g, f->type, BITWISE_AND, + NULL_OFFSET, offset, + mask_offset); + v.v_num = value_get_num(f->desc.d_num, jvalue); + } else { + v.v_num = value_get_num(f->desc.d_num, jvalue); } - /* 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, offset, const_offset, gluten_size[f->type], - jump); + emit_cmp(g, cmp, data_offset, const_offset, + gluten_size[f->type], jump); break; case GNU_DEV_MAJOR: /* diff --git a/cooker/parse.c b/cooker/parse.c index 353947e..7cac43f 100644 --- a/cooker/parse.c +++ b/cooker/parse.c @@ -128,6 +128,66 @@ struct rule_parser { { NULL, NULL }, }; +static union value value_get_set(struct num *desc, JSON_Array *set) +{ + struct num *tmp; + union value n; + unsigned i; + + for (i = 0; i < json_array_get_count(set); i++) { + for (tmp = desc; tmp->name; tmp++) { + if (!strcmp(tmp->name, json_array_get_string(set, i))) { + n.v_num |= desc->value; + break; + } + } + + if (!tmp->name) + die("invalid flag'%s'", json_array_get_string(set, i)); + } + + return n; +} + +void value_get_flags(struct num *desc, JSON_Object *obj, + union value *bitset, enum op_cmp_type *cmp, + union value *cmpterm) +{ + JSON_Array *set; + + if ((set = json_object_get_array(obj, "some"))) { + *bitset = value_get_set(desc, set); + *cmp = CMP_EQ; + cmpterm->v_num = 0; + } else if ((set = json_object_get_array(obj, "all"))) { + *bitset = value_get_set(desc, set); + *cmp = CMP_NE; + *cmpterm = *bitset; + } else if ((set = json_object_get_array(obj, "not"))) { + *bitset = value_get_set(desc, set); + *cmp = CMP_NE; + cmpterm->v_num = 0; + } else { + die("unsupported flag quantifier"); + } +} + +/** + * value_get_mask() - Get union of all possible numeric values from description + * @desc: Description of possible values from model + * + * Return: numeric value + */ +long long value_get_mask(struct num *desc) +{ + long long n = 0; + + while ((desc++)->name) + n |= desc->value; + + return n; +} + /** * value_get_num() - Get numeric value from description matching JSON input * @desc: Description of possible values from model diff --git a/cooker/parse.h b/cooker/parse.h index 8bb37a4..7dd3e11 100644 --- a/cooker/parse.h +++ b/cooker/parse.h @@ -7,6 +7,10 @@ #define PARSE_H long long value_get_num(struct num *desc, JSON_Value *value); +long long value_get_mask(struct num *desc); +void value_get_flags(struct num *desc, JSON_Object *obj, + union value *bitset, enum op_cmp_type *cmp, + union value *cmpterm); size_t value_get_size(struct gluten_ctx *g, intptr_t id); void value_get(union desc desc, enum type type, JSON_Value *value, union value *out); diff --git a/demo/socket.hjson b/demo/socket.hjson new file mode 100644 index 0000000..7a16ccc --- /dev/null +++ b/demo/socket.hjson @@ -0,0 +1,11 @@ +[ + { + "match": [ + { "socket": { "family": "unix", "type": "stream", "flags": 0, "protocol": 0 } } + ], + "call": [ + { "socket": { "family": "unix", "type": "stream", "flags": [ "cloexec", "nonblock" ], "protocol": 0 }, "ret": "new_fd" } + ], + "fd": { "src": { "tag": "new_fd" }, "close_on_exec": false, "return": true } + } +] diff --git a/operations.c b/operations.c index 120b142..b08d837 100644 --- a/operations.c +++ b/operations.c @@ -141,15 +141,15 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, static int set_namespaces(struct arg_clone *c) { - char *path; + char (*path)[PATH_MAX]; int fd; - for (path = c->ns_path[0]; *path; path++) { - if ((fd = open(path, O_CLOEXEC)) < 0) - ret_err(-1, "open for file %s", path); + for (path = c->ns_path; **path; *path++) { + if ((fd = open(*path, O_CLOEXEC)) < 0) + ;//ret_err(-1, "open for file %s", *path); if (setns(fd, 0) != 0) - ret_err(-1, "setns"); + ;//ret_err(-1, "setns"); } return 0; } -- cgit v1.2.3