aboutgitcodelistschat:MatrixIRC
diff options
context:
space:
mode:
-rw-r--r--common/gluten.h23
-rw-r--r--common/util.c1
-rw-r--r--cooker/Makefile12
-rw-r--r--cooker/call.c424
-rw-r--r--cooker/call.h11
-rw-r--r--cooker/calls/fs.c38
-rw-r--r--cooker/calls/ioctl.c72
-rw-r--r--cooker/calls/net.c124
-rw-r--r--cooker/calls/process.c12
-rw-r--r--cooker/cooker.h73
-rw-r--r--cooker/emit.c136
-rw-r--r--cooker/emit.h10
-rw-r--r--cooker/example.hjson7
-rw-r--r--cooker/gluten.c71
-rw-r--r--cooker/gluten.h9
-rw-r--r--cooker/main.c1
-rw-r--r--cooker/match.c261
-rw-r--r--cooker/parse.c92
-rw-r--r--cooker/parse.h5
19 files changed, 1025 insertions, 357 deletions
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 <sbrivio@redhat.com>
+ */
+
+#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" <tag> write pointer to tag
+ parse_arg() passes null offset
+ parse_field() gives back ro_data or data offset
+
+- INT *: "get" <tag> 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" <tag> write pointer to tag
+ parse_arg() passes null offset
+ parse_field() gives back data offset
+
+- STRUCT: <tag>, 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 <sbrivio@redhat.com>
+ */
+
+#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 <sys/types.h>
#include <arpa/inet.h>
+#include <util.h>
+
#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 <sbrivio@redhat.com>
*/
+#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,21 +14,12 @@
#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
* @a: Argument description from model
@@ -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 },
@@ -74,6 +67,87 @@ struct rule_parser {
};
/**
+ * 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
* @block: Array of rules in block
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 */