From 1644bbec6161ec971a2ba3c213ce285b995cac22 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Thu, 1 Jun 2023 11:04:38 +0200 Subject: cooker: OP_CALL and OP_COPY stuff ...mostly. Signed-off-by: Stefano Brivio --- common/gluten.h | 23 +-- common/util.c | 1 + cooker/Makefile | 12 +- cooker/call.c | 424 +++++++++++++++++++++++++++++++++++++++++++++++++ cooker/call.h | 11 ++ cooker/calls/fs.c | 38 +++-- cooker/calls/ioctl.c | 72 ++++++--- cooker/calls/net.c | 124 ++++++++++----- cooker/calls/process.c | 12 +- cooker/cooker.h | 73 ++++----- cooker/emit.c | 136 ++++++++++++---- cooker/emit.h | 10 ++ cooker/example.hjson | 7 +- cooker/gluten.c | 71 +++++++-- cooker/gluten.h | 9 +- cooker/main.c | 1 + cooker/match.c | 261 +++++++++++------------------- cooker/parse.c | 92 +++++++++-- cooker/parse.h | 5 + 19 files changed, 1025 insertions(+), 357 deletions(-) create mode 100644 cooker/call.c create mode 100644 cooker/call.h diff --git a/common/gluten.h b/common/gluten.h index 6c0fc25..5409234 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -36,25 +36,26 @@ extern struct seccomp_data anonymous_seccomp_data; #define GET_BIT(x, i) (((x) & (1UL << (i))) != 0) enum gluten_offset_type { - OFFSET_RO_DATA = 0, - OFFSET_DATA = 1, - OFFSET_SECCOMP_DATA = 2, - OFFSET_INSTRUCTION = 3, - OFFSET_TYPE_MAX = OFFSET_INSTRUCTION, + OFFSET_NULL = 0, + OFFSET_RO_DATA = 1, + OFFSET_DATA = 2, + OFFSET_SECCOMP_DATA = 3, + OFFSET_INSTRUCTION = 4, + OFFSET_TYPE_MAX = OFFSET_INSTRUCTION, }; extern const char *gluten_offset_name[OFFSET_TYPE_MAX + 1]; struct gluten_offset { #ifdef __GNUC__ - enum gluten_offset_type type : BITS_PER_NUM(OFFSET_TYPE_MAX); + enum gluten_offset_type type :BITS_PER_NUM(OFFSET_TYPE_MAX); #else - uint16_t type : BITS_PER_NUM(OFFSET_TYPE_MAX); + uint32_t type :BITS_PER_NUM(OFFSET_TYPE_MAX); #endif - uint16_t offset : BITS_PER_NUM(OFFSET_MAX); + uint32_t offset :BITS_PER_NUM(OFFSET_MAX); }; -BUILD_BUG_ON(BITS_PER_NUM(OFFSET_TYPE_MAX) + BITS_PER_NUM(OFFSET_MAX) > 16) +BUILD_BUG_ON(BITS_PER_NUM(OFFSET_TYPE_MAX) + BITS_PER_NUM(OFFSET_MAX) > 32) enum ns_spec_type { NS_NONE, @@ -98,6 +99,7 @@ enum op_type { OP_END = 0, OP_NR, OP_CALL, + OP_COPY, OP_BLOCK, OP_CONT, OP_INJECT, @@ -106,7 +108,6 @@ enum op_type { OP_LOAD, OP_CMP, OP_RESOLVEDFD, - OP_COPY, }; struct op_nr { @@ -212,6 +213,8 @@ BUILD_BUG_ON(INST_SIZE < INST_MAX * sizeof(struct op)) static inline bool is_offset_valid(const struct gluten_offset x) { switch (x.type) { + case OFFSET_NULL: + return false; case OFFSET_DATA: return x.offset < DATA_SIZE; case OFFSET_RO_DATA: diff --git a/common/util.c b/common/util.c index 3a4b2e5..19d5c12 100644 --- a/common/util.c +++ b/common/util.c @@ -30,5 +30,6 @@ logfn(info) logfn(debug) const char *gluten_offset_name[OFFSET_TYPE_MAX + 1] = { + "NULL", "read-only data", "temporary data", "seccomp data", "instruction area" }; diff --git a/cooker/Makefile b/cooker/Makefile index 5a0ea8e..823e6db 100644 --- a/cooker/Makefile +++ b/cooker/Makefile @@ -29,12 +29,14 @@ AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/SH4/SH/') CFLAGS += -DSEITAN_AUDIT_ARCH=AUDIT_ARCH_$(AUDIT_ARCH) -SRCS := calls.c emit.c gluten.c filter.c main.c match.c parse.c parson.c \ - $(COMMON)/util.c \ +SRCS := call.c calls.c emit.c gluten.c filter.c main.c match.c \ + parse.c parson.c \ + $(COMMON)/util.c \ calls/net.c calls/ioctl.c calls/process.c calls/fs.c -HEADERS := calls.h cooker.h emit.h filter.h gluten.h match.h parse.h parson.h \ - $(COMMON)/gluten.h $(COMMON)/util.h \ - calls/net.h calls/ioctl.h calls/process.h calls/fs.h +HEADERS := call.h calls.h cooker.h emit.h filter.h gluten.h match.h \ + parse.h parson.h \ + $(COMMON)/gluten.h $(COMMON)/util.h \ + calls/net.h calls/ioctl.h calls/process.h calls/fs.h $(BIN): $(SRCS) $(HEADERS) $(CC) $(CFLAGS) -o $(BIN) $(SRCS) diff --git a/cooker/call.c b/cooker/call.c new file mode 100644 index 0000000..e1abb39 --- /dev/null +++ b/cooker/call.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +/* seitan - Syscall Expressive Interpreter, Transformer and Notifier + * + * cooker/call.c - Parse "call" rules from JSON recipe into bytecode + * + * Copyright 2023 Red Hat GmbH + * Author: Stefano Brivio + */ + +#include "parson.h" +#include "calls.h" +#include "cooker.h" +#include "gluten.h" +#include "emit.h" +#include "parse.h" +#include "util.h" + +/** + +struct syscall_desc { + unsigned nr:9; + + unsigned arg_count:3; + unsigned has_ret:1; + + unsigned arg_deref:6; + + struct gluten_offset data[]; +}; + +match: "tag": "x" -- meaning "set" +call: "tag": { "set": "x", "get": "y" } + +Examples of arguments: +- INT: 2 write 2 in ro_data, and pointer to it + parse_arg() passes null offset + parse_field() passes back ro_data offset + +- INT *: 2 write 2 in ro_data (using size), and pointer to it + no flags or tags parse_arg() passes ro_data offset + +- INT *: 2 - write 2 in ro_data at x + COPY_ON_CALL - emit op_copy from x to y + or "set" tag - write pointer to y + parse_arg() passes data offset + +- INT: "get" write pointer to tag + parse_arg() passes null offset + parse_field() gives back ro_data or data offset + +- INT *: "get" write pointer to tag + no COPY_ON_CALL parse_arg() passes null offset + parse_field() gives back data offset + +- STRING: abcd write abcd to ro_data (using size), and pointer to it + parse_arg() passes ro_data offset + +- STRUCT: 1, 2 write struct to ro_data, and pointer to it + parse_tag() passes ro_data offset + +- STRUCT: "get" write pointer to tag + parse_arg() passes null offset + parse_field() gives back data offset + +- STRUCT: , 2 - write 2 to ro_data at x + - allocate rw_data at y + - emit op_copy from tag to y + - emit op_copy from x to y + offset of "2" + - write pointer to y + parse_arg() passes data offset + */ + +static union value parse_field(struct gluten_ctx *g, + struct gluten_offset *base_offset, + int index, struct field *f, JSON_Value *jvalue, + bool dry_run, bool add) +{ + struct gluten_offset offset = *base_offset; + union value v = { .v_num = 0 }; + JSON_Object *tmp1, *tmp2; + struct field *f_inner; + JSON_Value *sel; + + if (f->name) + debug(" parsing field name %s", f->name); + + if (offset.type != OFFSET_NULL) + offset.offset += f->offset; + + if (json_value_get_type(jvalue) == JSONObject && + (tmp1 = json_value_get_object(jvalue)) && + (tmp2 = json_object_get_object(tmp1, "tag"))) { + const char *tag_set, *tag_get; + size_t count = 0; + + if ((tag_set = json_object_get_string(tmp2, "set"))) { + count++; + debug(" setting tag reference (post) '%s'", tag_set); + + if (!dry_run) + gluten_add_tag_post(g, tag_set, offset); + } + + if ((tag_get = json_object_get_string(tmp2, "get"))) { + struct gluten_offset tag_offset; + + count++; + debug(" getting tag reference '%s'", tag_get); + + /* TODO: Check type */ + tag_offset = gluten_get_tag(g, tag_get); + if (tag_offset.type == OFFSET_NULL) + die(" tag not found"); + + if (base_offset->type == OFFSET_NULL) + *base_offset = tag_offset; + else + emit_copy_field(g, f, offset, tag_offset); + } + + if (json_object_get_count(tmp2) > count) + die("stray object in tag reference"); + + if (!count) + die("invalid tag specification"); + + jvalue = json_object_get_value(tmp1, "value"); + + if (tag_get) { + if (jvalue) + die("stray value with \"get\" tag"); + + return v; + } + } + + if (!jvalue) + return v; + + switch (f->type) { + case INT: + case LONG: + case U32: + if (f->flags == FLAGS) { + /* fetch/combine expr algebra loop */ + ; + } + if (f->flags == MASK) { + /* calculate mask first */ + ; + } + + v.v_num = value_get_num(f->desc.d_num, jvalue); + if (dry_run) + break; + + if (base_offset->type == OFFSET_NULL) + *base_offset = emit_data(g, f->type, 0, &v); + else if (add) + emit_data_or(g, offset, f->type, &v); + else + emit_data_at(g, offset, f->type, &v); + + break; + case SELECT: + f_inner = f->desc.d_select->field; + + if ((tmp1 = json_value_get_object(jvalue))) { + if (!(sel = json_object_get_value(tmp1, f_inner->name))) + die(" no selector for '%s'", f_inner->name); + } else { + sel = jvalue; + } + + v = parse_field(g, &offset, index, f_inner, sel, false, false); + + f = select_field(g, index, f->desc.d_select, v); + if (f) + parse_field(g, &offset, index, f, jvalue, false, add); + break; + case STRING: + v.v_str = json_value_get_string(jvalue); + if (strlen(v.v_str) + 1 > f->size) + die(" string %s too long for field", v.v_str); + + if (dry_run) + break; + + emit_data_at(g, offset, f->type, &v); + break; + case STRUCT: + for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) { + JSON_Value *f_value; + + tmp1 = json_value_get_object(jvalue); + f_value = json_object_get_value(tmp1, f_inner->name); + if (!f_value) + continue; + + parse_field(g, &offset, index, f_inner, f_value, false, + add); + } + break; + default: + ; + } + + return v; +} + +bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, + bool *top_level_tag, int level) +{ + struct gluten_offset unused = { .type = OFFSET_NULL, .offset = 0 }; + union value v = { .v_num = 0 }; + struct field *f_inner; + JSON_Object *tmp; + JSON_Value *sel; + + if (f->flags & COPY_ON_CALL) + return true; + + if (f->flags & SIZE) + return false; + + if (json_value_get_type(jvalue) == JSONObject && + (tmp = json_value_get_object(jvalue)) && + (tmp = json_object_get_object(tmp, "tag"))) { + if (json_object_get_string(tmp, "set")) + return true; + + if (level) + return true; + + if (top_level_tag) + *top_level_tag = true; + + return false; + } + + switch (f->type) { + case INT: + case LONG: + case U32: + return false; + case SELECT: + f_inner = f->desc.d_select->field; + if (arg_needs_temp(f_inner, pos, jvalue, top_level_tag, level)) + return true; + + if ((tmp = json_value_get_object(jvalue))) { + if (!(sel = json_object_get_value(tmp, f_inner->name))) + die(" no selector for '%s'", f_inner->name); + } else { + sel = jvalue; + } + + v = parse_field(NULL, &unused, pos, f_inner, sel, true, false); + + f = select_field(NULL, pos, f->desc.d_select, v); + if (f) + return arg_needs_temp(f, pos, jvalue, NULL, level + 1); + + return false; + case STRING: + return false; + case STRUCT: + for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) { + JSON_Value *f_value; + + tmp = json_value_get_object(jvalue); + f_value = json_object_get_value(tmp, f_inner->name); + if (!f_value) + continue; + + if (arg_needs_temp(f, pos, f_value, NULL, level + 1)) + return true; + } + + return false; + default: + ; + } + + return false; +} + +static struct gluten_offset parse_arg(struct gluten_ctx *g, struct arg *a, + struct gluten_offset offset, + JSON_Value *jvalue) +{ + bool top_level_tag = false; + + debug(" Parsing call argument %s", a->f.name); + + if (offset.type != OFFSET_NULL) { + parse_field(g, &offset, a->pos, &a->f, jvalue, false, true); + 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) + offset = gluten_ro_alloc(g, a->f.size); + + parse_field(g, &offset, a->pos, &a->f, jvalue, false, false); + + return offset; +} + +static void parse_call(struct gluten_ctx *g, JSON_Object *obj, const char *ret, + struct arg *args) +{ + struct gluten_offset offset[6] = { 0 }, ret_offset = { 0 }; + /* Minimum requirements for argument specification: + * - if argument can be FDPATH, exactly one value for that position + * - if argument is a size field, value is optional + * - otherwise, every argument needs to be specified + */ + struct { + bool needs_fd; + bool has_fd; + } arg_check[6] = { 0 }; + unsigned count = 0; + struct arg *a; + + /* Set requirements first */ + for (a = args; a->f.name; a++) { + if (a->f.type == SELECTED) { + if (!g->selected_arg[a->pos]) + die(" No argument selected for %s", a->f.name); + a = g->selected_arg[a->pos]; + } + + if (a->f.type == FDPATH) + arg_check[a->pos].needs_fd = true; + } + + /* TODO: Factor this out into a function in... parse.c? */ + for (a = args; a->f.name; a++) { + JSON_Value *jvalue; + + if (a->f.type == SELECTED) + a = g->selected_arg[a->pos]; + + /* Not common with parse_match(), though */ + if ((jvalue = json_object_get_value(obj, a->f.name))) { + if (arg_check[a->pos].has_fd) + die(" Conflicting value for %s", a->f.name); + else if (arg_check[a->pos].needs_fd) + arg_check[a->pos].has_fd = true; + + offset[a->pos] = parse_arg(g, a, 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, a, offset[a->pos], + jvalue); + } else { + die(" No specification for argument %s", a->f.name); + } + } + + if (ret) { + ret_offset = gluten_rw_alloc_type(g, U64); + gluten_add_tag_post(g, ret, ret_offset); + } + + /* emit_call() */ + + if (count != json_object_get_count(obj)) + die(" Stray elements in call"); +} + +void handle_calls(struct gluten_ctx *g, JSON_Value *value) +{ + JSON_Array *calls = json_value_get_array(value); + unsigned i, count; + + if (calls) + count = json_array_get_count(calls); + else + count = 1; + + for (i = 0; i < count; i++) { + struct call **set, *call; + JSON_Object *obj, *args; + const char *name, *ret; + + if (calls) + obj = json_array_get_object(calls, i); + else + obj = json_value_get_object(value); + + name = json_object_get_name(obj, 0); + value = json_object_get_value_at(obj, 0); + args = json_object_get_object(obj, name); + + debug(" Parsing call %s", name); + + ret = json_object_get_string(obj, "ret"); + + /* TODO: Factor this out into a function in calls.c */ + for (set = call_sets, call = set[0]; *set; ) { + if (!call->name) { + set++; + call = set[0]; + continue; + } + + if (!strcmp(name, call->name)) { + debug(" Found description for %s", + name); + parse_call(g, args, ret, call->args); + break; + } + call++; + } + } +} diff --git a/cooker/call.h b/cooker/call.h new file mode 100644 index 0000000..3c0eb09 --- /dev/null +++ b/cooker/call.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright 2023 Red Hat GmbH + * Author: Stefano Brivio + */ + +#ifndef CALL_H +#define CALL_H + +void handle_calls(struct gluten_ctx *g, JSON_Value *value); + +#endif /* CALL_H */ diff --git a/cooker/calls/fs.c b/cooker/calls/fs.c index d800f38..924907d 100644 --- a/cooker/calls/fs.c +++ b/cooker/calls/fs.c @@ -66,23 +66,35 @@ swapoff #include "../calls.h" static struct arg mknod_args[] = { - { - 0, "path", STRING, 1 /* TODO: PATH_MAX */, - { 0 } + { 0, + { + "path", STRING, 0, + 0, 1 /* TODO: PATH_MAX */, + { 0 } + } }, - { - 1, "mode", INTFLAGS, 0, - { 0 /* TODO */ }, + { 1, + { + "mode", UNDEF /* TODO */, FLAGS, + 0, 0, + { 0 /* TODO */ }, + } }, - { - 2, "major", UNDEF /* TODO */, 0, - { 0 }, + { 2, + { + "major", UNDEF /* TODO */, 0, + 0, 0, + { 0 }, + } }, - { - 2, "minor", UNDEF /* TODO */, 0, - { 0 }, + { 2, + { + "minor", UNDEF /* TODO */, 0, + 0, 0, + { 0 }, + } }, - { 0 }, + { 0 } }; struct call syscalls_fs[] = { diff --git a/cooker/calls/ioctl.c b/cooker/calls/ioctl.c index 576e02e..a60b834 100644 --- a/cooker/calls/ioctl.c +++ b/cooker/calls/ioctl.c @@ -71,38 +71,50 @@ static struct num tun_ifr_flags[] = { static struct field tun_ifr[] = { /* netdevice(7) */ { - "name", STRING, + "name", STRING, 0, offsetof(struct ifreq, ifr_name), IFNAMSIZ, { 0 }, }, { - "flags", INT, /* One allowed at a time? */ + "flags", INT, /* One allowed at a time? */ 0, offsetof(struct ifreq, ifr_flags), 0, { .d_num = tun_ifr_flags }, }, }; static struct select_num ioctl_request_arg[] = { - { - FS_IOC_GETFLAGS, - { 2, "argp", INTFLAGS, sizeof(int), { .d_num = attr } } + { FS_IOC_GETFLAGS, + { 2, + { + "argp", INT, FLAGS, + sizeof(int), 0, + { .d_num = attr } + } + } }, - { - FS_IOC_SETFLAGS, - { 2, "argp", INTFLAGS, sizeof(int), { .d_num = attr } } + { FS_IOC_SETFLAGS, + { 2, + { + "argp", INT, FLAGS, + sizeof(int), 0, + { .d_num = attr } + } + } }, - { - TUNSETIFF, - { - 2, "ifr", STRUCT, sizeof(struct ifreq), - { .d_struct = tun_ifr } + { TUNSETIFF, + { 2, + { + "ifr", STRUCT, 0, + sizeof(struct ifreq), 0, + { .d_struct = tun_ifr } + } } }, { 0 }, }; static struct field ioctl_request = { - "request", INT, 0, 0, { .d_num = request }, + "request", INT, 0, 0, 0, { .d_num = request }, }; static struct select ioctl_request_select = { @@ -110,21 +122,29 @@ static struct select ioctl_request_select = { }; static struct arg ioctl_args[] = { - { - 0, "path", FDPATH, 0, - { 0 } + { 0, + { + "path", FDPATH, 0, 0, 0, + { 0 } + } }, - { - 0, "fd", INT, 0, - { 0 } + { 0, + { + "fd", INT, 0, 0, 0, + { 0 } + } }, - { - 1, "request", SELECT, 0, - { .d_select = &ioctl_request_select } + { 1, + { + "request", SELECT, 0, 0, 0, + { .d_select = &ioctl_request_select } + } }, - { - 2, "arg", SELECTED, -1, - { 0 } + { 2, + { + "arg", SELECTED, 0, -1, 0, + { 0 } + } }, { 0 }, }; diff --git a/cooker/calls/net.c b/cooker/calls/net.c index df97aab..f5e2728 100644 --- a/cooker/calls/net.c +++ b/cooker/calls/net.c @@ -83,16 +83,36 @@ static struct num protocols[] = { }; static struct arg socket_args[] = { - { 0, "family", INT, 0, { .d_num = af } }, - { 1, "type", INTMASK, 0, { .d_num = socket_types } }, - { 1, "flags", INTFLAGS, 0, { .d_num = socket_flags } }, - { 2, "protocol", INT, 0, { .d_num = protocols } }, + { 0, + { + "family", INT, 0, 0, 0, + { .d_num = af } + } + }, + { 1, + { + "type", INT, MASK, 0, 0, + { .d_num = socket_types } + } + }, + { 1, + { + "flags", INT, FLAGS, 0, 0, + { .d_num = socket_flags } + } + }, + { 2, + { + "protocol", INT, 0, 0, 0, + { .d_num = protocols } + } + }, { 0 }, }; static struct field connect_addr_unix[] = { { - "path", STRING, + "path", STRING, 0, offsetof(struct sockaddr_un, sun_path), UNIX_PATH_MAX, { 0 } }, @@ -101,12 +121,12 @@ static struct field connect_addr_unix[] = { static struct field connect_addr_ipv4[] = { { - "port", PORT, + "port", PORT, 0, offsetof(struct sockaddr_in, sin_port), 0, { 0 } }, { - "addr", IPV4, + "addr", IPV4, 0, offsetof(struct sockaddr_in, sin_addr), 0, { 0 } }, @@ -115,12 +135,12 @@ static struct field connect_addr_ipv4[] = { static struct field connect_addr_ipv6[] = { { - "port", PORT, + "port", PORT, 0, offsetof(struct sockaddr_in6, sin6_port), 0, { 0 } }, { - "addr", IPV6, + "addr", IPV6, 0, offsetof(struct sockaddr_in6, sin6_addr), 0, { 0 } }, @@ -129,12 +149,12 @@ static struct field connect_addr_ipv6[] = { static struct field connect_addr_nl[] = { { - "pid", PID, + "pid", PID, 0, offsetof(struct sockaddr_nl, nl_pid), 0, { 0 } }, { - "groups", U32, + "groups", U32, 0, offsetof(struct sockaddr_nl, nl_groups), 0, { 0 } }, @@ -142,27 +162,43 @@ static struct field connect_addr_nl[] = { }; static struct field connect_family = { - "family", INT, + "family", INT, 0, offsetof(struct sockaddr, sa_family), 0, { .d_num = af } }; static struct select_num connect_addr_select_family[] = { - { - AF_UNIX, - { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_unix } } + { AF_UNIX, + { 1, + { + NULL, STRUCT, 0, 0, 0, + { .d_struct = connect_addr_unix } + } + } }, - { - AF_INET, - { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_ipv4 } } + { AF_INET, + { 1, + { + NULL, STRUCT, 0, 0, 0, + { .d_struct = connect_addr_ipv4 } + } + } }, - { - AF_INET6, - { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_ipv6 } } + { AF_INET6, + { 1, + { + NULL, STRUCT, 0, 0, 0, + { .d_struct = connect_addr_ipv6 } + } + } }, - { - AF_NETLINK, - { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_nl } } + { AF_NETLINK, + { 1, + { + NULL, STRUCT, 0, 0, 0, + { .d_struct = connect_addr_nl } + } + } }, { 0 }, }; @@ -172,21 +208,37 @@ static struct select connect_addr_select = { }; static struct arg connect_args[] = { - { - 0, "fd", INT, 0, - { 0 }, + { 0, + { + "fd", INT, 0, + 0, + 0, + { 0 }, + }, }, - { - 0, "path", FDPATH, 0, - { 0 }, + { 0, + { + "path", FDPATH, 0, + 0, + 0, + { 0 }, + }, }, - { - 1, "addr", SELECT, sizeof(struct sockaddr_storage), - { .d_select = &connect_addr_select }, + { 1, + { + "addr", SELECT, 0, + 0, + sizeof(struct sockaddr_storage), + { .d_select = &connect_addr_select }, + }, }, - { - 2, "addrlen", LONG, 0, - { 0 }, + { 2, + { + "addrlen", LONG, SIZE, + 0, + 0, + { .d_arg_size = 1 }, + }, }, }; diff --git a/cooker/calls/process.c b/cooker/calls/process.c index 7c0f36e..b133629 100644 --- a/cooker/calls/process.c +++ b/cooker/calls/process.c @@ -36,13 +36,15 @@ clone3 #include "../calls.h" static struct arg unshare_args[] = { - { - 0, "flags", INTFLAGS, 0, - { 0 /* TODO */ } - }, + { 0, + { + "flags", UNDEF /* TODO */, FLAGS, 0, 0, + { 0 /* TODO */ } + } + } }; struct call syscalls_process[] = { { __NR_unshare, "unshare", unshare_args }, - { 0 }, + { 0 } }; diff --git a/cooker/cooker.h b/cooker/cooker.h index 8dcf93e..1b9d9dd 100644 --- a/cooker/cooker.h +++ b/cooker/cooker.h @@ -17,23 +17,28 @@ #include #include +#include + #define TAGS_MAX 256 #define CALL_ARGS 6 struct num; struct field; struct select; +struct size; /** * union desc - Description of lists of numbers, structs or selector fields * @d_num: Pointer to a list of numbers and their labels * @d_struct: Pointer to a struct description * @d_select: Pointer to description of a selector + * @d_arg_size: Position of argument whose pointed length is described */ union desc { struct num *d_num; struct field *d_struct; struct select *d_select; + int d_arg_size; }; /** @@ -59,20 +64,9 @@ enum type { NONE, INT, - INTMASK, - INTFLAGS, - U32, - U32MASK, - U32FLAGS, - U64, - U64MASK, - U64FLAGS, - LONG, - LONGMASK, - LONGFLAGS, STRING, @@ -91,10 +85,28 @@ enum type { TYPE_END, }; +enum flags { + /* Mask field with all possible values, first */ + MASK = BIT(1), + /* Intersection of multiple bits */ + FLAGS = BIT(2), + /* Represent size of another argument */ + SIZE = BIT(3), + + COPY_ON_CALL = BIT(4), + + /* Don't copy value from original call, but fill on return */ + RBUF = BIT(5), + /* Copy value from original call, ignore on return */ + WBUF = BIT(6), +}; + #define TYPE_COUNT (TYPE_END - 1) -#define TYPE_IS_COMPOUND(t) ((t) == STRUCT || (t) == SELECT) -#define TYPE_IS_NUM(t) ((t) == INT || (t) == U32 || (t) == LONG) +#define TYPE_IS_COMPOUND(t) \ + ((t) == STRUCT || (t) == SELECT) +#define TYPE_IS_NUM(t) \ + ((t) == INT || (t) == U32 || (t) == U64 || (t) == LONG) /** * struct num - A numeric value and its label @@ -107,31 +119,21 @@ struct num { }; /** - * struct field - Field inside a struct + * struct field - Field inside argument or struct * @name: Name of field * @type: Type of field - * @offset: Offset of field within struct, in bytes - * @strlen: Length of string for string types, 0 otherwise + * @flags: Modifier flags for type + * @offset: Offset of field within argument or struct, in bytes + * @size: Turns int into int *, char into string, humans into paperclips * @desc: Description of possible values for field, or linked struct */ struct field { char *name; enum type type; - off_t offset; + enum flags flags; - size_t strlen; - - union desc desc; -}; + off_t offset; -/** - * struct select_target - Description of value selected by selector field - * @type: Type of value - * @size: Size to dereference for pointers, 0 otherwise - * @desc: Description for selected value - */ -struct select_target { - enum type type; /* TODO: Almost a struct arg? */ size_t size; union desc desc; @@ -140,19 +142,11 @@ struct select_target { /** * struct arg - Description of part of, or complete system call argument * @pos: Index of argument in system call - * @name: JSON name used for matches and calls - * @type: Argument type - * @size: Size of pointed area if any, 0 otherwise - * @desc: Description of list of numbers, struct or selector field + * @f: Field describing part or complete argument */ struct arg { int pos; - char *name; - - enum type type; - size_t size; - - union desc desc; + struct field f; }; /** @@ -162,7 +156,6 @@ struct arg { */ struct select_num { long long value; - struct arg target; }; diff --git a/cooker/emit.c b/cooker/emit.c index 3fcddba..403f1ba 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -16,10 +16,7 @@ static const char *type_str[] = { "UNDEF", "NONE", - "INT", "INTMASK", "INTFLAGS", - "U32", "U32MASK", "U32FLAGS", - "U64", "U64MASK", "U64FLAGS", - "LONG", "LONGMASK", "LONGFLAGS", + "INT", "U32", "U64", "LONG", "STRING", "STRUCT", "SELECT", "PID", @@ -48,8 +45,8 @@ void emit_nr(struct gluten_ctx *g, struct gluten_offset number) nr->no_match.type = OFFSET_INSTRUCTION; nr->no_match.offset = JUMP_NEXT_BLOCK; - debug(" %i: OP_NR: if syscall number is not %li, jump to %s", - g->ip.offset, number, jump_name[nr->no_match.offset]); + debug(" %i: OP_NR: if syscall number is not C#%lu, jump to %s", + g->ip.offset, number.offset, jump_name[nr->no_match.offset]); if (++g->ip.offset > INST_MAX) die("Too many instructions"); @@ -133,7 +130,7 @@ void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, enum jump_type jmp) { emit_cmp(g, cmp, x, y, - field->strlen ? field->strlen : gluten_size[field->type], + field->size ? field->size : gluten_size[field->type], jmp); } @@ -174,42 +171,95 @@ void emit_block(struct gluten_ctx *g, int32_t error) die("Too many instructions"); } -struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, - size_t str_len, union value *value) +/** + * emit_copy(): Emit OP_COPY instruction: copy between given offsets + * @g: gluten context + * @dst: gluten pointer to destination + * @src: gluten pointer to source + * @size: Bytes to copy + */ +void emit_copy(struct gluten_ctx *g, + struct gluten_offset dst, struct gluten_offset src, size_t size) +{ + struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); + struct op_copy *copy = &op->op.copy; + + op->type = OP_COPY; + + copy->dst = dst; + copy->src = src; + copy->size = size; + + debug(" %i: OP_COPY: %lu bytes from %s: #%lu to %s: #%lu", + g->ip.offset, size, + gluten_offset_name[src.type], src.offset, + gluten_offset_name[dst.type], dst.offset); + + if (++g->ip.offset > INST_MAX) + die("Too many instructions"); +} + +/** + * emit_copy_field() - Emit OP_COPY for a given field type + * @g: gluten context + * @field: Description of field from system call model + * @dst: gluten pointer to destination + * @src: gluten pointer to source + */ +void emit_copy_field(struct gluten_ctx *g, struct field *field, + struct gluten_offset dst, struct gluten_offset src) +{ + emit_copy(g, dst, src, + field->size ? field->size : gluten_size[field->type]); +} + +static struct gluten_offset emit_data_do(struct gluten_ctx *g, + struct gluten_offset offset, + enum type type, size_t str_len, + union value *value, bool add) { - void *p = gluten_ptr(&g->g, g->cp); - struct gluten_offset ret = g->cp; + void *p = gluten_ptr(&g->g, offset); + struct gluten_offset ret = offset; + + if (!p) { + if (type == STRING) + ret = gluten_ro_alloc(g, str_len); + else + ret = gluten_ro_alloc_type(g, type); + + p = gluten_ptr(&g->g, ret); + } 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 (add) { + *(int *)p |= value->v_int; + debug(" C#%i |= (%s) %i", + ret.offset, type_str[type], value->v_int); + } else { + *(int *)p = value->v_int; + debug(" C#%i := (%s) %i", + ret.offset, type_str[type], value->v_int); + } - g->cp.offset += sizeof(int); break; case U64: - if (g->cp.offset + sizeof(uint64_t) > RO_DATA_SIZE) - die(" Read-only data section exceeded"); - - *(uint64_t *)p = value->v_u64; - debug(" C#%i: (%s) %i", g->cp.offset, type_str[type], - value->v_u64); + if (add) { + *(uint64_t *)p |= value->v_num; + debug(" C#%i |= (%s) %llu", + ret.offset, type_str[type], value->v_num); + } else { + *(uint64_t *)p = value->v_num; + debug(" C#%i := (%s) %llu", + ret.offset, type_str[type], value->v_num); + } - g->cp.offset += sizeof(uint64_t); 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: ; @@ -218,6 +268,32 @@ struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, return ret; } +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); +} + +struct gluten_offset emit_data_at(struct gluten_ctx *g, + struct gluten_offset offset, + enum type type, union value *value) +{ + return emit_data_do(g, offset, type, + (type == STRING) ? strlen(value->v_str) : 0, value, + false); +} + +struct gluten_offset emit_data_or(struct gluten_ctx *g, + struct gluten_offset offset, + enum type type, union value *value) +{ + return emit_data_do(g, offset, type, + (type == STRING) ? strlen(value->v_str) : 0, value, + true); +} + static void gluten_link(struct gluten_ctx *g, enum jump_type type, struct op *start) { @@ -246,7 +322,7 @@ static void gluten_link(struct gluten_ctx *g, enum jump_type type, void link_block(struct gluten_ctx *g) { - debug(" Linking block..."); + debug(" Linking block..."); gluten_link(g, JUMP_NEXT_BLOCK, (struct op *)gluten_ptr(&g->g, g->lr)); } diff --git a/cooker/emit.h b/cooker/emit.h index 5d15b1b..ee0583f 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -18,8 +18,18 @@ void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, enum jump_type jmp); void emit_return(struct gluten_ctx *g, struct gluten_offset v); void emit_block(struct gluten_ctx *g, int32_t error); +void emit_copy(struct gluten_ctx *g, + struct gluten_offset dst, struct gluten_offset src, size_t size); +void emit_copy_field(struct gluten_ctx *g, struct field *field, + struct gluten_offset dst, struct gluten_offset src); struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, size_t str_len, union value *value); +struct gluten_offset emit_data_at(struct gluten_ctx *g, + struct gluten_offset offset, + enum type type, union value *value); +struct gluten_offset emit_data_or(struct gluten_ctx *g, + struct gluten_offset offset, + enum type type, union value *value); void link_block(struct gluten_ctx *g); void link_match(struct gluten_ctx *g); diff --git a/cooker/example.hjson b/cooker/example.hjson index 8c862da..4c656f7 100644 --- a/cooker/example.hjson +++ b/cooker/example.hjson @@ -3,7 +3,10 @@ "match": [ /* qemu-pr-helper and similar */ { "connect": { "addr": { "family": "unix", "path": "/var/run/pr-helper.sock" }, "fd": { "tag": "fd" } } } ], - "call": { "connect": { "addr": { "family": "unix", "path": "/var/run/pr-helper.sock" }, "ret": "y" } }, + "call": [ + { "socket": { "family": "unix", "type": "stream", "flags": 0, "protocol": 0 }, "ret": "new_fd" }, + { "connect": { "fd": { "tag": { "get": "new_fd" } }, "addr": { "family": "unix", "path": "/var/run/pr-helper.sock" } }, "ret": "y" } + ], "inject": { "what": "fd", "new": { "tag": "y" }, "old": { "tag": "fd" }, "return": 0 } }, { @@ -31,7 +34,7 @@ { "mknod": { "path": { "tag": "path" }, "mode": "c", "major": 1, "minor": { "in": [ 3, 5, 7, 8, 9 ], "tag": "minor" } } } ], "context": { "userns": "init", "mountns": "caller" }, - "call": { "mknod": { "path": { "tag": "path" }, "mode": "c", "major": 1, "minor": { "tag": "minor" }, "ret": "x" } }, + "call": { "mknod": { "path": { "tag": { "get": "path" } }, "mode": "c", "major": 1, "minor": { "tag": { "get": "minor" } } }, "ret": "x" }, "inject": { "what": "fd", "new": { "tag": "x" } }, "return": { "tag": "x" } } diff --git a/cooker/gluten.c b/cooker/gluten.c index 05d408f..cc317d3 100644 --- a/cooker/gluten.c +++ b/cooker/gluten.c @@ -14,16 +14,9 @@ size_t gluten_size[TYPE_COUNT] = { [INT] = sizeof(int), - [INTMASK] = sizeof(int), - [INTFLAGS] = sizeof(int), - [U32] = sizeof(uint32_t), - [U32MASK] = sizeof(uint32_t), - [U32FLAGS] = sizeof(uint32_t), - + [U64] = sizeof(uint64_t), [LONG] = sizeof(long), - [LONGMASK] = sizeof(long), - [LONGFLAGS] = sizeof(long), [PID] = sizeof(pid_t), [PORT] = sizeof(in_port_t), @@ -35,17 +28,17 @@ size_t gluten_size[TYPE_COUNT] = { const char *jump_name[JUMP_COUNT] = { "next block", "next match", "end" }; /** - * gluten_alloc() - Allocate in temporary (seitan read-write) data area + * gluten_rw_alloc() - Allocate in temporary (seitan read-write) data area * @g: gluten context * @size: Bytes to allocate * * Return: offset to allocated area */ -struct gluten_offset gluten_alloc(struct gluten_ctx *g, size_t size) +struct gluten_offset gluten_rw_alloc(struct gluten_ctx *g, size_t size) { struct gluten_offset ret = g->dp; - debug(" allocating %lu at offset %i", size, g->dp.offset); + debug(" allocating %lu at read-write offset %i", size, g->dp.offset); if ((g->dp.offset += size) >= DATA_SIZE) die("Temporary data size exceeded"); @@ -53,15 +46,45 @@ struct gluten_offset gluten_alloc(struct gluten_ctx *g, size_t size) } /** - * gluten_alloc() - Allocate storage for given type in temporary data area + * gluten_rw_alloc_type() - Allocate storage for given type in temporary area + * @g: gluten context + * @type: Data type + * + * Return: offset to allocated area + */ +struct gluten_offset gluten_rw_alloc_type(struct gluten_ctx *g, enum type type) +{ + return gluten_rw_alloc(g, gluten_size[type]); +} + +/** + * gluten_ro_alloc() - Allocate in read-only data area + * @g: gluten context + * @size: Bytes to allocate + * + * Return: offset to allocated area + */ +struct gluten_offset gluten_ro_alloc(struct gluten_ctx *g, size_t size) +{ + struct gluten_offset ret = g->cp; + + debug(" allocating %lu at read-only offset %i", size, g->cp.offset); + if ((g->cp.offset += size) >= RO_DATA_SIZE) + die("Read-only data size exceeded"); + + return ret; +} + +/** + * gluten_ro_alloc_type() - Allocate storage for given type in read-only area * @g: gluten context * @type: Data type * * Return: offset to allocated area */ -struct gluten_offset gluten_alloc_type(struct gluten_ctx *g, enum type type) +struct gluten_offset gluten_ro_alloc_type(struct gluten_ctx *g, enum type type) { - return gluten_alloc(g, gluten_size[type]); + return gluten_ro_alloc(g, gluten_size[type]); } void gluten_add_tag(struct gluten_ctx *g, const char *name, @@ -80,6 +103,26 @@ void gluten_add_tag(struct gluten_ctx *g, const char *name, name, gluten_offset_name[offset.type], offset.offset); } +void gluten_add_tag_post(struct gluten_ctx *g, const char *name, + struct gluten_offset offset) +{ + /* TODO: Mark as invalid for current rule */ + gluten_add_tag(g, name, offset); +} + +struct gluten_offset gluten_get_tag(struct gluten_ctx *g, const char *name) +{ + int i; + + for (i = 0; i < TAGS_MAX && g->tags[i].name; i++) { + if (!strcmp(g->tags[i].name, name)) + return g->tags[i].offset; + } + + die(" tag '%s' not found", name); + return g->tags[0].offset; /* Pro forma, not actually happening */ +} + /** * gluten_init() - Initialise gluten structures and layout * @g: gluten context diff --git a/cooker/gluten.h b/cooker/gluten.h index edd3240..0fcea26 100644 --- a/cooker/gluten.h +++ b/cooker/gluten.h @@ -47,10 +47,15 @@ enum jump_type { JUMP_COUNT, }; -struct gluten_offset gluten_alloc(struct gluten_ctx *g, size_t size); -struct gluten_offset gluten_alloc_type(struct gluten_ctx *g, enum type type); +struct gluten_offset gluten_rw_alloc(struct gluten_ctx *g, size_t size); +struct gluten_offset gluten_rw_alloc_type(struct gluten_ctx *g, enum type type); +struct gluten_offset gluten_ro_alloc(struct gluten_ctx *g, size_t size); +struct gluten_offset gluten_ro_alloc_type(struct gluten_ctx *g, enum type type); void gluten_add_tag(struct gluten_ctx *g, const char *name, struct gluten_offset offset); +void gluten_add_tag_post(struct gluten_ctx *g, const char *name, + struct gluten_offset offset); +struct gluten_offset gluten_get_tag(struct gluten_ctx *g, const char *name); void gluten_init(struct gluten_ctx *g); void gluten_block_init(struct gluten_ctx *g); void gluten_write(struct gluten_ctx *g, const char *path); diff --git a/cooker/main.c b/cooker/main.c index 5512d54..db62fcb 100644 --- a/cooker/main.c +++ b/cooker/main.c @@ -8,6 +8,7 @@ * Author: Stefano Brivio */ +#include "parson.h" #include "cooker.h" #include "gluten.h" #include "parse.h" diff --git a/cooker/match.c b/cooker/match.c index bd96a03..5fe077f 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -14,20 +14,11 @@ #include "gluten.h" #include "emit.h" #include "filter.h" +#include "parse.h" #include "util.h" #include "calls/net.h" -/** - * struct rule_parser - Parsing handler for JSON rule type - * @type: JSON key name - * @fn: Parsing function - */ -struct rule_parser { - const char *type; - int (*fn)(struct gluten_ctx *g, JSON_Value *value); -}; - /** * arg_load() - Allocate and build bytecode for one syscall argument * @g: gluten context @@ -37,16 +28,17 @@ struct rule_parser { */ static struct gluten_offset arg_load(struct gluten_ctx *g, struct arg *a) { + struct gluten_offset offset; int index = a->pos; size_t size; - if (a->type == SELECTED) { - if (g->selected_arg[index]->type != UNDEF) - size = g->selected_arg[index]->size; + if (a->f.type == SELECTED) { + if (g->selected_arg[index]->f.type != UNDEF) + size = g->selected_arg[index]->f.size; else - die(" no storage size for argument %s", a->name); + die(" no storage size for argument %s", a->f.name); } else { - size = a->size; + size = a->f.size; } if (!size) { @@ -61,182 +53,121 @@ static struct gluten_offset arg_load(struct gluten_ctx *g, struct arg *a) if (g->match_dst[index].len) /* Already allocated */ return g->match_dst[index].offset; - g->match_dst[index].offset = gluten_alloc(g, size); - g->match_dst[index].len = size; - - emit_load(g, g->match_dst[index].offset, index, size); - - return g->match_dst[index].offset; -} - -/** - * value_get_num() - Get numeric value from description matching JSON input - * @desc: Description of possible values from model - * @value: JSON value - * - * Return: numeric value - */ -static long long value_get_num(struct num *desc, JSON_Value *value) -{ - const char *s = NULL; - long long n; - - if (desc) { - s = json_value_get_string(value); - for (; desc->name && s && strcmp(s, desc->name); desc++); - if (s && !desc->name) - die(" Invalid value %s", s); + offset = gluten_rw_alloc(g, size); - n = desc->value; - } - - if (!s) { - if (json_value_get_type(value) != JSONNumber) - die(" Invalid value type"); - - n = json_value_get_number(value); - } - - return n; -} - -/** - * value_get() - Get generic value from description matching JSON input - * @desc: Description of possible values from model - * @type: Data type from model - * @value: JSON value - * @out: Corresponding bytecode value, set on return - */ -static void value_get(union desc desc, enum type type, JSON_Value *value, - union value *out) -{ - if (TYPE_IS_NUM(type)) - out->v_num = value_get_num(desc.d_num, value); -} - -/** - * select_desc() - Get description and type for selected value - * @g: gluten context - * @s: Possible selection choices - * @v: Selector value - * @pos: Index of syscall argument being parsed - * @type: Type of selected value, set on return - * @desc: Description of selected value, set on return - */ -static void select_desc(struct gluten_ctx *g, struct select *s, union value v, - int pos, enum type *type, union desc *desc) -{ - if (TYPE_IS_NUM(s->field->type)) { - struct select_num *d_num; - - for (d_num = s->desc.d_num; d_num->target.type; d_num++) { - if (d_num->value == v.v_num) { - if (d_num->target.pos == pos) { - *type = d_num->target.type; - *desc = d_num->target.desc; - } else { - pos = d_num->target.pos; - g->selected_arg[pos] = &d_num->target; - *type = NONE; - } - - return; - } - } + g->match_dst[index].offset = offset; + g->match_dst[index].len = size; - if (!d_num->target.type) - die(" No match for numeric selector %i", v.v_num); - } + emit_load(g, offset, index, size); - die(" not supported yet"); + return offset; } /** - * parse_value() - Parse JSON value for generic item of data description + * parse_field() - Parse generic field along with JSON value * @g: gluten context - * @index: Index of parent syscall argument * @offset: Base offset of container field (actual offset for non-compound) - * @type: Data type, from model - * @str_len: Length of string, valid for STRING type only - * @desc: Description of possible values, from model - * @value: JSON value + * @index: Index of parent syscall argument + * @f: Field from syscall model + * @jvalue: JSON value + * + * Return: parsed value for simple types, empty value otherwise */ -static void parse_value(struct gluten_ctx *g, int index, - struct gluten_offset offset, enum type type, - size_t str_len, union desc desc, JSON_Value *value) +static union value parse_field(struct gluten_ctx *g, + struct gluten_offset offset, + int index, struct field *f, JSON_Value *jvalue) { - struct gluten_offset data_offset, const_offset; + struct gluten_offset const_offset; + union value v = { .v_num = 0 }; + struct field *f_inner; const char *tag_name; JSON_Object *tmp; - struct field *f; - union value v; + JSON_Value *sel; + + if (f->name) + debug(" parsing field name %s", f->name); - if (type == SELECT) { - struct select *select = desc.d_select; - struct field *field = select->field; +/* + if (f->type == SELECT) { + struct select *select = f->desc.d_select; + struct field *s_field = select->field; JSON_Value *sel; if ((tmp = json_value_get_object(value))) { - if (!(sel = json_object_get_value(tmp, field->name))) - die(" no selector for '%s'", field->name); + if (!(sel = json_object_get_value(tmp, s_field->name))) + die(" no selector for '%s'", s_field->name); } else { sel = value; } - value_get(field->desc, field->type, sel, &v); - const_offset = emit_data(g, field->type, field->strlen, &v); + value_get(s_field->desc, s_field->type, sel, &v); + const_offset = emit_data(g, s_field->type, s_field->size, &v); data_offset = offset; - data_offset.offset += field->offset; + data_offset.offset += s_field->offset; - emit_cmp_field(g, CMP_NE, field, data_offset, const_offset, + emit_cmp_field(g, CMP_NE, s_field, data_offset, const_offset, JUMP_NEXT_BLOCK); - select_desc(g, select, v, index, &type, &desc); + swap_field(g, select, v, index, &f); - if (type == NONE) + if (!f) return; } +*/ - if (json_value_get_type(value) == JSONObject && - (tmp = json_value_get_object(value)) && + if (json_value_get_type(jvalue) == JSONObject && + (tmp = json_value_get_object(jvalue)) && (tag_name = json_object_get_string(tmp, "tag"))) { - if (TYPE_IS_COMPOUND(type)) - die("Tag reference '%s' to compound value", tag_name); - debug(" setting tag reference '%s'", tag_name); gluten_add_tag(g, tag_name, offset); - value = json_object_get_value(tmp, "value"); + jvalue = json_object_get_value(tmp, "value"); } /* Nothing to match on: just store as reference */ - if (!value) - return; - - switch (type) { - case INTFLAGS: - case LONGFLAGS: - case U32FLAGS: - /* fetch/combine expr algebra loop */ - case INTMASK: - /* calculate mask first */ - break; + if (!jvalue) + return v; + + offset.offset += f->offset; + + switch (f->type) { case INT: case LONG: case U32: - v.v_num = value_get_num(desc.d_num, value); - const_offset = emit_data(g, type, 0, &v); - emit_cmp(g, CMP_NE, offset, const_offset, gluten_size[type], + if (f->flags == FLAGS) { + /* fetch/combine expr algebra loop */ + ; + } + if (f->flags == MASK) { + /* calculate mask first */ + ; + } + + 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); break; case SELECT: - /* TODO: check how nested selects should work */ - parse_value(g, index, offset, type, 0, desc, value); + f_inner = f->desc.d_select->field; + + if ((tmp = json_value_get_object(jvalue))) { + if (!(sel = json_object_get_value(tmp, f_inner->name))) + die(" no selector for '%s'", f_inner->name); + } else { + sel = jvalue; + } + + v = parse_field(g, offset, index, f_inner, sel); + + f = select_field(g, index, f->desc.d_select, v); + if (f) + parse_field(g, offset, index, f, jvalue); break; case STRING: - v.v_str = json_value_get_string(value); - if (strlen(v.v_str) + 1 > str_len) + v.v_str = json_value_get_string(jvalue); + if (strlen(v.v_str) + 1 > f->size) die(" string %s too long for field", v.v_str); const_offset = emit_data(g, STRING, strlen(v.v_str) + 1, &v); @@ -244,43 +175,43 @@ static void parse_value(struct gluten_ctx *g, int index, JUMP_NEXT_BLOCK); break; case STRUCT: - for (f = desc.d_struct; f->name; f++) { + for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) { JSON_Value *field_value; - tmp = json_value_get_object(value); - field_value = json_object_get_value(tmp, f->name); + tmp = json_value_get_object(jvalue); + field_value = json_object_get_value(tmp, f_inner->name); if (!field_value) continue; - parse_value(g, index, offset, f->type, f->strlen, - f->desc, field_value); + parse_field(g, offset, index, f_inner, field_value); } + break; default: ; } + + return v; } /** * parse_arg() - Parse syscall argument from JSON, following model * @g: gluten context - * @name: Name of argument (key) in JSON and model - * @value: JSON value for argument * @a: Argument description from model + * @value: JSON value for argument */ -static void parse_arg(struct gluten_ctx *g, const char *name, JSON_Value *value, - struct arg *a) +static void parse_arg(struct gluten_ctx *g, JSON_Value *jvalue, struct arg *a) { struct gluten_offset offset; - debug(" Parsing match argument %s", name); + debug(" Parsing match argument %s", a->f.name); offset = arg_load(g, a); - parse_value(g, a->pos, offset, a->type, a->size, a->desc, value); + parse_field(g, offset, a->pos, &a->f, jvalue); } /** - * parse_match() - Parse one syscall rule in "match" array + * parse_match() - Parse one "match" item in syscall rules * @g: gluten context * @obj: Matching rule for one syscall * @args: Description of arguments from syscall model @@ -291,18 +222,18 @@ static void parse_match(struct gluten_ctx *g, JSON_Object *obj, unsigned count = 0; struct arg *a; - for (a = args; a->name; a++) { + for (a = args; a->f.name; a++) { struct arg *real_arg = a; - JSON_Value *value; + JSON_Value *jvalue; - if (a->type == SELECTED) { + if (a->f.type == SELECTED) { if (!(real_arg = g->selected_arg[a->pos])) - die(" No selection for argument %s", a->name); + die(" No argument selected for %s", a->f.name); } - if ((value = json_object_get_value(obj, real_arg->name))) { + if ((jvalue = json_object_get_value(obj, real_arg->f.name))) { count++; - parse_arg(g, real_arg->name, value, real_arg); + parse_arg(g, jvalue, real_arg); } } diff --git a/cooker/parse.c b/cooker/parse.c index acb6810..64901f5 100644 --- a/cooker/parse.c +++ b/cooker/parse.c @@ -12,18 +12,11 @@ #include "calls.h" #include "cooker.h" #include "gluten.h" +#include "call.h" #include "match.h" #include "emit.h" #include "util.h" -#include "calls/net.h" - -static void handle_call(struct gluten_ctx *g, JSON_Value *value) -{ - (void)g; - (void)value; -} - static void handle_inject(struct gluten_ctx *g, JSON_Value *value) { (void)g; @@ -64,7 +57,7 @@ struct rule_parser { void (*fn)(struct gluten_ctx *g, JSON_Value *value); } parsers[] = { { "match", handle_matches }, - { "call", handle_call }, + { "call", handle_calls }, { "inject", handle_inject }, { "limit", handle_limit }, { "return", handle_return }, @@ -73,6 +66,87 @@ struct rule_parser { { NULL, NULL }, }; +/** + * value_get_num() - Get numeric value from description matching JSON input + * @desc: Description of possible values from model + * @value: JSON value + * + * Return: numeric value + */ +long long value_get_num(struct num *desc, JSON_Value *value) +{ + const char *s = NULL; + long long n; + + if (desc) { + s = json_value_get_string(value); + for (; desc->name && s && strcmp(s, desc->name); desc++); + if (s && !desc->name) + die(" Invalid value %s", s); + + n = desc->value; + } + + if (!s) { + if (json_value_get_type(value) != JSONNumber) + die(" Invalid value type"); + + n = json_value_get_number(value); + } + + return n; +} + +/** + * value_get() - Get generic value from description matching JSON input + * @desc: Description of possible values from model + * @type: Data type from model + * @value: JSON value + * @out: Corresponding bytecode value, set on return + */ +void value_get(union desc desc, enum type type, JSON_Value *value, + union value *out) +{ + if (TYPE_IS_NUM(type)) + out->v_num = value_get_num(desc.d_num, value); +} + +/** + * select_field() - Select field based on value and description + * @g: gluten context + * @pos: Index of syscall argument being parsed + * @s: Possible selection choices + * @v: Selector value + * + * Return: pointer to field, NULL if selector refers to a different argument + */ +struct field *select_field(struct gluten_ctx *g, int pos, + struct select *s, union value v) +{ + if (TYPE_IS_NUM(s->field->type)) { + struct select_num *d_num; + + for (d_num = s->desc.d_num; d_num->target.f.type; d_num++) { + if (d_num->value == v.v_num) { + if (d_num->target.pos == pos) + return &d_num->target.f; + + if (g) { + pos = d_num->target.pos; + g->selected_arg[pos] = &d_num->target; + } + + return NULL; + } + } + + if (!d_num->target.f.type) + die(" No match for numeric selector %i", v.v_num); + } + + die(" not supported yet"); +} + /** * parse_block() - Parse a transformation block with rules * @g: gluten context diff --git a/cooker/parse.h b/cooker/parse.h index 05079cc..361fc55 100644 --- a/cooker/parse.h +++ b/cooker/parse.h @@ -6,6 +6,11 @@ #ifndef PARSE_H #define PARSE_H +long long value_get_num(struct num *desc, JSON_Value *value); +void value_get(union desc desc, enum type type, JSON_Value *value, + union value *out); +struct field *select_field(struct gluten_ctx *g, int pos, + struct select *s, union value v); void parse_file(struct gluten_ctx *g, const char *path); #endif /* PARSE_H */ -- cgit v1.2.3