aboutgitcodelistschat:MatrixIRC
diff options
context:
space:
mode:
-rw-r--r--common/gluten.h10
-rw-r--r--common/util.c5
-rw-r--r--cooker/Makefile28
-rw-r--r--cooker/calls.c5
-rw-r--r--cooker/calls/fs.c91
-rw-r--r--cooker/calls/fs.h11
-rw-r--r--cooker/calls/ioctl.c135
-rw-r--r--cooker/calls/ioctl.h11
-rw-r--r--cooker/calls/net.c22
-rw-r--r--cooker/calls/process.c48
-rw-r--r--cooker/calls/process.h11
-rw-r--r--cooker/cooker.h101
-rw-r--r--cooker/emit.c150
-rw-r--r--cooker/emit.h6
-rw-r--r--cooker/example.hjson14
-rw-r--r--cooker/filter.c388
-rw-r--r--cooker/filter.h36
-rw-r--r--cooker/gluten.c58
-rw-r--r--cooker/gluten.h23
-rw-r--r--cooker/main.c15
-rw-r--r--cooker/match.c361
-rw-r--r--cooker/match.h11
-rw-r--r--cooker/parse.c273
-rw-r--r--cooker/parse.h2
-rw-r--r--operations.c6
-rw-r--r--operations.h3
-rw-r--r--seitan.c7
27 files changed, 1311 insertions, 520 deletions
diff --git a/common/gluten.h b/common/gluten.h
index 3df638e..ff29caa 100644
--- a/common/gluten.h
+++ b/common/gluten.h
@@ -10,6 +10,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
+#include <sys/types.h>
#include <linux/seccomp.h>
#include <stdio.h>
@@ -39,6 +40,8 @@ enum gluten_offset_type {
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);
@@ -89,6 +92,8 @@ struct op_context {
};
enum op_type {
+ OP_END = 0,
+ OP_NR,
OP_CALL,
OP_BLOCK,
OP_CONT,
@@ -98,7 +103,6 @@ enum op_type {
OP_LOAD,
OP_CMP,
OP_RESOLVEDFD,
- OP_END,
};
struct op_nr {
@@ -147,7 +151,7 @@ struct op_cmp {
struct gluten_offset y;
size_t size;
enum op_cmp_type cmp;
- unsigned int jmp;
+ struct gluten_offset jmp;
};
struct op_resolvedfd {
@@ -237,7 +241,7 @@ static inline const void *gluten_ptr(const struct seccomp_data *s,
if (!is_offset_valid(x))
return NULL;
- if(x.type == OFFSET_SECCOMP_DATA && s == NULL)
+ if (x.type == OFFSET_SECCOMP_DATA && s == NULL)
return NULL;
switch (x.type) {
diff --git a/common/util.c b/common/util.c
index a2ecce0..3a4b2e5 100644
--- a/common/util.c
+++ b/common/util.c
@@ -12,6 +12,8 @@
#include <stdarg.h>
#include <string.h>
+#include "gluten.h"
+
#define logfn(name) \
void name(const char *format, ...) { \
va_list args; \
@@ -27,3 +29,6 @@ logfn(err)
logfn(info)
logfn(debug)
+const char *gluten_offset_name[OFFSET_TYPE_MAX + 1] = {
+ "read-only data", "temporary data", "seccomp data", "instruction area"
+};
diff --git a/cooker/Makefile b/cooker/Makefile
index be8f703..5a0ea8e 100644
--- a/cooker/Makefile
+++ b/cooker/Makefile
@@ -13,12 +13,28 @@ COMMON := ../common
BIN := $(OUTDIR)/seitan-cooker
CFLAGS := -O0 -g -Wall -Wextra -pedantic -std=c99 -I$(COMMON)
-SRCS := calls.c emit.c gluten.c main.c parse.c parson.c \
- $(COMMON)/util.c \
- calls/net.c
-HEADERS := calls.h cooker.h emit.h gluten.h parse.h parson.h \
- $(COMMON)/gluten.h $(COMMON)/util.h \
- calls/net.h
+TARGET ?= $(shell $(CC) -dumpmachine)
+# Get 'uname -m'-like architecture description for target
+TARGET_ARCH := $(shell echo $(TARGET) | cut -f1 -d- | tr [A-Z] [a-z])
+TARGET_ARCH := $(shell echo $(TARGET_ARCH) | sed 's/powerpc/ppc/')
+
+AUDIT_ARCH := $(shell echo $(TARGET_ARCH) | tr [a-z] [A-Z] | sed 's/^ARM.*/ARM/')
+AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/I[456]86/I386/')
+AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/PPC64/PPC/')
+AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/PPCLE/PPC64LE/')
+AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/MIPS64EL/MIPSEL64/')
+AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/HPPA/PARISC/')
+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 \
+ 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
$(BIN): $(SRCS) $(HEADERS)
$(CC) $(CFLAGS) -o $(BIN) $(SRCS)
diff --git a/cooker/calls.c b/cooker/calls.c
index 74b5a06..783c3f6 100644
--- a/cooker/calls.c
+++ b/cooker/calls.c
@@ -12,7 +12,10 @@
#include "calls.h"
#include "calls/net.h"
+#include "calls/ioctl.h"
+#include "calls/process.h"
+#include "calls/fs.h"
struct call *call_sets[] = {
- syscalls_net, NULL,
+ syscalls_net, syscalls_ioctl, syscalls_process, syscalls_fs, NULL,
};
diff --git a/cooker/calls/fs.c b/cooker/calls/fs.c
new file mode 100644
index 0000000..d800f38
--- /dev/null
+++ b/cooker/calls/fs.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
+ *
+ * cooker/calls/fs.c - Description of known filesystem-related system calls
+ *
+ * Copyright 2023 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ */
+
+/*
+stat ?
+fstat ?
+lstat ?
+
+lseek ?
+
+fcntl ?
+flock ~
+fsync
+fdatasync
+truncate
+ftruncate
+
+getdents
+getcwd
+chdir
+fchdir
+mkdir
+rmdir
+
+rename
+
+creat
+
+link
+unlink
+symlink
+readlink
+
+chmod
+fchmod
+chown
+fchown
+fchownat
+lchown
+umask
+
+mknod
+mknodat
+
+mount
+umount2
+swapon
+swapoff
+*/
+
+#include <asm-generic/unistd.h>
+#include <sys/syscall.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "../cooker.h"
+#include "../calls.h"
+
+static struct arg mknod_args[] = {
+ {
+ 0, "path", STRING, 1 /* TODO: PATH_MAX */,
+ { 0 }
+ },
+ {
+ 1, "mode", INTFLAGS, 0,
+ { 0 /* TODO */ },
+ },
+ {
+ 2, "major", UNDEF /* TODO */, 0,
+ { 0 },
+ },
+ {
+ 2, "minor", UNDEF /* TODO */, 0,
+ { 0 },
+ },
+ { 0 },
+};
+
+struct call syscalls_fs[] = {
+ { __NR_mknod, "mknod", mknod_args },
+ { 0 },
+};
diff --git a/cooker/calls/fs.h b/cooker/calls/fs.h
new file mode 100644
index 0000000..2e3c06b
--- /dev/null
+++ b/cooker/calls/fs.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 CALLS_FS_H
+#define CALLS_FS_H
+
+extern struct call syscalls_fs[];
+
+#endif /* CALLS_FS_H */
diff --git a/cooker/calls/ioctl.c b/cooker/calls/ioctl.c
new file mode 100644
index 0000000..576e02e
--- /dev/null
+++ b/cooker/calls/ioctl.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
+ *
+ * cooker/calls/ioctl.c - Description of known ioctl(2) requests
+ *
+ * Copyright 2023 Red Hat GmbH
+ * Authors: Alice Frosi <afrosi@redhat.com>
+ * Stefano Brivio <sbrivio@redhat.com>
+ */
+
+/*
+fd = ioctl_ns(fd, request)
+n = ioctl_tty(fd, cmd, argp)
+e = ioctl_iflags(fd, cmd, attr)
+*/
+
+#include <asm-generic/unistd.h>
+#include <sys/syscall.h>
+
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <linux/fs.h>
+#include <linux/nsfs.h>
+
+#include <net/if.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#include "../cooker.h"
+#include "../calls.h"
+
+static struct num request[] = {
+ { "FS_IOC_GETFLAGS", FS_IOC_GETFLAGS }, /* ioctl_iflags */
+ { "FS_IOC_SETFLAGS", FS_IOC_SETFLAGS },
+
+ { "NS_GET_USERNS", NS_GET_USERNS }, /* ioctl_ns*/
+ { "NS_GET_PARENT", NS_GET_PARENT },
+
+ { "TCGETS", TCGETS }, /* ioctl_tty */
+ { "TCSETS", TCSETS },
+ { "TCSETSW", TCSETSW },
+ { "TCSETSF", TCSETSF },
+
+ { "TUNSETIFF", TUNSETIFF }, /* no man page? */
+
+ { 0 },
+};
+
+static struct num attr[] = {
+ { "FS_APPEND_FL", FS_APPEND_FL },
+ { "FS_COMPR_FL", FS_COMPR_FL },
+ { "FS_DIRSYNC_FL", FS_DIRSYNC_FL },
+ { "FS_IMMUTABLE_FL", FS_IMMUTABLE_FL },
+ { "FS_JOURNAL_DATA_FL", FS_JOURNAL_DATA_FL },
+ { "FS_NOATIME_FL", FS_NOATIME_FL },
+ { "FS_NOCOW_FL", FS_NOCOW_FL },
+ { "FS_NODUMP_FL", FS_NODUMP_FL },
+ { "FS_NOTAIL_FL", FS_NOTAIL_FL },
+ { "FS_PROJINHERIT_FL", FS_PROJINHERIT_FL },
+ { "FS_SECRM_FL", FS_SECRM_FL },
+ { "FS_SYNC_FL", FS_SYNC_FL },
+ { "FS_TOPDIR_FL", FS_TOPDIR_FL },
+ { "FS_UNRM_FL", FS_UNRM_FL },
+};
+
+static struct num tun_ifr_flags[] = {
+ { "IFF_TUN", IFF_TUN },
+ { 0 },
+};
+
+static struct field tun_ifr[] = { /* netdevice(7) */
+ {
+ "name", STRING,
+ offsetof(struct ifreq, ifr_name),
+ IFNAMSIZ, { 0 },
+ },
+ {
+ "flags", INT, /* One allowed at a time? */
+ 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_SETFLAGS,
+ { 2, "argp", INTFLAGS, sizeof(int), { .d_num = attr } }
+ },
+ {
+ TUNSETIFF,
+ {
+ 2, "ifr", STRUCT, sizeof(struct ifreq),
+ { .d_struct = tun_ifr }
+ }
+ },
+ { 0 },
+};
+
+static struct field ioctl_request = {
+ "request", INT, 0, 0, { .d_num = request },
+};
+
+static struct select ioctl_request_select = {
+ &ioctl_request, { .d_num = ioctl_request_arg }
+};
+
+static struct arg ioctl_args[] = {
+ {
+ 0, "path", FDPATH, 0,
+ { 0 }
+ },
+ {
+ 0, "fd", INT, 0,
+ { 0 }
+ },
+ {
+ 1, "request", SELECT, 0,
+ { .d_select = &ioctl_request_select }
+ },
+ {
+ 2, "arg", SELECTED, -1,
+ { 0 }
+ },
+ { 0 },
+};
+
+struct call syscalls_ioctl[] = {
+ { __NR_ioctl, "ioctl", ioctl_args },
+ { 0 },
+};
diff --git a/cooker/calls/ioctl.h b/cooker/calls/ioctl.h
new file mode 100644
index 0000000..a06a9bc
--- /dev/null
+++ b/cooker/calls/ioctl.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 CALLS_IOCTL_H
+#define CALLS_IOCTL_H
+
+extern struct call syscalls_ioctl[];
+
+#endif /* CALLS_IOCTL_H */
diff --git a/cooker/calls/net.c b/cooker/calls/net.c
index 370a3a1..df97aab 100644
--- a/cooker/calls/net.c
+++ b/cooker/calls/net.c
@@ -135,7 +135,7 @@ static struct field connect_addr_nl[] = {
},
{
"groups", U32,
- offsetof(struct sockaddr_in6, sin6_addr),
+ offsetof(struct sockaddr_nl, nl_groups),
0, { 0 }
},
{ 0 },
@@ -148,10 +148,22 @@ static struct field connect_family = {
};
static struct select_num connect_addr_select_family[] = {
- { AF_UNIX, STRUCT, { .d_struct = connect_addr_unix } },
- { AF_INET, STRUCT, { .d_struct = connect_addr_ipv4 } },
- { AF_INET6, STRUCT, { .d_struct = connect_addr_ipv6 } },
- { AF_NETLINK, STRUCT, { .d_struct = connect_addr_nl } },
+ {
+ AF_UNIX,
+ { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_unix } }
+ },
+ {
+ AF_INET,
+ { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_ipv4 } }
+ },
+ {
+ AF_INET6,
+ { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_ipv6 } }
+ },
+ {
+ AF_NETLINK,
+ { 1, NULL, STRUCT, 0, { .d_struct = connect_addr_nl } }
+ },
{ 0 },
};
diff --git a/cooker/calls/process.c b/cooker/calls/process.c
new file mode 100644
index 0000000..7c0f36e
--- /dev/null
+++ b/cooker/calls/process.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
+ *
+ * cooker/calls/process.c - Description of known process-related system calls
+ *
+ * Copyright 2023 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ */
+
+/*
+clone
+fork
+vfork
+execve
+exit
+wait3
+wait4
+waitid
+kill
+exit_group
+unshare
+kcmp
+clone3
+*/
+
+#include <asm-generic/unistd.h>
+#include <sys/syscall.h>
+
+#include <unistd.h>
+#include <sched.h>
+#include <linux/kcmp.h>
+#include <sys/wait.h>
+
+#include "../cooker.h"
+#include "../calls.h"
+
+static struct arg unshare_args[] = {
+ {
+ 0, "flags", INTFLAGS, 0,
+ { 0 /* TODO */ }
+ },
+};
+
+struct call syscalls_process[] = {
+ { __NR_unshare, "unshare", unshare_args },
+ { 0 },
+};
diff --git a/cooker/calls/process.h b/cooker/calls/process.h
new file mode 100644
index 0000000..5e214ef
--- /dev/null
+++ b/cooker/calls/process.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 CALLS_PROCESS_H
+#define CALLS_PROCESS_H
+
+extern struct call syscalls_process[];
+
+#endif /* CALLS_PROCESS_H */
diff --git a/cooker/cooker.h b/cooker/cooker.h
index a1cc360..82b24f7 100644
--- a/cooker/cooker.h
+++ b/cooker/cooker.h
@@ -6,6 +6,9 @@
#ifndef COOKER_H
#define COOKER_H
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <unistd.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -14,27 +17,46 @@
#include <sys/types.h>
#include <arpa/inet.h>
-#define REFS_MAX 256
-#define REF_NAMEMAX 256
+#define TAGS_MAX 256
#define CALL_ARGS 6
struct num;
struct field;
struct select;
+/**
+ * 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
+ */
union desc {
struct num *d_num;
struct field *d_struct;
struct select *d_select;
};
+/**
+ * union value - Represent a generic value used internally by cooker
+ * @v_int: Value of type int
+ * @v_u32: Value of type u32
+ * @v_num: Value of type long long, or any other numeric type
+ * @v_str: String, directly from JSON
+ */
union value {
int v_int;
uint32_t v_u32;
long long v_num;
+ const char *v_str;
};
+/**
+ * enum type - Types of values for arguments and fields within arguments
+ */
enum type {
+ UNDEF = 0,
+ NONE,
+
INT,
INTMASK,
INTFLAGS,
@@ -43,6 +65,10 @@ enum type {
U32MASK,
U32FLAGS,
+ U64,
+ U64MASK,
+ U64FLAGS,
+
LONG,
LONGMASK,
LONGFLAGS,
@@ -51,6 +77,7 @@ enum type {
STRUCT,
SELECT,
+ SELECTED,
PID,
@@ -68,16 +95,24 @@ enum type {
#define TYPE_IS_COMPOUND(t) ((t) == STRUCT || (t) == SELECT)
#define TYPE_IS_NUM(t) ((t) == INT || (t) == U32 || (t) == LONG)
-enum jump_type {
- NEXT_BLOCK,
- END,
-};
-
+/**
+ * struct num - A numeric value and its label
+ * @name: Label for numeric value
+ * @value: Numeric value
+ */
struct num {
char *name;
long long value;
};
+/**
+ * struct field - Field inside a 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
+ * @desc: Description of possible values for field, or linked struct
+ */
struct field {
char *name;
enum type type;
@@ -88,21 +123,27 @@ struct field {
union desc desc;
};
-struct select_num {
- long long value;
+/**
+ * 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;
- enum type type;
union desc desc;
};
-struct select {
- struct field *field;
-
- union {
- struct select_num *d_num;
- } desc;
-};
-
+/**
+ * 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
+ */
struct arg {
int pos;
char *name;
@@ -113,4 +154,28 @@ struct arg {
union desc desc;
};
+/**
+ * struct select_num - List of possible selections based on numeric selector
+ * @value: Numeric value of the selector
+ * @target: Argument description defined by this selector
+ */
+struct select_num {
+ long long value;
+
+ struct arg target;
+};
+
+/**
+ * struct select - Association between argument description and selected values
+ * @field: Description of argument operating the selection
+ * @d_num: List of possible selections
+ */
+struct select {
+ struct field *field;
+
+ union {
+ struct select_num *d_num;
+ } desc;
+};
+
#endif /* COOKER_H */
diff --git a/cooker/emit.c b/cooker/emit.c
index 8c35f1d..c233b0a 100644
--- a/cooker/emit.c
+++ b/cooker/emit.c
@@ -14,8 +14,10 @@
#include "emit.h"
static const char *type_str[] = {
+ "UNDEF", "NONE",
"INT", "INTMASK", "INTFLAGS",
"U32", "U32MASK", "U32FLAGS",
+ "U64", "U64MASK", "U64FLAGS",
"LONG", "LONGMASK", "LONGFLAGS",
"STRING",
"STRUCT", "SELECT",
@@ -25,83 +27,183 @@ static const char *type_str[] = {
NULL
};
-static const char *cmp_type_str[] = { "EQ", "GT", "GE", "LT", "LE", NULL };
+static const char *cmp_type_str[] = {
+ "EQ", "NE", "GT", "GE", "LT", "LE", NULL
+};
-void emit_nr(struct gluten_ctx *g, long number)
+/**
+ * emit_nr() - Emit OP_NR instruction: jump on syscall mismatch
+ * @g: gluten context
+ * @number: Pointer to system call number
+ */
+void emit_nr(struct gluten_ctx *g, struct gluten_offset number)
{
- struct op_nr *nr = (struct op_nr *)gluten_ptr(&g->g, g->ip);
+ struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
+ struct op_nr *nr = &op->op.nr;
+
+ op->type = OP_NR;
nr->nr = number;
nr->no_match.type = OFFSET_INSTRUCTION;
- nr->no_match.offset = NEXT_BLOCK;
+ nr->no_match.offset = JUMP_NEXT_BLOCK;
- debug(" %i: OP_NR %li, < >", g->ip.offset, number);
+ debug(" %i: OP_NR: if syscall number is not %li, jump to %s",
+ g->ip.offset, number, jump_name[nr->no_match.offset]);
if (++g->ip.offset > INST_MAX)
die("Too many instructions");
}
+/**
+ * emit_load() - Emit OP_LOAD instruction: dereference and copy syscall argument
+ * @g: gluten context
+ * @dst: gluten destination to copy dereferenced data
+ * @index: Index of system call argument
+ * @len: Length of data item pointed by reference
+ */
void emit_load(struct gluten_ctx *g, struct gluten_offset dst,
int index, size_t len)
{
- struct op_load *load = (struct op_load *)gluten_ptr(&g->g, g->ip);
+ struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
+ struct op_load *load = &op->op.load;
+
+ op->type = OP_LOAD;
load->src.type = OFFSET_SECCOMP_DATA;
load->src.offset = index;
load->dst = dst;
- debug(" %i: OP_LOAD #%i < %i (%lu)", g->ip.offset, dst.offset,
- index, len);
+ debug(" %i: OP_LOAD: #%i < args[%i] (size: %lu)",
+ g->ip.offset, dst.offset, index, len);
if (++g->ip.offset > INST_MAX)
die("Too many instructions");
}
-void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp,
+/**
+ * emit_cmp(): Emit OP_CMP instruction: compare data from two offsets
+ * @g: gluten context
+ * @cmp_type: Type of comparison
+ * @x: gluten pointer to first operand of comparison
+ * @y: gluten pointer to second operand of comparison
+ * @size: Size of comparison
+ * @jmp: Jump direction if comparison is true
+ */
+void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type,
struct gluten_offset x, struct gluten_offset y, size_t size,
enum jump_type jmp)
{
- struct op_cmp *op = (struct op_cmp *)gluten_ptr(&g->g, g->ip);
+ struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
+ struct op_cmp *cmp = &op->op.cmp;
- op->x = x;
- op->y = y;
- op->size = size;
- op->cmp = cmp;
- op->jmp = jmp;
+ op->type = OP_CMP;
- debug(" %i: OP_CMP (#%lu) %%%lu %s %%%lu", g->ip.offset, size,
- x.offset, cmp_type_str[cmp], y.offset);
+ cmp->x = x;
+ cmp->y = y;
+ cmp->size = size;
+ cmp->cmp = cmp_type;
+ cmp->jmp.type = OFFSET_INSTRUCTION;
+ cmp->jmp.offset = jmp;
+
+ debug(" %i: OP_CMP: if %s: #%lu %s (size: %lu) %s: #%lu, jump to %s",
+ g->ip.offset,
+ gluten_offset_name[x.type], x.offset,
+ cmp_type_str[cmp_type], size,
+ gluten_offset_name[y.type], y.offset,
+ jump_name[jmp]);
if (++g->ip.offset > INST_MAX)
die("Too many instructions");
}
+/**
+ * emit_cmp_field() - Emit OP_CMP for a given field type
+ * @g: gluten context
+ * @cmp: Type of comparison
+ * @field: Description of field from system call model
+ * @x: gluten pointer to first operand of comparison
+ * @y: gluten pointer to second operand of comparison
+ * @jmp: Jump direction if comparison is true
+ */
void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp,
struct field *field,
- struct gluten_offset base, struct gluten_offset match,
+ struct gluten_offset x, struct gluten_offset y,
enum jump_type jmp)
{
- base.offset += field->offset;
-
- emit_cmp(g, cmp, base, match,
+ emit_cmp(g, cmp, x, y,
field->strlen ? field->strlen : gluten_size[field->type],
jmp);
}
struct gluten_offset emit_data(struct gluten_ctx *g, enum type type,
- union value *value)
+ size_t str_len, union value *value)
{
void *p = gluten_ptr(&g->g, g->cp);
struct gluten_offset ret = g->cp;
- if (type == INT) {
+ 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 ((g->cp.offset += sizeof(int)) > RO_DATA_SIZE)
+
+ g->cp.offset += sizeof(int);
+ 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:
+ ;
}
return ret;
}
+
+static void gluten_link(struct gluten_ctx *g, enum jump_type type,
+ struct op *start)
+{
+ struct gluten_offset *jmp;
+ struct op *op;
+
+ for (op = (struct op *)start; op->type; op++) {
+ switch (op->type) {
+ case OP_NR:
+ jmp = &op->op.nr.no_match;
+ break;
+ case OP_CMP:
+ jmp = &op->op.cmp.jmp;
+ break;
+ default:
+ continue;
+ }
+
+ if (jmp->offset == type) {
+ jmp->offset = g->ip.offset;
+ debug(" linked jump of instruction #%i to #%i",
+ op - (struct op *)g->g.inst, g->ip.offset);
+ }
+ }
+}
+
+void link_block(struct gluten_ctx *g)
+{
+ debug(" Linking block...");
+ gluten_link(g, JUMP_NEXT_BLOCK, (struct op *)gluten_ptr(&g->g, g->lr));
+}
+
+void link_match(struct gluten_ctx *g)
+{
+ debug(" Linking match...");
+ gluten_link(g, JUMP_NEXT_MATCH, (struct op *)gluten_ptr(&g->g, g->mr));
+}
diff --git a/cooker/emit.h b/cooker/emit.h
index 94b2600..2135052 100644
--- a/cooker/emit.h
+++ b/cooker/emit.h
@@ -6,7 +6,7 @@
#ifndef EMIT_H
#define EMIT_H
-void emit_nr(struct gluten_ctx *g, long number);
+void emit_nr(struct gluten_ctx *g, struct gluten_offset number);
void emit_load(struct gluten_ctx *g, struct gluten_offset dst,
int index, size_t len);
void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp,
@@ -17,6 +17,8 @@ void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp,
struct gluten_offset base, struct gluten_offset match,
enum jump_type jmp);
struct gluten_offset emit_data(struct gluten_ctx *g, enum type type,
- union value *value);
+ size_t str_len, union value *value);
+void link_block(struct gluten_ctx *g);
+void link_match(struct gluten_ctx *g);
#endif /* EMIT_H */
diff --git a/cooker/example.hjson b/cooker/example.hjson
index 45ed339..8c862da 100644
--- a/cooker/example.hjson
+++ b/cooker/example.hjson
@@ -1,10 +1,10 @@
[
{
"match": [ /* qemu-pr-helper and similar */
- { "connect": { "addr": { "family": "unix", "path": "/var/run/pr-helper.sock" }, "fd": { "ref": "fd" } } }
+ { "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" } },
- "inject": { "what": "fd", "new": { "ref": "y" }, "old": { "ref": "fd" }, "return": 0 }
+ "inject": { "what": "fd", "new": { "tag": "y" }, "old": { "tag": "fd" }, "return": 0 }
},
{
"match": [ /* qemu creates a tap interface */
@@ -12,7 +12,7 @@
],
"limit": { "scope": "process", "count": 1 },
"call": { "ioctl": { "request": "TUNSETIFF", "path": "/dev/net/tun", "ifr": { "name": "tap0", "flags": "IFF_TUN", "ret": "x" } } },
- "return": { "ref": "x" }
+ "return": { "tag": "x" }
},
{
"match": [ /* CVE-2022-0185-style */
@@ -28,12 +28,12 @@
},
{
"match": [ /* Giuseppe's example */
- { "mknod": { "path": { "ref": "path" }, "mode": "c", "major": 1, "minor": { "in": [ 3, 5, 7, 8, 9 ], "ref": "minor" } } }
+ { "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": { "ref": "path" }, "mode": "c", "major": 1, "minor": { "ref": "minor" }, "ret": "x" } },
- "inject": { "what": "fd", "new": { "ref": "x" } },
- "return": { "ref": "x" }
+ "call": { "mknod": { "path": { "tag": "path" }, "mode": "c", "major": 1, "minor": { "tag": "minor" }, "ret": "x" } },
+ "inject": { "what": "fd", "new": { "tag": "x" } },
+ "return": { "tag": "x" }
}
]
diff --git a/cooker/filter.c b/cooker/filter.c
index 0539e42..9476089 100644
--- a/cooker/filter.c
+++ b/cooker/filter.c
@@ -12,29 +12,13 @@
#include <string.h>
#include <unistd.h>
-#include "numbers.h"
#include "filter.h"
#include "util.h"
-#define N_SYSCALL sizeof(numbers) / sizeof(numbers[0])
-
-static int compare_key(const void *key, const void *base)
-{
- return strcmp((const char *)key,
- ((struct syscall_numbers *)base)->name);
-}
-
-int compare_bpf_call_names(const void *a, const void *b)
-{
- return strcmp(((struct bpf_call *)a)->name,
- ((struct bpf_call *)b)->name);
-}
-
-static int compare_table_nr(const void *a, const void *b)
-{
- return (((struct syscall_entry *)a)->nr -
- ((struct syscall_entry *)b)->nr);
-}
+struct notify {
+ long nr;
+ struct bpf_arg arg[6];
+} notify_call[512];
static unsigned int count_shift_right(unsigned int n)
{
@@ -104,102 +88,50 @@ void create_lookup_nodes(int jumps[], unsigned int n)
}
}
-long resolve_syscall_nr(const char *name)
-{
- struct syscall_numbers *p;
- p = (struct syscall_numbers *)bsearch(
- name, numbers, sizeof(numbers) / sizeof(numbers[0]),
- sizeof(numbers[0]), compare_key);
- if (p == NULL)
- return -1;
- return p->number;
-}
-
-/*
- * Construct a syscall tables ordered by increasing syscall number
- * @returns number of syscall entries in the table
- */
-int construct_table(const struct bpf_call *entries, int n,
- struct syscall_entry *table)
-{
- long nr;
- unsigned int tn;
- int i;
-
- tn = 0;
- for (i = 0; i < n; i++) {
- table[i].count = 0;
- table[i].entry = NULL;
- }
-
- for (i = 0; i < n; i++) {
- if (tn > N_SYSCALL - 1)
- return -1;
- if (i > 0) {
- if (strcmp((entries[i]).name, (entries[i - 1]).name) ==
- 0) {
- table[tn - 1].count++;
- continue;
- }
- }
- nr = resolve_syscall_nr((entries[i]).name);
- if (nr < 0) {
- fprintf(stderr, "wrong syscall number for %s\n",
- (entries[i]).name);
- continue;
- }
- table[tn].entry = &entries[i];
- table[tn].count++;
- table[tn].nr = nr;
- tn++;
- }
- qsort(table, tn, sizeof(struct syscall_entry), compare_table_nr);
-
- return tn;
-}
-
-static unsigned get_n_args_syscall_entry(const struct bpf_call *entry)
+static unsigned get_n_args_syscall_entry(const struct notify *entry)
{
unsigned i, n = 0;
for (i = 0; i < 6; i++)
- if (entry->args[i].cmp != NO_CHECK)
+ if (entry->arg[i].cmp != NO_CHECK)
n++;
return n;
}
-static unsigned int get_n_args_syscall_instr(const struct syscall_entry *table)
+static unsigned int get_n_args_syscall_instr(const struct notify *table,
+ int len)
{
- const struct bpf_call *entry;
+ const struct notify *entry;
bool has_arg = false;
unsigned n = 0, total_instr = 0;
+ int i;
- for (unsigned int i = 0; i < table->count; i++) {
- entry = table->entry + i;
+ for (i = 0; i < len; i++) {
+ entry = table + i;
n = 0;
for (unsigned int k = 0; k < 6; k++) {
- if (entry->args[k].cmp == NO_CHECK)
+ if (entry->arg[k].cmp == NO_CHECK)
continue;
- switch (entry->args[k].type) {
- case U32:
+ switch (entry->arg[k].type) {
+ case BPF_U32:
/* For 32 bit arguments
* comparison instructions (2):
* 1 loading the value + 1 for evaluation
* arithemtic instructions (3):
* 1 loading the value + 1 for the operation + 1 for evaluation
*/
- if (entry->args[k].cmp == AND_EQ ||
- entry->args[k].cmp == AND_NE)
+ if (entry->arg[k].cmp == AND_EQ ||
+ entry->arg[k].cmp == AND_NE)
n += 3;
else
n += 2;
break;
- case U64:
+ case BPF_U64:
/* For 64 bit arguments: 32 instructions * 2
* for loading and evaluating the high and low 32 bits chuncks.
*/
- if (entry->args[k].cmp == AND_EQ ||
- entry->args[k].cmp == AND_NE)
+ if (entry->arg[k].cmp == AND_EQ ||
+ entry->arg[k].cmp == AND_NE)
n += 6;
else
n += 4;
@@ -222,44 +154,34 @@ static unsigned int get_n_args_syscall_instr(const struct syscall_entry *table)
return total_instr;
}
-static bool check_args_syscall_entry(const struct bpf_call *entry){
- return entry->args[0].cmp != NO_CHECK ||
- entry->args[1].cmp != NO_CHECK ||
- entry->args[2].cmp != NO_CHECK ||
- entry->args[3].cmp != NO_CHECK ||
- entry->args[4].cmp != NO_CHECK || entry->args[5].cmp != NO_CHECK;
-}
-
-static bool check_args_syscall(const struct syscall_entry *table)
-{
- for (unsigned int i = 0; i < table->count; i++) {
- if (check_args_syscall_entry(table->entry + i))
- return true;
- }
- return false;
+static bool check_args_syscall_entry(const struct notify *entry){
+ return entry->arg[0].cmp != NO_CHECK ||
+ entry->arg[1].cmp != NO_CHECK ||
+ entry->arg[2].cmp != NO_CHECK ||
+ entry->arg[3].cmp != NO_CHECK ||
+ entry->arg[4].cmp != NO_CHECK || entry->arg[5].cmp != NO_CHECK;
}
static unsigned int eq(struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
unsigned int size = 0;
uint32_t hi, lo;
- switch (entry->args[idx].type) {
- case U64:
- hi = get_hi((entry->args[idx]).value.v64);
- lo = get_lo((entry->args[idx]).value.v64);
+ switch (entry->arg[idx].type) {
+ case BPF_U64:
+ hi = get_hi((entry->arg[idx]).value.v64);
+ lo = get_lo((entry->arg[idx]).value.v64);
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)EQ(lo, 0, jfalse);
filter[size++] = (struct sock_filter)LOAD(HI_ARG(idx));
filter[size++] = (struct sock_filter)EQ(hi, jtrue, jfalse);
break;
- case U32:
-
+ case BPF_U32:
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)EQ(
- entry->args[idx].value.v32, jtrue, jfalse);
+ entry->arg[idx].value.v32, jtrue, jfalse);
break;
}
@@ -267,26 +189,25 @@ static unsigned int eq(struct sock_filter filter[], int idx,
}
static unsigned int gt(struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
unsigned int size = 0;
uint32_t hi, lo;
- switch (entry->args[idx].type) {
- case U64:
- hi = get_hi((entry->args[idx]).value.v64);
- lo = get_lo((entry->args[idx]).value.v64);
+ switch (entry->arg[idx].type) {
+ case BPF_U64:
+ hi = get_hi((entry->arg[idx]).value.v64);
+ lo = get_lo((entry->arg[idx]).value.v64);
filter[size++] = (struct sock_filter)LOAD(HI_ARG(idx));
filter[size++] = (struct sock_filter)GT(hi, jtrue + 2, 0);
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)GT(lo, jtrue, jfalse);
break;
- case U32:
-
+ case BPF_U32:
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)GT(
- entry->args[idx].value.v32, jtrue, jfalse);
+ entry->arg[idx].value.v32, jtrue, jfalse);
break;
}
@@ -294,26 +215,25 @@ static unsigned int gt(struct sock_filter filter[], int idx,
}
static unsigned int lt(struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
unsigned int size = 0;
uint32_t hi, lo;
- switch (entry->args[idx].type) {
- case U64:
- hi = get_hi((entry->args[idx]).value.v64);
- lo = get_lo((entry->args[idx]).value.v64);
+ switch (entry->arg[idx].type) {
+ case BPF_U64:
+ hi = get_hi((entry->arg[idx]).value.v64);
+ lo = get_lo((entry->arg[idx]).value.v64);
filter[size++] = (struct sock_filter)LOAD(HI_ARG(idx));
filter[size++] = (struct sock_filter)LT(hi, jtrue + 2, jfalse);
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)LT(lo, jtrue, jfalse);
break;
- case U32:
-
+ case BPF_U32:
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)LT(
- entry->args[idx].value.v32, jtrue, jfalse);
+ entry->arg[idx].value.v32, jtrue, jfalse);
break;
}
@@ -321,52 +241,51 @@ static unsigned int lt(struct sock_filter filter[], int idx,
}
static unsigned int neq(struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
return eq(filter, idx, entry, jfalse, jtrue);
}
static unsigned int ge(struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
return lt(filter, idx, entry, jfalse, jtrue);
}
static unsigned int le(struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
return gt(filter, idx, entry, jfalse, jtrue);
}
static unsigned int and_eq (struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
unsigned int size = 0;
- switch (entry->args[idx].type) {
- case U64:
+ switch (entry->arg[idx].type) {
+ case BPF_U64:
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)AND(
- get_lo(entry->args[idx].op2.v64));
+ get_lo(entry->arg[idx].op2.v64));
filter[size++] = (struct sock_filter)EQ(
- get_lo((entry->args[idx]).value.v64), 0, jfalse);
+ get_lo((entry->arg[idx]).value.v64), 0, jfalse);
filter[size++] = (struct sock_filter)LOAD(HI_ARG(idx));
filter[size++] = (struct sock_filter)AND(
- get_hi(entry->args[idx].op2.v64));
+ get_hi(entry->arg[idx].op2.v64));
filter[size++] = (struct sock_filter)EQ(
- get_hi(entry->args[idx].value.v64), jtrue, jfalse);
+ get_hi(entry->arg[idx].value.v64), jtrue, jfalse);
break;
- case U32:
-
+ case BPF_U32:
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] =
- (struct sock_filter)AND(entry->args[idx].op2.v32);
+ (struct sock_filter)AND(entry->arg[idx].op2.v32);
filter[size++] = (struct sock_filter)EQ(
- entry->args[idx].value.v32, jtrue, jfalse);
+ entry->arg[idx].value.v32, jtrue, jfalse);
break;
}
@@ -374,55 +293,52 @@ static unsigned int and_eq (struct sock_filter filter[], int idx,
}
static unsigned int and_ne(struct sock_filter filter[], int idx,
- const struct bpf_call *entry, unsigned int jtrue,
+ const struct notify *entry, unsigned int jtrue,
unsigned int jfalse)
{
unsigned int size = 0;
- switch (entry->args[idx].type) {
- case U64:
+ switch (entry->arg[idx].type) {
+ case BPF_U64:
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] = (struct sock_filter)AND(
- get_lo(entry->args[idx].op2.v64));
+ get_lo(entry->arg[idx].op2.v64));
filter[size++] = (struct sock_filter)EQ(
- get_lo((entry->args[idx]).value.v64), 0, jtrue + 3);
+ get_lo((entry->arg[idx]).value.v64), 0, jtrue + 3);
filter[size++] = (struct sock_filter)LOAD(HI_ARG(idx));
filter[size++] = (struct sock_filter)AND(
- get_hi(entry->args[idx].op2.v64));
+ get_hi(entry->arg[idx].op2.v64));
filter[size++] = (struct sock_filter)EQ(
- get_hi(entry->args[idx].value.v64), jfalse, jtrue);
+ get_hi(entry->arg[idx].value.v64), jfalse, jtrue);
break;
- case U32:
-
+ case BPF_U32:
filter[size++] = (struct sock_filter)LOAD(LO_ARG(idx));
filter[size++] =
- (struct sock_filter)AND(entry->args[idx].op2.v32);
+ (struct sock_filter)AND(entry->arg[idx].op2.v32);
filter[size++] = (struct sock_filter)EQ(
- entry->args[idx].value.v32, jfalse, jtrue);
+ entry->arg[idx].value.v32, jfalse, jtrue);
break;
}
return size;
}
-unsigned int create_bfp_program(struct syscall_entry table[],
- struct sock_filter filter[],
- unsigned int n_syscall)
+unsigned int filter_build(struct sock_filter filter[], unsigned int n)
{
unsigned int offset_left, offset_right;
unsigned int n_nodes, notify, accept;
unsigned int next_offset, offset;
- const struct bpf_call *entry;
+ const struct notify *entry;
unsigned int size = 0;
unsigned int next_args_off;
int nodes[MAX_JUMPS];
unsigned int i, j, k;
unsigned n_checks;
- create_lookup_nodes(nodes, n_syscall);
+ create_lookup_nodes(nodes, n);
/* No nodes if there is a single syscall */
- n_nodes = (1 << count_shift_right(n_syscall - 1)) - 1;
+ n_nodes = (1 << count_shift_right(n - 1)) - 1;
/* Pre */
/* cppcheck-suppress badBitmaskCheck */
@@ -438,7 +354,7 @@ unsigned int create_bfp_program(struct syscall_entry table[],
BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr)));
/* pre-check instruction + load syscall number (4 instructions) */
- accept = size + n_nodes + n_syscall;
+ accept = size + n_nodes + n;
notify = accept + 1;
/* Insert nodes */
@@ -450,21 +366,22 @@ unsigned int create_bfp_program(struct syscall_entry table[],
offset_left = left_child(i) - i - 1;
offset_right = right_child(i) - i - 1;
filter[size++] = (struct sock_filter)JGE(
- table[nodes[i]].nr, offset_right, offset_left);
+ notify_call[i].nr, offset_right, offset_left);
}
}
- next_offset = n_syscall + 1;
+ next_offset = n + 1;
/* Insert leaves */
- for (i = 0; i < n_syscall; i++) {
+ for (i = 0; i < n; i++) {
/* If the syscall doesn't have any arguments, then notify */
- if (check_args_syscall(&table[i]))
+ if (check_args_syscall_entry(notify_call + i))
offset = next_offset;
else
offset = notify - size - 1;
- filter[size++] = (struct sock_filter)EQ(table[i].nr, offset,
+ filter[size++] = (struct sock_filter)EQ(notify_call[i].nr,
+ offset,
accept - size);
- next_offset += get_n_args_syscall_instr(&table[i]) - 1;
+ next_offset += get_n_args_syscall_instr(notify_call + i, n) - 1;
}
/* Seccomp accept and notify instruction */
filter[size++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K,
@@ -477,15 +394,20 @@ unsigned int create_bfp_program(struct syscall_entry table[],
* entry. If a check on the argument isn't equal then it jumps to
* check the following entry of the syscall and its arguments.
*/
- for (i = 0; i < n_syscall; i++) {
+ for (i = 0; i < n; i++) {
bool has_arg = false;
- for (j = 0; j < (table[i]).count; j++) {
+ unsigned int count = 0, x;
+
+ for (x = 0; x < 6; x++)
+ count += notify_call[i].arg[x].cmp == NO_CHECK;
+
+ for (j = 0; j < count; j++) {
n_checks = 0;
- entry = table[i].entry + j;
+ entry = notify_call + i + j;
next_args_off = get_n_args_syscall_entry(entry);
for (k = 0; k < 6; k++) {
offset = next_args_off - n_checks;
- switch (entry->args[k].cmp) {
+ switch (entry->arg[k].cmp) {
case NO_CHECK:
continue;
case EQ:
@@ -525,7 +447,7 @@ unsigned int create_bfp_program(struct syscall_entry table[],
n_checks++;
has_arg = true;
}
- if (check_args_syscall_entry(table[i].entry))
+ if (check_args_syscall_entry(notify_call + i))
filter[size++] = (struct sock_filter)BPF_STMT(
BPF_RET | BPF_K,
SECCOMP_RET_USER_NOTIF);
@@ -541,31 +463,121 @@ unsigned int create_bfp_program(struct syscall_entry table[],
return size;
}
-static int compare_names(const void *a, const void *b)
-{
- return strcmp(((struct syscall_numbers *)a)->name,
- ((struct syscall_numbers *)b)->name);
+/**
+ * struct filter_call_input - First input stage for cooker notification requests
+ * @notify: Notify on this system call
+ * @no_args: No argument comparisons are allowed for this call
+ * @args_set: Argument matches were already set up once for this call
+ * @arg: Argument specification
+ */
+struct filter_call_input {
+ bool notify;
+ bool no_args;
+ bool args_set;
+ struct bpf_arg arg[6];
+} filter_input[512] = { 0 };
+
+static struct {
+ bool used;
+ struct bpf_arg arg[6];
+} filter_current_args;
+
+static long current_nr;
+
+/**
+ * filter_notify() - Start of notification request, check/flush previous one
+ * @nr: System call number, -1 to just flush previous request
+ */
+void filter_notify(long nr) {
+ struct filter_call_input *call = filter_input + nr;
+ long prev_nr = current_nr;
+
+ if (nr >= 0) {
+ current_nr = nr;
+ call->notify = true;
+ }
+
+ if (filter_current_args.used) {
+ struct filter_call_input *prev_call = filter_input + prev_nr;
+
+ /* First time arguments for previous call are flushed? */
+ if (!prev_call->args_set && !prev_call->no_args) {
+ prev_call->args_set = true;
+ memcpy(prev_call->arg, filter_current_args.arg,
+ sizeof(filter_current_args.arg));
+ return;
+ }
+
+ prev_call->args_set = true;
+
+ /* ...not the first time: check exact overlap of matches */
+ if (memcmp(prev_call->arg, filter_current_args.arg,
+ sizeof(filter_current_args.arg)))
+ prev_call->no_args = true;
+
+ /* Flush temporary set of arguments */
+ memset(&filter_current_args, 0, sizeof(filter_current_args));
+ }
+}
+
+/**
+ * filter_needs_deref() - Mark system call as ineligible for argument evaluation
+ */
+void filter_needs_deref(void) {
+ struct filter_call_input *call = filter_input + current_nr;
+
+ call->no_args = true;
}
-int convert_bpf(char *file, struct bpf_call *entries, int n)
+/**
+ * Use temporary filter_call_cur_args storage. When there's a new notification,
+ * or the parser is done, we flush these argument matches to filter_input, and
+ * check if they match (including no-matches) all the previous argument
+ * specification. If they don't, the arguments can't be evaluated in the filter.
+ */
+void filter_add_arg(int index, struct bpf_arg arg) {
+ struct filter_call_input *call = filter_input + current_nr;
+
+ if (call->no_args)
+ return;
+
+ memcpy(filter_current_args.arg + index, &arg, sizeof(arg));
+ filter_current_args.used = true;
+}
+
+unsigned int filter_close_input(void)
{
- int nt, fd, fsize;
- struct syscall_entry table[N_SYSCALL];
- struct sock_filter filter[MAX_FILTER];
+ struct notify *call = notify_call;
+ int i, count = 0;
- qsort(numbers, sizeof(numbers) / sizeof(numbers[0]), sizeof(numbers[0]),
- compare_names);
+ filter_notify(-1);
- qsort(entries, n, sizeof(struct bpf_call), compare_bpf_call_names);
- nt = construct_table(entries, n, table);
+ for (i = 0; i < 512; i++) {
+ if (filter_input[i].notify) {
+ count++;
+ call->nr = i;
- fsize = create_bfp_program(table, filter, nt);
+ if (filter_input[i].no_args)
+ continue;
- fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
- S_IRUSR | S_IWUSR);
- write(fd, filter, sizeof(struct sock_filter) * fsize);
+ memcpy(call->arg, filter_input[i].arg,
+ sizeof(call->arg));
+ }
+ }
- close(fd);
+ return count;
+}
- return 0;
+void filter_write(const char *path)
+{
+ struct sock_filter filter[MAX_FILTER];
+ int fd, n;
+
+ n = filter_close_input();
+ n = filter_build(filter, n);
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR);
+ write(fd, filter, sizeof(struct sock_filter) * n);
+ close(fd);
}
diff --git a/cooker/filter.h b/cooker/filter.h
index 205fa06..7059a7c 100644
--- a/cooker/filter.h
+++ b/cooker/filter.h
@@ -44,40 +44,28 @@
#define MAX_JUMPS 128
#define EMPTY -1
-enum arg_type { U32, U64 };
+enum bpf_type { BPF_U32, BPF_U64 };
-union arg_value {
+union bpf_value {
uint32_t v32;
uint64_t v64;
};
-enum arg_cmp { NO_CHECK, EQ, NE, LE, LT, GE, GT, AND_EQ, AND_NE };
+enum bpf_cmp { NO_CHECK = 0, EQ, NE, LE, LT, GE, GT, AND_EQ, AND_NE };
-struct arg {
- union arg_value value;
- enum arg_type type;
- enum arg_cmp cmp;
- union arg_value op2;
+struct bpf_arg {
+ union bpf_value value;
+ enum bpf_type type;
+ enum bpf_cmp cmp;
+ union bpf_value op2;
};
-struct bpf_call {
- char *name;
- struct arg args[6];
-};
-
-struct syscall_entry {
- unsigned int count;
- long nr;
- const struct bpf_call *entry;
-};
+void filter_notify(long nr);
+void filter_needs_deref(void);
+void filter_add_arg(int index, struct bpf_arg arg);
+void filter_write(const char *path);
void create_lookup_nodes(int jumps[], unsigned int n);
unsigned int left_child(unsigned int parent_index);
unsigned int right_child(unsigned int parent_index);
-
-unsigned int create_bfp_program(struct syscall_entry table[],
- struct sock_filter filter[],
- unsigned int n_syscall);
-int convert_bpf(char *file, struct bpf_call *entries, int n);
-
#endif
diff --git a/cooker/gluten.c b/cooker/gluten.c
index 6460798..05d408f 100644
--- a/cooker/gluten.c
+++ b/cooker/gluten.c
@@ -32,6 +32,15 @@ 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
+ * @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 ret = g->dp;
@@ -43,16 +52,59 @@ struct gluten_offset gluten_alloc(struct gluten_ctx *g, size_t size)
return ret;
}
+/**
+ * gluten_alloc() - Allocate storage for given type in temporary data 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)
{
return gluten_alloc(g, gluten_size[type]);
}
-void gluten_init(struct gluten_ctx *g)
+void gluten_add_tag(struct gluten_ctx *g, const char *name,
+ struct gluten_offset offset)
{
- (void)g;
+ int i;
+
+ for (i = 0; i < TAGS_MAX && g->tags[i].name; i++);
+ if (i == TAGS_MAX)
+ die("Too many tags");
+
+ g->tags[i].name = name;
+ g->tags[i].offset = offset;
+
+ debug(" tag '%s' now refers to %s at %i",
+ name, gluten_offset_name[offset.type], offset.offset);
+}
- g->ip.type = g->lr.type = OFFSET_INSTRUCTION;
+/**
+ * gluten_init() - Initialise gluten structures and layout
+ * @g: gluten context
+ */
+void gluten_init(struct gluten_ctx *g)
+{
+ g->ip.type = g->lr.type = g->mr.type = OFFSET_INSTRUCTION;
+ g->ip.offset = g->lr.offset = g->mr.offset = 0;
g->dp.type = OFFSET_DATA;
g->cp.type = OFFSET_RO_DATA;
}
+
+void gluten_write(struct gluten_ctx *g, const char *path)
+{
+ ssize_t n;
+ int fd;
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR);
+
+ if ((n = write(fd, &g->g, sizeof(g->g))) == -1)
+ die("Failed to write gluten: %s", strerror(errno));
+
+ if (n != sizeof(g->g))
+ die("Failed to write %i bytes of gluten", sizeof(g->g) - n);
+
+ close(fd);
+}
diff --git a/cooker/gluten.h b/cooker/gluten.h
index a48cd6d..edd3240 100644
--- a/cooker/gluten.h
+++ b/cooker/gluten.h
@@ -14,8 +14,8 @@ struct gluten_arg_data {
size_t len;
};
-struct gluten_ref_data {
- char name[REF_NAMEMAX];
+struct gluten_tag_data {
+ const char *name;
struct gluten_offset offset;
size_t len;
};
@@ -23,6 +23,7 @@ struct gluten_ref_data {
struct gluten_ctx {
struct gluten_offset ip;
struct gluten_offset lr;
+ struct gluten_offset mr;
struct gluten_offset cp;
struct gluten_offset dp;
@@ -31,14 +32,30 @@ struct gluten_ctx {
struct gluten_arg_data match_dst[CALL_ARGS];
struct gluten_arg_data call_src[CALL_ARGS];
- struct gluten_ref_data refs[REFS_MAX];
+ struct gluten_tag_data tags[TAGS_MAX];
+
+ struct arg *selected_arg[6];
+};
+
+/**
+ * enum jump_type - Indicate direction of jump before linking phase
+ */
+enum jump_type {
+ JUMP_NEXT_BLOCK,
+ JUMP_NEXT_MATCH,
+ JUMP_END,
+ 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);
+void gluten_add_tag(struct gluten_ctx *g, const char *name,
+ struct gluten_offset offset);
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);
extern size_t gluten_size[TYPE_COUNT];
+extern const char *jump_name[JUMP_COUNT];
#endif /* GLUTEN_H */
diff --git a/cooker/main.c b/cooker/main.c
index 9965cff..5512d54 100644
--- a/cooker/main.c
+++ b/cooker/main.c
@@ -11,18 +11,29 @@
#include "cooker.h"
#include "gluten.h"
#include "parse.h"
+#include "filter.h"
+/**
+ * main() - Entry point for cooker
+ * @argc: Argument count
+ * @argv: Options: input filename, output filename
+ *
+ * Return: zero on success, doesn't return on failure
+ */
int main(int argc, char **argv)
{
struct gluten_ctx g = { 0 };
/* TODO: Options and usage */
- (void)argc;
- (void)argv;
+ if (argc != 4)
+ die("%s INPUT GLUTEN BPF", argv[0]);
gluten_init(&g);
parse_file(&g, argv[1]);
+ gluten_write(&g, argv[2]);
+ filter_write(argv[3]);
+
return 0;
}
diff --git a/cooker/match.c b/cooker/match.c
new file mode 100644
index 0000000..bd96a03
--- /dev/null
+++ b/cooker/match.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
+ *
+ * cooker/match.c - Parse "match" 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 "filter.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
+ *
+ * Return: offset where (full) argument is stored
+ */
+static struct gluten_offset arg_load(struct gluten_ctx *g, struct arg *a)
+{
+ int index = a->pos;
+ size_t size;
+
+ if (a->type == SELECTED) {
+ if (g->selected_arg[index]->type != UNDEF)
+ size = g->selected_arg[index]->size;
+ else
+ die(" no storage size for argument %s", a->name);
+ } else {
+ size = a->size;
+ }
+
+ if (!size) {
+ g->match_dst[index].offset.type = OFFSET_SECCOMP_DATA;
+ g->match_dst[index].offset.offset = index;
+ g->match_dst[index].len = 0;
+ return g->match_dst[index].offset;
+ }
+
+ filter_needs_deref();
+
+ 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);
+
+ 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;
+ }
+ }
+
+ if (!d_num->target.type)
+ die(" No match for numeric selector %i", v.v_num);
+ }
+
+ die(" not supported yet");
+}
+
+/**
+ * parse_value() - Parse JSON value for generic item of data description
+ * @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
+ */
+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)
+{
+ struct gluten_offset data_offset, const_offset;
+ const char *tag_name;
+ JSON_Object *tmp;
+ struct field *f;
+ union value v;
+
+ if (type == SELECT) {
+ struct select *select = desc.d_select;
+ struct field *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);
+ } else {
+ sel = value;
+ }
+
+ value_get(field->desc, field->type, sel, &v);
+ const_offset = emit_data(g, field->type, field->strlen, &v);
+
+ data_offset = offset;
+ data_offset.offset += field->offset;
+
+ emit_cmp_field(g, CMP_NE, field, data_offset, const_offset,
+ JUMP_NEXT_BLOCK);
+
+ select_desc(g, select, v, index, &type, &desc);
+
+ if (type == NONE)
+ return;
+ }
+
+ if (json_value_get_type(value) == JSONObject &&
+ (tmp = json_value_get_object(value)) &&
+ (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");
+ }
+
+ /* 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;
+ 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],
+ JUMP_NEXT_BLOCK);
+ break;
+ case SELECT:
+ /* TODO: check how nested selects should work */
+ parse_value(g, index, offset, type, 0, desc, value);
+ break;
+ case STRING:
+ v.v_str = json_value_get_string(value);
+ if (strlen(v.v_str) + 1 > str_len)
+ die(" string %s too long for field", v.v_str);
+
+ const_offset = emit_data(g, STRING, strlen(v.v_str) + 1, &v);
+ emit_cmp(g, CMP_NE, offset, const_offset, strlen(v.v_str) + 1,
+ JUMP_NEXT_BLOCK);
+ break;
+ case STRUCT:
+ for (f = desc.d_struct; f->name; f++) {
+ JSON_Value *field_value;
+
+ tmp = json_value_get_object(value);
+ field_value = json_object_get_value(tmp, f->name);
+ if (!field_value)
+ continue;
+
+ parse_value(g, index, offset, f->type, f->strlen,
+ f->desc, field_value);
+ }
+ default:
+ ;
+ }
+}
+
+/**
+ * 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
+ */
+static void parse_arg(struct gluten_ctx *g, const char *name, JSON_Value *value,
+ struct arg *a)
+{
+ struct gluten_offset offset;
+
+ debug(" Parsing match argument %s", name);
+
+ offset = arg_load(g, a);
+
+ parse_value(g, a->pos, offset, a->type, a->size, a->desc, value);
+}
+
+/**
+ * parse_match() - Parse one syscall rule in "match" array
+ * @g: gluten context
+ * @obj: Matching rule for one syscall
+ * @args: Description of arguments from syscall model
+ */
+static void parse_match(struct gluten_ctx *g, JSON_Object *obj,
+ struct arg *args)
+{
+ unsigned count = 0;
+ struct arg *a;
+
+ for (a = args; a->name; a++) {
+ struct arg *real_arg = a;
+ JSON_Value *value;
+
+ if (a->type == SELECTED) {
+ if (!(real_arg = g->selected_arg[a->pos]))
+ die(" No selection for argument %s", a->name);
+ }
+
+ if ((value = json_object_get_value(obj, real_arg->name))) {
+ count++;
+ parse_arg(g, real_arg->name, value, real_arg);
+ }
+ }
+
+ if (count != json_object_get_count(obj))
+ die(" Stray elements in match");
+}
+
+/**
+ * handle_matches() - Parse "match" array, find syscall models
+ * @g: gluten context
+ * @value: "match" object containing array of rules
+ */
+void handle_matches(struct gluten_ctx *g, JSON_Value *value)
+{
+ JSON_Array *matches = json_value_get_array(value);
+ unsigned i;
+
+ for (i = 0; i < json_array_get_count(matches); i++) {
+ JSON_Object *match, *args;
+ struct call **set, *call;
+ const char *name;
+
+ g->mr = g->ip;
+
+ match = json_array_get_object(matches, i);
+ name = json_object_get_name(match, 0);
+ args = json_object_get_object(match, name);
+ debug(" Parsing match %i: %s", i, name);
+
+ for (set = call_sets, call = set[0]; *set; ) {
+ if (!call->name) {
+ set++;
+ call = set[0];
+ continue;
+ }
+
+ if (!strcmp(name, call->name)) {
+ union value v = { .v_num = call->number };
+
+ debug(" Found description for %s", name);
+ emit_nr(g, emit_data(g, U64, 0, &v));
+
+ filter_notify(call->number);
+
+ parse_match(g, args, call->args);
+ break;
+ }
+ call++;
+ }
+
+ if (!*set)
+ die(" Unknown system call: %s", name);
+
+ link_match(g);
+ }
+}
diff --git a/cooker/match.h b/cooker/match.h
new file mode 100644
index 0000000..95c7b4d
--- /dev/null
+++ b/cooker/match.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 MATCH_H
+#define MATCH_H
+
+void handle_matches(struct gluten_ctx *g, JSON_Value *value);
+
+#endif /* MATCH_H */
diff --git a/cooker/parse.c b/cooker/parse.c
index 0a87088..09b1e46 100644
--- a/cooker/parse.c
+++ b/cooker/parse.c
@@ -12,260 +12,80 @@
#include "calls.h"
#include "cooker.h"
#include "gluten.h"
+#include "match.h"
#include "emit.h"
#include "util.h"
#include "calls/net.h"
-struct rule_parser {
- const char *type;
- int (*fn)(struct gluten_ctx *g, JSON_Value *value);
-};
-
-static int parse_match_load(struct gluten_ctx *g, struct arg *a)
-{
- if (!a->size || g->match_dst[a->pos].len)
- return 0;
-
- g->match_dst[a->pos].offset = gluten_alloc(g, a->size);
- g->match_dst[a->pos].len = a->size;
-
- emit_load(g, g->match_dst[a->pos].offset, a->pos, a->size);
-
- return 0;
-}
-
-static long long parse_match_expr_num(struct num *desc, JSON_Value *value)
+static void handle_call(struct gluten_ctx *g, 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;
-}
-
-static void parse_match_expr_value(union desc desc, enum type type,
- JSON_Value *value, union value *out)
-{
- if (TYPE_IS_NUM(type))
- out->v_num = parse_match_expr_num(desc.d_num, value);
-}
-
-/**
- * parse_match_select() - Get description and type for selected value
- * @s: Possible selection choices
- * @v: Selector value
- * @type: Type of selected value, set on return
- * @desc: Description of selected value, set on return
- */
-static void parse_match_select(struct select *s, union value v,
- 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->type; d_num++) {
- if (d_num->value == v.v_num) {
- *type = d_num->type;
- *desc = d_num->desc;
- return;
- }
- }
-
- if (!d_num->type)
- die(" No match for numeric selector %i", v.v_num);
- }
-
- die(" not supported yet");
-}
-
-/*
- * parse_match_arg()
- * load argument
- * parse_match_key()
- * compound types? demux, parse_match_expr
- * parse_match_expr
- * in/all/not/false-true array: demux, parse_match_expr_{num,string}
- * parse_match_expr_{num,string}
- *
- * at terminal values
- * store ref *pointers*! no copies
- * emit additional bpf statement if syscall arg is also terminal
-*/
-static int parse_match_key(struct gluten_ctx *g, int index, enum type type,
- union desc desc, JSON_Value *value)
-{
- JSON_Object *tmp;
- const char *ref;
-
- if (type == SELECT) {
- struct gluten_offset base_offset, const_offset;
- struct select *select = desc.d_select;
- struct field *field = select->field;
- JSON_Value *selector;
- union value v;
-
- if (!(tmp = json_value_get_object(value)))
- die(" no object for compound value");
-
- if (!(selector = json_object_get_value(tmp, field->name)))
- die(" missing selector for '%s'", field->name);
-
- parse_match_expr_value(field->desc, field->type, selector, &v);
-
- base_offset = g->match_dst[index].offset;
- const_offset = emit_data(g, field->type, &v);
- emit_cmp_field(g, CMP_NE, field, base_offset, const_offset,
- NEXT_BLOCK);
-
- parse_match_select(select, v, &type, &desc);
- }
-
- if (json_value_get_type(value) == JSONObject &&
- (tmp = json_value_get_object(value)) &&
- (ref = json_object_get_string(tmp, "ref"))) {
- if (TYPE_IS_COMPOUND(type))
- die("Reference '%s' to compound value");
-
- debug(" setting reference '%s'", ref);
- value = json_object_get_value(tmp, "value");
-
- emit_load(g, gluten_alloc_type(g, type), index, type);
- }
-
- /* Nothing to match on: just store as reference */
- if (!value)
- return 0;
-
- switch (type) {
- case INTFLAGS:
- case LONGFLAGS:
- case U32FLAGS:
- /* fetch/combine expr algebra loop */
- case INTMASK:
- /* calculate mask first */
- case INT:
- case LONG:
- case U32:
- parse_match_expr_num(desc.d_num, value);
- //emit_cmp(...);
- default:
- ;
- }
-
- return 0;
+ (void)g;
+ (void)value;
}
-static int parse_match_arg(struct gluten_ctx *g, const char *name,
- JSON_Value *value, struct arg *a)
+static void handle_inject(struct gluten_ctx *g, JSON_Value *value)
{
- debug(" Parsing match argument %s", name);
-
- parse_match_load(g, a);
- parse_match_key(g, a->pos, a->type, a->desc, value);
-
- return 0;
+ (void)g;
+ (void)value;
}
-static int parse_match(struct gluten_ctx *g, JSON_Object *obj, struct arg *args)
+static void handle_limit(struct gluten_ctx *g, JSON_Value *value)
{
- unsigned count = 0;
- struct arg *a;
-
- for (a = args; a->name; a++) {
- JSON_Value *value;
-
- if ((value = json_object_get_value(obj, a->name))) {
- count++;
- parse_match_arg(g, a->name, value, a);
- }
- }
-
- if (count != json_object_get_count(obj))
- die(" Stray elements in match");
-
- return 0;
+ (void)g;
+ (void)value;
}
-static int parse_matches(struct gluten_ctx *g, JSON_Value *value)
+static void handle_return(struct gluten_ctx *g, JSON_Value *value)
{
- JSON_Array *matches = json_value_get_array(value);
- unsigned i;
-
- for (i = 0; i < json_array_get_count(matches); i++) {
- JSON_Object *match, *args;
- struct call **set, *call;
- const char *name;
-
- g->lr = g->ip;
-
- match = json_array_get_object(matches, i);
- name = json_object_get_name(match, 0);
- args = json_object_get_object(match, name);
- debug(" Parsing match %i: %s", i, name);
-
- for (set = call_sets, call = set[0]; *set; call++) {
- if (!call->name) {
- set++;
- continue;
- }
-
- if (!strcmp(name, call->name)) {
- debug(" Found handler for %s", name);
- emit_nr(g, call->number);
-
- parse_match(g, args, call->args);
- break;
- }
- }
-
- if (!*set)
- die(" Unknown system call: %s", name);
- }
-
- return 0;
+ (void)g;
+ (void)value;
}
-static int parse_call(struct gluten_ctx *g, JSON_Value *value)
+static void handle_block(struct gluten_ctx *g, JSON_Value *value)
{
(void)g;
(void)value;
- return 0;
}
-static int parse_inject(struct gluten_ctx *g, JSON_Value *value)
+static void handle_context(struct gluten_ctx *g, JSON_Value *value)
{
(void)g;
(void)value;
- return 0;
}
-struct rule_parser parsers[] = {
- { "match", parse_matches },
- { "call", parse_call },
- { "inject", parse_inject },
+/**
+ * struct rule_parser - Parsing handler for JSON rule type
+ * @type: JSON key name
+ * @fn: Parsing function
+ */
+struct rule_parser {
+ const char *type;
+ void (*fn)(struct gluten_ctx *g, JSON_Value *value);
+} parsers[] = {
+ { "match", handle_matches },
+ { "call", handle_call },
+ { "inject", handle_inject },
+ { "limit", handle_limit },
+ { "return", handle_return },
+ { "block", handle_block },
+ { "context", handle_context },
{ NULL, NULL },
};
-static int parse_block(struct gluten_ctx *g, JSON_Object *block)
+/**
+ * parse_block() - Parse a transformation block with rules
+ * @g: gluten context
+ * @block: Array of rules in block
+ */
+static void parse_block(struct gluten_ctx *g, JSON_Object *block)
{
unsigned i;
+ memset(g->selected_arg, 0, sizeof(g->selected_arg));
+ memset(g->tags, 0, sizeof(g->tags));
+ g->lr = g->ip;
+
for (i = 0; i < json_object_get_count(block); i++) {
struct rule_parser *parser;
JSON_Value *rule;
@@ -285,10 +105,15 @@ static int parse_block(struct gluten_ctx *g, JSON_Object *block)
die(" Invalid rule type: \"%s\"", type);
}
- return 0;
+ link_block(g);
}
-int parse_file(struct gluten_ctx *g, const char *path)
+/**
+ * parse_file() - Entry point for parsing of a JSON input file
+ * @g: gluten context
+ * @path: Input file path
+ */
+void parse_file(struct gluten_ctx *g, const char *path)
{
JSON_Array *blocks;
JSON_Value *root;
@@ -306,6 +131,4 @@ int parse_file(struct gluten_ctx *g, const char *path)
debug("Parsing block %i", i);
parse_block(g, obj);
}
-
- return 0;
}
diff --git a/cooker/parse.h b/cooker/parse.h
index cb4a2f2..05079cc 100644
--- a/cooker/parse.h
+++ b/cooker/parse.h
@@ -6,6 +6,6 @@
#ifndef PARSE_H
#define PARSE_H
-int parse_file(struct gluten_ctx *g, const char *path);
+void parse_file(struct gluten_ctx *g, const char *path);
#endif /* PARSE_H */
diff --git a/operations.c b/operations.c
index d404b87..f4adb0a 100644
--- a/operations.c
+++ b/operations.c
@@ -357,7 +357,7 @@ int op_cmp(const struct seccomp_notif *req, int notifier, struct gluten *g,
(res < 0 && (cmp == CMP_LT || cmp == CMP_LE)) ||
(res > 0 && (cmp == CMP_GT || cmp == CMP_GE)) ||
(res != 0 && (cmp == CMP_NE)))
- return op->jmp;
+ return op->jmp.offset; /* TODO: check boundaries */
return 0;
}
@@ -385,10 +385,10 @@ int op_resolve_fd(const struct seccomp_notif *req, int notifier,
return 0;
}
-int eval(struct gluten *g, struct op *ops, const struct seccomp_notif *req,
+int eval(struct gluten *g, const struct seccomp_notif *req,
int notifier)
{
- struct op *op = ops;
+ struct op *op = (struct op *)g->inst;
while (op->type != OP_END) {
switch (op->type) {
diff --git a/operations.h b/operations.h
index f68ea89..6904b4f 100644
--- a/operations.h
+++ b/operations.h
@@ -40,8 +40,7 @@ struct arg_clone {
};
int do_call(struct arg_clone *c);
-int eval(struct gluten *g, struct op *ops, const struct seccomp_notif *req,
- int notifier);
+int eval(struct gluten *g, const struct seccomp_notif *req, int notifier);
int op_call(const struct seccomp_notif *req, int notifier, struct gluten *g,
struct op_call *op);
int op_block(const struct seccomp_notif *req, int notifier, struct gluten *g,
diff --git a/seitan.c b/seitan.c
index 4a4cc86..fff820c 100644
--- a/seitan.c
+++ b/seitan.c
@@ -184,10 +184,10 @@ int main(int argc, char **argv)
struct epoll_event ev, events[EPOLL_EVENTS];
struct seccomp_notif *req = (struct seccomp_notif *)req_b;
struct arguments arguments;
- struct op operations[INST_MAX];
char path[PATH_MAX + 1];
bool running = true;
int pidfd, notifier;
+ struct gluten g;
int fd, epollfd;
int notifierfd;
int nevents, i;
@@ -195,7 +195,8 @@ int main(int argc, char **argv)
arguments.pid = -1;
parse(argc, argv, &arguments);
fd = open(arguments.input_file, O_CLOEXEC | O_RDONLY);
- /* TODO: Load bytecode */
+ if (read(fd, &g, sizeof(g)) != sizeof(g))
+ die("Failed to read gluten file");
close(fd);
if (arguments.pid > 0) {
@@ -237,7 +238,7 @@ int main(int argc, char **argv)
/* The notifier fd was closed by the target */
running = false;
} else if (notifier == events[i].data.fd) {
- eval(NULL, &operations[0], req, notifier);
+ eval(&g, req, notifier);
}
}
}