aboutgitcodelistschat:MatrixIRC
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--common/gluten.h42
-rw-r--r--common/util.c10
-rw-r--r--cooker/call.c20
-rw-r--r--cooker/calls/fs.c190
-rw-r--r--cooker/calls/net.c92
-rw-r--r--cooker/cooker.h5
-rw-r--r--cooker/emit.c101
-rw-r--r--cooker/emit.h15
-rw-r--r--cooker/filter.c12
-rw-r--r--cooker/gluten.c2
-rw-r--r--cooker/match.c172
-rw-r--r--demo/Makefile9
-rw-r--r--demo/fremovexattr.c11
-rw-r--r--demo/fsetxattr.c11
-rw-r--r--demo/iptables.hjson214
-rw-r--r--demo/nft.hjson221
-rw-r--r--demo/open_by_handle_at.c22
-rw-r--r--demo/virtiofsd.hjson93
-rw-r--r--operations.c232
-rw-r--r--operations.h2
21 files changed, 1360 insertions, 123 deletions
diff --git a/Makefile b/Makefile
index 8f537a4..8b93e06 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ HEADERS := $(COMMON_DIR)/common.h $(COMMON_DIR)/gluten.h \
CFLAGS += -DTMP_DATA_SIZE=1000
CFLAGS += -Wall -Wextra -pedantic -std=c99 -I$(COMMON_DIR) -g
-all: cooker eater seitan
+all: cooker eater seitan demo
.PHONY: cooker
cooker:
@@ -37,12 +37,17 @@ seitan: $(SRCS) $(HEADERS)
debug:
$(MAKE) -C debug
+.PHONY: demo
+demo:
+ $(MAKE) -C demo
+
.PHONY: clean
clean:
rm -f $(BIN)
$(MAKE) -C cooker clean
$(MAKE) -C eater clean
$(MAKE) -C debug clean
+ $(MAKE) -C demo clean
numbers.h:
./scripts/nr_syscalls.sh
diff --git a/common/gluten.h b/common/gluten.h
index 1414d9e..4e1c249 100644
--- a/common/gluten.h
+++ b/common/gluten.h
@@ -21,10 +21,10 @@
extern struct seccomp_data anonymous_seccomp_data;
-#define HEADER_SIZE 65536
-#define INST_SIZE 65536
-#define RO_DATA_SIZE 65536
-#define DATA_SIZE 65536
+#define HEADER_SIZE (1 << 20)
+#define INST_SIZE (1 << 20)
+#define RO_DATA_SIZE (1 << 20)
+#define DATA_SIZE (1 << 20)
#define INST_MAX 256
#define OFFSET_MAX \
@@ -67,13 +67,14 @@ enum op_type {
OP_CALL,
OP_COPY,
OP_FD,
+ OP_FDGET,
OP_RETURN,
OP_LOAD,
OP_STORE,
OP_IOVLOAD,
OP_BITWISE,
OP_CMP,
- OP_RESOLVEDFD,
+ OP_RESOLVEFD,
};
/**
@@ -199,6 +200,11 @@ struct op_fd {
struct gluten_offset desc; /* struct fd_desc */
};
+struct op_fdget {
+ struct gluten_offset src;
+ struct gluten_offset dst;
+};
+
struct op_load {
struct gluten_offset src;
struct gluten_offset dst;
@@ -216,6 +222,12 @@ struct op_iovload {
struct gluten_offset iovlen;
struct gluten_offset dst;
size_t size;
+ size_t zero_fill;
+};
+
+struct vec_desc {
+ struct gluten_offset start;
+ off_t len_offset;
};
enum op_cmp_type {
@@ -232,6 +244,7 @@ extern const char *cmp_type_str[CMP_MAX + 1];
struct cmp_desc {
enum op_cmp_type cmp;
size_t size;
+ struct vec_desc vec;
struct gluten_offset x;
struct gluten_offset y;
struct gluten_offset jmp;
@@ -252,6 +265,7 @@ extern const char *bitwise_type_str[BITWISE_MAX + 1];
struct bitwise_desc {
size_t size;
enum bitwise_type type;
+ struct vec_desc vec;
struct gluten_offset dst;
struct gluten_offset x;
struct gluten_offset y;
@@ -261,14 +275,23 @@ struct op_bitwise {
struct gluten_offset desc; /* struct bitwise_desc */
};
+enum resolvefd_type {
+ RESOLVEFD_PATH,
+ RESOLVEFD_MOUNT,
+ RESOLVEFD_MAX = RESOLVEFD_MOUNT,
+};
+
+extern const char *resolvefd_type_str[RESOLVEFD_MAX + 1];
+
struct resolvefd_desc {
- struct gluten_offset fd;
- struct gluten_offset path;
- size_t path_max;
+ struct gluten_offset fd;
+ struct gluten_offset path;
+ enum resolvefd_type type;
+ size_t path_max;
};
struct op_resolvefd {
- struct gluten_offset desc;
+ struct gluten_offset desc; /* struct resolvefd_desc */
};
struct op_copy {
@@ -284,6 +307,7 @@ struct op {
struct op_call call;
struct op_return ret;
struct op_fd fd;
+ struct op_fdget fdget;
struct op_load load;
struct op_store store;
struct op_iovload iovload;
diff --git a/common/util.c b/common/util.c
index 94aeea3..f0a1df4 100644
--- a/common/util.c
+++ b/common/util.c
@@ -54,11 +54,13 @@ const char *context_spec_type_name[CONTEXT_SPEC_TYPE_MAX + 1] = {
const char *bitwise_type_str[BITWISE_MAX + 1] = { "&", "|" };
const char *cmp_type_str[CMP_MAX + 1] = {
- "EQ", "NE", "GT", "GE", "LT", "LE",
+ "EQ", "NE", "GT", "GE", "LT", "LE",
};
const char *metadata_type_str[METADATA_MAX + 1] = { "uid", "gid", "pid" };
+const char *resolvefd_type_str[RESOLVEFD_MAX + 1] = { "path", "mount" };
+
const char *syscall_name(long nr) {
struct call **set, *call;
@@ -66,14 +68,16 @@ const char *syscall_name(long nr) {
if (!call->name) {
set++;
call = set[0];
- continue;
+
+ if (!*set)
+ return "unknown";
}
if (nr == call->number)
break;
}
- return call ? call->name : "unknown";
+ return call->name;
}
const char *syscall_name_str[N_SYSCALL + 1] = {
diff --git a/cooker/call.c b/cooker/call.c
index dd37fe9..19a7b7e 100644
--- a/cooker/call.c
+++ b/cooker/call.c
@@ -72,11 +72,13 @@ static union value parse_metadata(struct gluten_ctx *g, struct field *f,
if (tag_offset.type == OFFSET_NULL)
die(" tag not found");
- if ((*base_offset)->type == OFFSET_NULL || (f->flags & WBUF)) {
+ if (f->flags & FD) {
+ emit_fdget(g, tag_offset, offset);
+ } else if ((*base_offset)->type == OFFSET_NULL || (f->flags & WBUF)) {
**base_offset = tag_offset;
} else if (f->flags & MASK || add) {
- emit_bitwise(g, f->type, BITWISE_OR, offset, offset,
- tag_offset);
+ emit_bitwise(g, f->type, NULL, BITWISE_OR,
+ offset, offset, tag_offset);
} else {
emit_copy_field(g, f, offset, tag_offset);
}
@@ -335,6 +337,9 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue,
if (json_object_get_string(tmp, "set"))
return true;
+ if (json_object_get_string(tmp, "get") && f->flags & FD)
+ return true;
+
if (level)
return true;
@@ -374,6 +379,7 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue,
return false;
case FDPATH:
+ case FDMOUNT:
case STRING:
return false;
case STRUCT:
@@ -433,7 +439,8 @@ static void parse_call(struct gluten_ctx *g, struct context_desc *cdesc,
struct gluten_offset offset[6] = { 0 }, ret_offset = { 0 };
bool is_ptr[6] = { false };
/* Minimum requirements for argument specification:
- * - if argument can be FDPATH, exactly one value for that position
+ * - if argument can be FDPATH/FDMOUNT, exactly one value for that
+ * position
* - if argument is a size field, value is optional
* - otherwise, every argument needs to be specified
*/
@@ -459,7 +466,7 @@ static void parse_call(struct gluten_ctx *g, struct context_desc *cdesc,
arg_check[a->pos].multi_field = true;
arg_check[a->pos].found = true;
- if (a->f.type == FDPATH)
+ if (a->f.type == FDPATH || a->f.type == FDMOUNT)
arg_check[a->pos].needs_fd = true;
if (a->f.size)
@@ -599,6 +606,9 @@ void handle_calls(struct gluten_ctx *g, JSON_Value *value)
}
name = json_object_get_name(obj, n);
+ if (!name)
+ continue;
+
value = json_object_get_value_at(obj, n);
args = json_object_get_object(obj, name);
diff --git a/cooker/calls/fs.c b/cooker/calls/fs.c
index 4135be9..29c653a 100644
--- a/cooker/calls/fs.c
+++ b/cooker/calls/fs.c
@@ -60,6 +60,7 @@ swapoff
#define _GNU_SOURCE
#include <sys/stat.h>
+#include <sys/xattr.h>
#include <fcntl.h>
#include <linux/limits.h>
@@ -200,10 +201,199 @@ static struct arg chown_args[] = {
{ 0 }
};
+static struct num xattr_flags[] = {
+ { "create", XATTR_CREATE },
+ { "replace", XATTR_REPLACE },
+ { 0 },
+};
+
+static struct arg fsetxattr_args[] = {
+ { 0,
+ {
+ "fd", INT, FD, 0, 0,
+ { 0 }
+ }
+ },
+ { 0,
+ {
+ "path", FDPATH, FD, 0, 0,
+ { 0 }
+ }
+ },
+ { 1,
+ {
+ "name", STRING, 0, 0,
+ XATTR_NAME_MAX,
+ { 0 }
+ }
+ },
+ { 2,
+ {
+ "value", STRING, WBUF, 0,
+ XATTR_SIZE_MAX,
+ { 0 }
+ }
+ },
+ { 3,
+ {
+ "size", LONG, 0, 0, 0,
+ { 0 }
+ }
+ },
+ { 4,
+ {
+ "flags", INT, FLAGS, 0, 0,
+ { .d_num = xattr_flags }
+ }
+ },
+ { 0 },
+};
+
+static struct arg fremovexattr_args[] = {
+ { 0,
+ {
+ "fd", INT, FD, 0, 0,
+ { 0 }
+ }
+ },
+ { 0,
+ {
+ "path", FDPATH, FD, 0, 0,
+ { 0 }
+ }
+ },
+ { 1,
+ {
+ "name", STRING, 0, 0,
+ XATTR_NAME_MAX,
+ { 0 }
+ }
+ },
+ { 0 },
+};
+
+/* enum fid_type copied (without comments) from include/linux/exportfs.h of
+ * Linux 6.9. Just copied and pasted like that? Is that an acceptable practice?
+ * Well then, let's say I _vendored_ it.
+ */
+enum fid_type {
+ FILEID_ROOT = 0,
+ FILEID_INO32_GEN = 1,
+ FILEID_INO32_GEN_PARENT = 2,
+ FILEID_BTRFS_WITHOUT_PARENT = 0x4d,
+ FILEID_BTRFS_WITH_PARENT = 0x4e,
+ FILEID_BTRFS_WITH_PARENT_ROOT = 0x4f,
+ FILEID_UDF_WITHOUT_PARENT = 0x51,
+ FILEID_UDF_WITH_PARENT = 0x52,
+ FILEID_NILFS_WITHOUT_PARENT = 0x61,
+ FILEID_NILFS_WITH_PARENT = 0x62,
+ FILEID_FAT_WITHOUT_PARENT = 0x71,
+ FILEID_FAT_WITH_PARENT = 0x72,
+ FILEID_INO64_GEN = 0x81,
+ FILEID_INO64_GEN_PARENT = 0x82,
+ FILEID_LUSTRE = 0x97,
+ FILEID_BCACHEFS_WITHOUT_PARENT = 0xb1,
+ FILEID_BCACHEFS_WITH_PARENT = 0xb2,
+ FILEID_KERNFS = 0xfe,
+ FILEID_INVALID = 0xff,
+};
+
+static struct num handle_types[] = {
+ { "root", FILEID_ROOT },
+ { "ino32_gen", FILEID_INO32_GEN },
+ { "ino32_gen_parent", FILEID_INO32_GEN_PARENT },
+ { "btrfs_with_parent", FILEID_BTRFS_WITH_PARENT },
+ { "btrfs_without_parent", FILEID_BTRFS_WITHOUT_PARENT },
+ { "udf_with_parent", FILEID_UDF_WITH_PARENT },
+ { "udf_without_parent", FILEID_UDF_WITHOUT_PARENT },
+ { "fat_with_parent", FILEID_FAT_WITH_PARENT },
+ { "fat_without_parent", FILEID_FAT_WITHOUT_PARENT },
+ { "ino64_gen", FILEID_INO64_GEN },
+ { "ino64_gen_parent", FILEID_INO64_GEN_PARENT },
+ { "lustre", FILEID_LUSTRE },
+ { "bcachefs_with_parent", FILEID_BCACHEFS_WITH_PARENT },
+ { "bcachefs_without_parent", FILEID_BCACHEFS_WITHOUT_PARENT },
+ { "kernfs", FILEID_KERNFS },
+ { "invalid", FILEID_INVALID },
+ { 0 },
+};
+
+static struct field file_handle[] = {
+ {
+ "handle_bytes", LONG, SIZE,
+ offsetof(struct file_handle, handle_bytes),
+ 0,
+ { .d_size = (intptr_t)&file_handle[2] },
+ },
+ {
+ "handle_type", INT, FLAGS,
+ offsetof(struct file_handle, handle_type),
+ 0,
+ { .d_num = handle_types },
+ },
+
+ {
+ "f_handle", STRING, 0,
+ offsetof(struct file_handle, f_handle),
+ MAX_HANDLE_SZ,
+ { 0 },
+ },
+ { 0 }
+};
+
+static struct num open_flags[] = {
+ { "rdonly", O_RDONLY },
+ { "wronly", O_WRONLY },
+ { "rdwr", O_RDWR },
+ { 0 },
+};
+
+static struct arg open_by_handle_at_args[] = {
+ { 0,
+ {
+ "mount", FDMOUNT, 0, 0,
+ 0,
+ { 0 }
+ }
+ },
+ { 0,
+ {
+ "mount_fd", INT, 0, 0,
+ 0,
+ { 0 }
+ }
+ },
+ { 1,
+ {
+ "handle", STRUCT, 0, 0,
+ sizeof(struct file_handle) + MAX_HANDLE_SZ,
+ { .d_struct = file_handle }
+ }
+ },
+ { 2,
+ {
+ "flags", INT, MASK | FLAGS, 0,
+ 0,
+ { .d_num = open_flags }
+ }
+ },
+ { 0 },
+};
+
struct call syscalls_fs[] = {
{ __NR_mknod, "mknod", mknod_args },
{ __NR_mknodat, "mknodat", mknodat_args },
+
{ __NR_chown, "chown", chown_args },
{ __NR_lchown, "lchown", chown_args },
+
+ { __NR_fsetxattr, "fsetxattr", fsetxattr_args },
+ /* fd, name, value, size, flags */
+
+ { __NR_fremovexattr, "fremovexattr", fremovexattr_args },
+ /* fd, name */
+
+ { __NR_open_by_handle_at, "open_by_handle_at", open_by_handle_at_args },
+
{ 0 },
};
diff --git a/cooker/calls/net.c b/cooker/calls/net.c
index 94b13cd..0688467 100644
--- a/cooker/calls/net.c
+++ b/cooker/calls/net.c
@@ -65,22 +65,23 @@ static struct num socket_flags[] = {
};
static struct num protocols[] = {
- { "ip", IPPROTO_IP },
- { "icmp", IPPROTO_ICMP },
- { "igmp", IPPROTO_IGMP },
- { "tcp", IPPROTO_TCP },
- { "udp", IPPROTO_UDP },
- { "ipv6", IPPROTO_IPV6 },
- { "gre", IPPROTO_GRE },
- { "esp", IPPROTO_ESP },
- { "ah", IPPROTO_AH },
- { "sctp", IPPROTO_SCTP },
- { "udplite", IPPROTO_UDPLITE },
- { "mpls", IPPROTO_MPLS },
- { "raw", IPPROTO_RAW },
- { "mptcp", IPPROTO_MPTCP },
+ { "ip", IPPROTO_IP },
+ { "icmp", IPPROTO_ICMP },
+ { "igmp", IPPROTO_IGMP },
+ { "tcp", IPPROTO_TCP },
+ { "udp", IPPROTO_UDP },
+ { "ipv6", IPPROTO_IPV6 },
+ { "gre", IPPROTO_GRE },
+ { "esp", IPPROTO_ESP },
+ { "ah", IPPROTO_AH },
+ { "sctp", IPPROTO_SCTP },
+ { "udplite", IPPROTO_UDPLITE },
+ { "mpls", IPPROTO_MPLS },
+ { "raw", IPPROTO_RAW },
+ { "mptcp", IPPROTO_MPTCP },
- { "nl_route", NETLINK_ROUTE },
+ { "nl_route", NETLINK_ROUTE },
+ { "nl_netfilter", NETLINK_NETFILTER },
{ 0 },
};
@@ -261,7 +262,7 @@ static struct num send_flags[] = {
static struct arg send_args[] = {
{ 0,
{
- "fd", INT, 0,
+ "fd", INT, FD,
0,
0,
{ 0 },
@@ -297,7 +298,7 @@ static struct arg send_args[] = {
static struct arg sendto_args[] = {
{ 0,
{
- "fd", INT, 0,
+ "fd", INT, FD,
0,
0,
{ 0 },
@@ -346,7 +347,7 @@ static struct arg sendto_args[] = {
{ 0 }
};
-static struct select sendmsg_name_select = {
+static struct select msg_name_select = {
&connect_family, { .d_num = connect_addr_select_family }
};
@@ -355,13 +356,13 @@ static struct field sendmsg_msghdr[] = {
"name", SELECT, 0,
offsetof(struct msghdr, msg_name),
sizeof(struct sockaddr_storage),
- { .d_select = &sendmsg_name_select },
+ { .d_select = &msg_name_select },
},
{
"namelen", LONG, SIZE,
offsetof(struct msghdr, msg_namelen),
0,
- { .d_size = (intptr_t)&sendmsg_name_select },
+ { .d_size = (intptr_t)&msg_name_select },
},
{
"iov", STRING, WBUF | IOV,
@@ -400,7 +401,7 @@ static struct field sendmsg_msghdr[] = {
static struct arg sendmsg_args[] = {
{ 0,
{
- "fd", INT, 0,
+ "fd", INT, FD,
0,
0,
{ 0 },
@@ -425,6 +426,53 @@ static struct arg sendmsg_args[] = {
{ 0 }
};
+static struct field recvmsg_msghdr[] = {
+ {
+ "name", SELECT, 0,
+ offsetof(struct msghdr, msg_name),
+ sizeof(struct sockaddr_storage),
+ { .d_select = &msg_name_select },
+ },
+ {
+ "namelen", LONG, SIZE,
+ offsetof(struct msghdr, msg_namelen),
+ 0,
+ { .d_size = (intptr_t)&msg_name_select },
+ },
+ {
+ "iov", STRING, RBUF | IOV,
+ offsetof(struct msghdr, msg_iov),
+ BUFSIZ,
+ { .d_iovlen = offsetof(struct msghdr, msg_iovlen) -
+ offsetof(struct msghdr, msg_iov) },
+ },
+ {
+ "iovlen", LONG, 0,
+ offsetof(struct msghdr, msg_iovlen),
+ 0,
+ { 0 },
+ },
+ {
+ "control", STRING, 0,
+ offsetof(struct msghdr, msg_control),
+ BUFSIZ,
+ { 0 },
+ },
+ {
+ "controllen", LONG, SIZE,
+ offsetof(struct msghdr, msg_controllen),
+ 0,
+ { 0 },
+ },
+ {
+ "flags", INT, 0,
+ offsetof(struct msghdr, msg_flags),
+ 0,
+ { 0 },
+ },
+ { 0 }
+};
+
static struct arg recvmsg_args[] = {
{ 0,
{
@@ -439,7 +487,7 @@ static struct arg recvmsg_args[] = {
"msg", STRUCT, 0,
0,
sizeof(struct msghdr),
- { .d_struct = sendmsg_msghdr },
+ { .d_struct = recvmsg_msghdr },
},
},
{ 2,
diff --git a/cooker/cooker.h b/cooker/cooker.h
index 05a1f1b..3fc714a 100644
--- a/cooker/cooker.h
+++ b/cooker/cooker.h
@@ -92,6 +92,8 @@ enum type {
GNU_DEV_MINOR,
FDPATH,
+ FDMOUNT,
+
UID_T,
GID_T,
@@ -114,6 +116,9 @@ enum flags {
WBUF = BIT(6),
IOV = BIT(7),
+
+ /* Copy file descriptor from target with pidfd_getfd() */
+ FD = BIT(8),
};
#define TYPE_COUNT (TYPE_END - 1)
diff --git a/cooker/emit.c b/cooker/emit.c
index 7899fa5..7d13a02 100644
--- a/cooker/emit.c
+++ b/cooker/emit.c
@@ -24,7 +24,8 @@ static const char *type_str[] = {
"PID",
"PORT", "IPV4", "IPV6",
"GNU_DEV_MAJOR", "GNU_DEV_MINOR",
- "FDPATH",
+ "FDPATH", "FDMOUNT",
+ "UID", "GID",
NULL
};
@@ -77,6 +78,37 @@ void emit_fd(struct gluten_ctx *g, struct fd_desc *desc)
}
/**
+ * emit_fdget() - Emit OP_FDGET instruction: copy file descriptor from target
+ * @g: gluten context
+ * @src: Pointer to existing file descriptor number in target process
+ * @dst: Pointer to new descriptor number in seitan, can be OFFSET_NULL
+ *
+ * Return: offset to destination operand, allocated here if not given
+ */
+struct gluten_offset emit_fdget(struct gluten_ctx *g,
+ struct gluten_offset src,
+ struct gluten_offset dst)
+{
+ struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
+ struct op_fdget *fdget = &op->op.fdget;
+
+ op->type = OP_FDGET;
+
+ if (dst.type == OFFSET_NULL)
+ dst = gluten_rw_alloc(g, sizeof(int));
+
+ fdget->src = src;
+ fdget->dst = dst;
+
+ debug(" %i: OP_FDGET: ...", g->ip.offset);
+
+ if (++g->ip.offset > INST_MAX)
+ die("Too many instructions");
+
+ return dst;
+}
+
+/**
* emit_call() - Emit OP_CALL instruction: execute a system call
* @g: gluten context
* @context: CONTEXT_SPEC_NONE-terminated array of context references
@@ -185,18 +217,19 @@ void emit_store(struct gluten_ctx *g, struct gluten_offset dst,
* @iov: Pointer to msg_iov, already stored in gluten
* @iovlen: Pointer to msg_iovlen, already stored in gluten
* @dst: gluten destination to copy dereferenced data
+ * @alloc: Allocation size (alloc - len bytes filled with zeroes)
* @len: Maximum length of data to copy altogether
*/
struct gluten_offset emit_iovload(struct gluten_ctx *g,
struct gluten_offset iov,
struct gluten_offset iovlen,
- size_t len)
+ size_t alloc, size_t len)
{
struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
struct op_iovload *load = &op->op.iovload;
struct gluten_offset dst;
- dst = gluten_rw_alloc(g, len);
+ dst = gluten_rw_alloc(g, alloc);
op->type = OP_IOVLOAD;
@@ -205,6 +238,7 @@ struct gluten_offset emit_iovload(struct gluten_ctx *g,
load->dst = dst;
load->size = len;
+ load->zero_fill = alloc - len;
debug(" %i: OP_IOVLOAD: #%i < (#%i) as iovec (size: %lu)",
g->ip.offset, dst.offset, iov.offset, len);
@@ -218,19 +252,21 @@ struct gluten_offset emit_iovload(struct gluten_ctx *g,
/**
* emit_resolved() - Emit OP_RESOLVEFD instruction: resolve file descriptor with path
* @g: gluten context
+ * @type: Type of field (FDPATH or FDMOUNT)
* @fd: offset of the file descriptor value
* @path: offset of the path
* @path_size: size of the path
*/
-void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd,
- struct gluten_offset path, size_t path_size)
+void emit_resolvefd(struct gluten_ctx *g, enum type type,
+ struct gluten_offset fd,
+ struct gluten_offset path, size_t path_size)
{
struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
struct op_resolvefd *resfd = &op->op.resfd;
struct gluten_offset o;
struct resolvefd_desc *desc;
- op->type = OP_RESOLVEDFD;
+ op->type = OP_RESOLVEFD;
o = gluten_ro_alloc(g, sizeof(struct resolvefd_desc));
desc = (struct resolvefd_desc *)gluten_ptr(&g->g, o);
@@ -238,6 +274,13 @@ void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd,
desc->path = path;
desc->path_max = path_size;
+ if (type == FDPATH)
+ desc->type = RESOLVEFD_PATH;
+ else if (type == FDMOUNT)
+ desc->type = RESOLVEFD_MOUNT;
+ else
+ die("Invalid field type for resolvefd");
+
resfd->desc = o;
debug(" %i: OP_RESOLVEDFD:", g->ip.offset);
@@ -253,6 +296,7 @@ void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd,
* emit_bitwise(): Emit OP_BITWISE instruction: bitwise operation and store
* @g: gluten context
* @type: Type of operands
+ * @vec: Description of vector structure, NULL for scalar operation
* @op_type: Type of bitwise operation
* @dst: gluten pointer to destination operand, can be OFFSET_NULL
* @x: gluten pointer to first source operand
@@ -261,6 +305,7 @@ void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd,
* Return: offset to destination operand, allocated here if not given
*/
struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type,
+ struct vec_desc *vec,
enum bitwise_type op_type,
struct gluten_offset dst,
struct gluten_offset x,
@@ -270,6 +315,7 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type,
struct op_bitwise *op_bitwise = &op->op.bitwise;
struct gluten_offset o;
struct bitwise_desc *desc;
+ char ip_str[BUFSIZ];
op->type = OP_BITWISE;
@@ -278,21 +324,42 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type,
desc->size = gluten_size[type];
desc->type = op_type;
- if (dst.type == OFFSET_NULL)
- desc->dst = gluten_rw_alloc(g, desc->size);
- else
+
+ if (vec)
+ desc->vec = *vec;
+
+ if (dst.type == OFFSET_NULL) {
+ size_t dst_size;
+ if (vec) /* FIXME: UIO_MAXIOV (1024) is a dubious choice */
+ dst_size = (desc->size + sizeof(uint32_t)) * 1024;
+ else
+ dst_size = desc->size;
+
+ desc->dst = gluten_rw_alloc(g, dst_size);
+ } else {
desc->dst = dst;
+ }
+
desc->x = x;
desc->y = y;
op_bitwise->desc = o;
- debug(" %i: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %s: #%lu",
- g->ip.offset,
+ snprintf(ip_str, BUFSIZ, "%i", g->ip.offset);
+
+ debug(" %s: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %s: #%lu",
+ ip_str,
gluten_offset_name[desc->dst.type], desc->dst.offset, desc->size,
gluten_offset_name[desc->x.type], desc->x.offset,
bitwise_type_str[op_type],
gluten_offset_name[desc->y.type], desc->y.offset);
+ if (vec) {
+ memset(ip_str, ' ', strlen(ip_str));
+ debug(" %s vector start: %s: #%lu, length descriptor at %lu",
+ ip_str,
+ gluten_offset_name[vec->start.type], vec->start.offset,
+ vec->len_offset);
+ }
if (++g->ip.offset > INST_MAX)
die("Too many instructions");
@@ -304,12 +371,14 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type,
* emit_cmp(): Emit OP_CMP instruction: compare data from two offsets
* @g: gluten context
* @cmp_type: Type of comparison
+ * @vec: Description of vector structure, NULL for scalar 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 vec_desc *vec,
struct gluten_offset x, struct gluten_offset y, size_t size,
enum jump_type jmp)
{
@@ -326,6 +395,11 @@ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type,
desc->x = x;
desc->y = y;
desc->size = size;
+ if (vec)
+ desc->vec = *vec;
+ else
+ desc->vec.start.type = OFFSET_NULL;
+
desc->cmp = cmp_type;
desc->jmp.type = OFFSET_INSTRUCTION;
desc->jmp.offset = jmp;
@@ -347,17 +421,18 @@ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type,
* emit_cmp_field() - Emit OP_CMP for a given field type
* @g: gluten context
* @cmp: Type of comparison
+ * @vec: Description of vector structure, NULL for scalar 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 vec_desc *vec, struct field *field,
struct gluten_offset x, struct gluten_offset y,
enum jump_type jmp)
{
- emit_cmp(g, cmp, x, y,
+ emit_cmp(g, cmp, vec, x, y,
field->size ? field->size : gluten_size[field->type],
jmp);
}
diff --git a/cooker/emit.h b/cooker/emit.h
index 5557187..7948071 100644
--- a/cooker/emit.h
+++ b/cooker/emit.h
@@ -8,6 +8,9 @@
void emit_nr(struct gluten_ctx *g, struct gluten_offset number);
void emit_fd(struct gluten_ctx *g, struct fd_desc *desc);
+struct gluten_offset emit_fdget(struct gluten_ctx *g,
+ struct gluten_offset src,
+ struct gluten_offset dst);
void emit_call(struct gluten_ctx *g, struct context_desc *cdesc, long nr,
unsigned count, bool is_ptr[6],
struct gluten_offset offset[6], struct gluten_offset ret_offset);
@@ -16,20 +19,21 @@ void emit_load(struct gluten_ctx *g, struct gluten_offset dst,
struct gluten_offset emit_iovload(struct gluten_ctx *g,
struct gluten_offset iov,
struct gluten_offset iovlen,
- size_t len);
+ size_t alloc, size_t len);
void emit_store(struct gluten_ctx *g, struct gluten_offset dst,
struct gluten_offset src, struct gluten_offset count);
struct gluten_offset emit_seccomp_data(int index);
struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type,
+ struct vec_desc *desc,
enum bitwise_type op_type,
struct gluten_offset dst,
struct gluten_offset x,
struct gluten_offset y);
-void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp,
+void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, struct vec_desc *vec,
struct gluten_offset x, struct gluten_offset y, size_t size,
enum jump_type jmp);
void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp,
- struct field *field,
+ struct vec_desc *vec, struct field *field,
struct gluten_offset base, struct gluten_offset match,
enum jump_type jmp);
void emit_return(struct gluten_ctx *g, struct gluten_offset v,
@@ -38,8 +42,9 @@ 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);
-void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd,
- struct gluten_offset path, size_t path_size);
+void emit_resolvefd(struct gluten_ctx *g, enum type type,
+ struct gluten_offset fd,
+ struct gluten_offset path, size_t path_size);
void emit_end(struct gluten_ctx *g);
struct gluten_offset emit_data(struct gluten_ctx *g, enum type type,
size_t str_len, union value *value);
diff --git a/cooker/filter.c b/cooker/filter.c
index e0c8081..765fa25 100644
--- a/cooker/filter.c
+++ b/cooker/filter.c
@@ -540,6 +540,11 @@ unsigned int filter_build(struct sock_filter filter[], unsigned n)
/* pre-check instruction + load syscall number (4 instructions) */
accept = size + n_nodes + n;
+
+ /* FIXME: See below */
+ if (n > 1 && n % 2)
+ accept++;
+
notify = accept + 1;
/* Insert nodes */
@@ -570,6 +575,13 @@ unsigned int filter_build(struct sock_filter filter[], unsigned n)
*/
next_offset += get_n_args_syscall_instr(nr) - 1;
}
+
+ /* FIXME: If we have an odd number of leaves, duplicate the last one,
+ * otherwise, left_child() will miscalculate terminal jump offsets.
+ */
+ if (n > 1 && n % 2)
+ filter[size++] = EQ(nr, offset - 1, accept - size);
+
/* Seccomp accept and notify instruction */
filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF);
diff --git a/cooker/gluten.c b/cooker/gluten.c
index 69c9384..ac5a4bd 100644
--- a/cooker/gluten.c
+++ b/cooker/gluten.c
@@ -26,6 +26,8 @@ size_t gluten_size[TYPE_COUNT] = {
[GNU_DEV_MAJOR] = sizeof(unsigned long long int),
[GNU_DEV_MINOR] = sizeof(unsigned long long int),
+
+ [FDMOUNT] = sizeof(int),
};
const char *jump_name[JUMP_COUNT] = { "next block", "next match", "next action",
diff --git a/cooker/match.c b/cooker/match.c
index c56d9e5..f65ac5c 100644
--- a/cooker/match.c
+++ b/cooker/match.c
@@ -22,9 +22,70 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
static struct num netlink_types[] = {
- { "newroute", RTM_NEWROUTE },
+ { "newroute", RTM_NEWROUTE },
+
+#define SUBSYS_NFCOMPAT(x) (NFNL_SUBSYS_NFT_COMPAT << 8 | (x))
+ { "nf_compat_get", (SUBSYS_NFCOMPAT(NFNL_MSG_COMPAT_GET))},
+
+#define SUBSYS_NFT(x) (NFNL_SUBSYS_NFTABLES << 8 | (x))
+ { "nf_get_any",
+ SUBSYS_NFT(NFT_MSG_GETTABLE | NFT_MSG_GETCHAIN | NFT_MSG_GETRULE |
+ NFT_MSG_GETSET | NFT_MSG_GETSETELEM | NFT_MSG_GETGEN |
+ NFT_MSG_GETFLOWTABLE | NFT_MSG_GETOBJ) |
+ SUBSYS_NFCOMPAT(NFNL_MSG_COMPAT_GET) },
+
+#undef SUBSYS_NFCOMPAT
+
+ { "nf_newtable", SUBSYS_NFT(NFT_MSG_NEWTABLE) },
+ { "nf_gettable", SUBSYS_NFT(NFT_MSG_GETTABLE) },
+ { "nf_deltable", SUBSYS_NFT(NFT_MSG_DELTABLE) },
+ { "nf_destroytable", SUBSYS_NFT(NFT_MSG_DESTROYTABLE) },
+ /* ignores ENOENT */
+
+ { "nf_newchain", SUBSYS_NFT(NFT_MSG_NEWCHAIN) },
+ { "nf_getchain", SUBSYS_NFT(NFT_MSG_GETCHAIN) },
+ { "nf_delchain", SUBSYS_NFT(NFT_MSG_DELCHAIN) },
+ { "nf_destroychain", SUBSYS_NFT(NFT_MSG_DESTROYCHAIN) },
+
+ { "nf_newrule", SUBSYS_NFT(NFT_MSG_NEWRULE) },
+ { "nf_getrule", SUBSYS_NFT(NFT_MSG_GETRULE) },
+ { "nf_getrule_reset", SUBSYS_NFT(NFT_MSG_GETRULE_RESET) },
+ { "nf_delrule", SUBSYS_NFT(NFT_MSG_DELRULE) },
+ { "nf_destroyrule", SUBSYS_NFT(NFT_MSG_DESTROYRULE) },
+
+ { "nf_newset", SUBSYS_NFT(NFT_MSG_NEWSET) },
+ { "nf_getset", SUBSYS_NFT(NFT_MSG_GETSET) },
+ { "nf_delset", SUBSYS_NFT(NFT_MSG_DELSET) },
+ { "nf_destroyset", SUBSYS_NFT(NFT_MSG_DESTROYSET) },
+
+ { "nf_newsetelem", SUBSYS_NFT(NFT_MSG_NEWSETELEM) },
+ { "nf_getsetelem", SUBSYS_NFT(NFT_MSG_GETSETELEM) },
+ { "nf_getsetelem_reset", SUBSYS_NFT(NFT_MSG_GETSETELEM_RESET) },
+ { "nf_delsetelem", SUBSYS_NFT(NFT_MSG_DELSETELEM) },
+ { "nf_destroysetelem", SUBSYS_NFT(NFT_MSG_DESTROYSETELEM) },
+
+ { "nf_newgen", SUBSYS_NFT(NFT_MSG_NEWGEN) },
+ { "nf_getgen", SUBSYS_NFT(NFT_MSG_GETGEN) },
+
+ { "nf_trace", SUBSYS_NFT(NFT_MSG_TRACE) },
+
+ { "nf_newobj", SUBSYS_NFT(NFT_MSG_NEWOBJ) },
+ { "nf_getobj", SUBSYS_NFT(NFT_MSG_GETOBJ) },
+ { "nf_getobj_reset", SUBSYS_NFT(NFT_MSG_GETOBJ_RESET) },
+ { "nf_delobj", SUBSYS_NFT(NFT_MSG_DELOBJ) },
+ { "nf_destroyobj", SUBSYS_NFT(NFT_MSG_DESTROYOBJ) },
+
+ { "nf_newflowtable", SUBSYS_NFT(NFT_MSG_NEWFLOWTABLE) },
+ { "nf_getflowtable", SUBSYS_NFT(NFT_MSG_GETFLOWTABLE) },
+ { "nf_delflowtable", SUBSYS_NFT(NFT_MSG_DELFLOWTABLE) },
+ { "nf_destroyflowtable", SUBSYS_NFT(NFT_MSG_DESTROYFLOWTABLE) },
+#undef SUBSYS_NFT
+
{ 0 },
};
@@ -109,6 +170,7 @@ static struct gluten_offset arg_load(struct gluten_ctx *g, struct arg *a)
*/
static union value parse_field(struct gluten_ctx *g,
struct gluten_offset offset,
+ struct vec_desc *vec,
enum op_cmp_type cmp, enum jump_type jump,
int index, struct field *f, JSON_Value *jvalue)
{
@@ -134,7 +196,7 @@ ______________________ _____________
*/
v.v_num = ((long long)0xfff << 44) | (0xfff << 8);
mask_offset = emit_data(g, U64, 0, &v);
- offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET,
+ offset = emit_bitwise(g, U64, NULL, BITWISE_AND, NULL_OFFSET,
offset, mask_offset);
break;
case GNU_DEV_MINOR:
@@ -144,7 +206,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
*/
v.v_num = 0xff | ((long long)0xfff << 20);
mask_offset = emit_data(g, U64, 0, &v);
- offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET,
+ offset = emit_bitwise(g, U64, NULL, BITWISE_AND, NULL_OFFSET,
offset, mask_offset);
break;
default:
@@ -155,9 +217,19 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
if (f->flags & IOV) {
struct gluten_offset iovlen = offset;
+ size_t alloc;
+
+ /* vectorised ops need a zero length descriptor at the end */
+ if (json_value_get_type(jvalue) == JSONObject &&
+ (tmp = json_value_get_object(jvalue)) &&
+ json_object_get_value(tmp, "netlink"))
+ alloc = f->size +
+ sizeof(((struct nlmsghdr *)NULL)->nlmsg_len);
+ else
+ alloc = f->size;
iovlen.offset += f->desc.d_iovlen;
- offset = emit_iovload(g, offset, iovlen, f->size);
+ offset = emit_iovload(g, offset, iovlen, alloc, f->size);
}
if (json_value_get_type(jvalue) == JSONObject &&
@@ -188,7 +260,16 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
}
jvalue = json_array_get_value(set, i);
- parse_field(g, offset, cmp, jump, index, f, jvalue);
+
+ /* FIXME: ugly. Otherwise nested parse_field() will
+ * increment twice.
+ */
+ offset.offset -= f->offset;
+
+ parse_field(g, offset, vec, cmp, jump, index, f,
+ jvalue);
+
+ offset.offset += f->offset;
}
return v; /* No SELECT based on sets... of course */
@@ -215,12 +296,12 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
&set, &cmp, &cmpterm);
set_offset = emit_data(g, f->type, 0, &set);
- masked = emit_bitwise(g, f->type, BITWISE_AND,
+ masked = emit_bitwise(g, f->type, vec, BITWISE_AND,
NULL_OFFSET,
offset, set_offset);
cmp_offset = emit_data(g, f->type, 0, &cmpterm);
- emit_cmp(g, cmp, masked, cmp_offset,
+ emit_cmp(g, cmp, vec, masked, cmp_offset,
gluten_size[f->type], jump);
emit_bpf_arg(index, f->type, cmpterm, set, cmp, g->mode);
@@ -241,9 +322,13 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
} else if (f->flags & MASK) {
mask.v_num = value_get_mask(f->desc.d_num);
mask_offset = emit_data(g, f->type, 0, &mask);
- data_offset = emit_bitwise(g, f->type, BITWISE_AND,
+
+ data_offset = emit_bitwise(g, f->type, vec, BITWISE_AND,
NULL_OFFSET, offset,
mask_offset);
+ if (vec)
+ vec->len_offset = 0;
+
v.v_num = value_get_num(f->desc.d_num, jvalue);
} else {
v.v_num = value_get_num(f->desc.d_num, jvalue);
@@ -251,7 +336,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
const_offset = emit_data(g, f->type, 0, &v);
- emit_cmp(g, cmp, data_offset, const_offset,
+ emit_cmp(g, cmp, vec, data_offset, const_offset,
gluten_size[f->type], jump);
emit_bpf_arg(index, f->type, v, mask, cmp, g->mode);
@@ -266,7 +351,7 @@ ______________________ _____________
v.v_num = (v.v_num & 0xfff) << 8 | (v.v_num & ~0xfff) << 32;
const_offset = emit_data(g, U64, 0, &v);
- emit_cmp_field(g, cmp, f, offset, const_offset, jump);
+ emit_cmp_field(g, cmp, vec, f, offset, const_offset, jump);
filter_needs_deref(); /* No shifts in BPF */
break;
@@ -278,7 +363,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
v.v_num = value_get_num(f->desc.d_num, jvalue);
v.v_num = (v.v_num & 0xff) | (v.v_num & ~0xfff) << 12;
const_offset = emit_data(g, U64, 0, &v);
- emit_cmp_field(g, cmp, f, offset, const_offset, jump);
+ emit_cmp_field(g, cmp, vec, f, offset, const_offset, jump);
filter_needs_deref(); /* No shifts in BPF */
break;
@@ -292,17 +377,43 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
sel = jvalue;
}
- v = parse_field(g, offset, cmp, jump, index, f_inner, sel);
+ v = parse_field(g, offset, vec, cmp, jump, index, f_inner, sel);
f = select_field(g, index, f->desc.d_select, v);
- if (f)
- parse_field(g, offset, cmp, jump, index, f, jvalue);
+ if (f) {
+ parse_field(g, offset, vec, cmp, jump, index, f,
+ jvalue);
+ }
break;
case STRING:
if (json_value_get_type(jvalue) == JSONObject &&
(tmp = json_value_get_object(jvalue))) {
if ((jvalue = json_object_get_value(tmp, "netlink"))) {
- parse_field(g, offset, cmp, jump, index,
+ struct vec_desc v_nl, *vecptr;
+
+ /* FIXME: even send()/sendto() might need
+ * vectorised operations
+ */
+ if (f->flags & IOV) {
+ v_nl.start = offset;
+ v_nl.len_offset = offsetof(struct nlmsghdr,
+ nlmsg_len);
+ vecptr = &v_nl;
+ } else {
+ vecptr = NULL;
+ }
+
+ /* TODO: mark as CMPVEC, with:
+ * - offset of length (offsetof nlmsghdr)
+ * - vector pointer: that's already offset?
+ * - offset of comparison in vector (0 atm)
+ * - second term of comparison (from jvalue)
+ * - length (still needed?)
+ *
+ * plus BITWISEVEC? is it even needed?
+ */
+
+ parse_field(g, offset, vecptr, cmp, jump, index,
&netlink_header, jvalue);
} else {
die(" unrecognised blob type");
@@ -317,18 +428,19 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
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);
+ emit_cmp(g, CMP_NE, vec, offset, const_offset,
+ strlen(v.v_str) + 1, JUMP_NEXT_BLOCK);
break;
case FDPATH:
+ case FDMOUNT:
v.v_str = json_value_get_string(jvalue);
size = strlen(v.v_str) + 1;
offset = gluten_rw_alloc(g, size);
const_offset = emit_data(g, STRING, size, &v);
seccomp_offset = emit_seccomp_data(index);
- emit_resolvefd(g, seccomp_offset, offset, size);
- emit_cmp(g, CMP_NE, offset, const_offset, size,
- JUMP_NEXT_BLOCK);
+ emit_resolvefd(g, f->type, seccomp_offset, offset, size);
+ emit_cmp(g, CMP_NE, vec, offset, const_offset,
+ size, JUMP_NEXT_BLOCK);
break;
case STRUCT:
for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) {
@@ -339,7 +451,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
if (!field_value)
continue;
- parse_field(g, offset, cmp, jump, index, f_inner,
+ parse_field(g, offset, vec, cmp, jump, index, f_inner,
field_value);
}
break;
@@ -364,7 +476,8 @@ static void parse_arg(struct gluten_ctx *g, JSON_Value *jvalue, struct arg *a)
offset = arg_load(g, a);
- parse_field(g, offset, CMP_NE, JUMP_NEXT_BLOCK, a->pos, &a->f, jvalue);
+ parse_field(g, offset, NULL, CMP_NE, JUMP_NEXT_BLOCK, a->pos, &a->f,
+ jvalue);
}
/**
@@ -406,16 +519,25 @@ static void parse_match(struct gluten_ctx *g, JSON_Object *obj,
void handle_matches(struct gluten_ctx *g, JSON_Value *value)
{
JSON_Array *matches = json_value_get_array(value);
- unsigned i;
+ unsigned i, count;
- for (i = 0; i < json_array_get_count(matches); i++) {
+ if (matches)
+ count = json_array_get_count(matches);
+ else
+ count = 1;
+
+ for (i = 0; i < count; i++) {
JSON_Object *match, *args;
struct call **set, *call;
const char *name;
+ if (matches)
+ match = json_array_get_object(matches, i);
+ else
+ match = json_value_get_object(value);
+
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);
diff --git a/demo/Makefile b/demo/Makefile
new file mode 100644
index 0000000..6b8558f
--- /dev/null
+++ b/demo/Makefile
@@ -0,0 +1,9 @@
+SRCS=$(wildcard *.c)
+
+all: $(SRCS:%.c=%)
+
+clean:
+ $(RM) $(SRCS:%.c=%)
+
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $< \ No newline at end of file
diff --git a/demo/fremovexattr.c b/demo/fremovexattr.c
new file mode 100644
index 0000000..1483975
--- /dev/null
+++ b/demo/fremovexattr.c
@@ -0,0 +1,11 @@
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ int fd = open(argv[1], O_RDONLY);
+
+ return fremovexattr(fd, argv[2]);
+}
diff --git a/demo/fsetxattr.c b/demo/fsetxattr.c
new file mode 100644
index 0000000..6ee423a
--- /dev/null
+++ b/demo/fsetxattr.c
@@ -0,0 +1,11 @@
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ int fd = open(argv[1], O_RDONLY);
+
+ return fsetxattr(fd, argv[2], argv[3], strlen(argv[3]) + 1, 0);
+}
diff --git a/demo/iptables.hjson b/demo/iptables.hjson
new file mode 100644
index 0000000..d24f476
--- /dev/null
+++ b/demo/iptables.hjson
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
+ *
+ * demo/iptables.hjson - Example for iptables-nft: fetch ruleset, add rules
+ *
+ * Copyright (c) 2024 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ *
+ * Example of stand-alone usage:
+ *
+ * ./seitan-cooker \
+ * -i demo/iptables.hjson -g demo/iptables.gluten -f demo/iptables.bpf
+ * ./seitan-eater \
+ * -i demo/iptables.bpf -- /sbin/iptables -t mangle -A FORWARD -j LOG
+ * # blocks
+ *
+ * ./seitan -p $(pgrep seitan-eater) -i demo/iptables.gluten
+ * # as root or with CAP_NET_ADMIN: adds rule on behalf of caller
+ *
+ * ./seitan-eater \
+ * -i demo/iptables.bpf -- /sbin/iptables -t mangle -D FORWARD -j LOG
+ * # blocks
+ *
+ * ./seitan -p $(pgrep seitan-eater) -i demo/iptables.gluten
+ * # as root or with CAP_NET_ADMIN: deletes rule on behalf of caller
+ */
+
+[
+ /* When the target process tries to open a netlink socket for netfilter, open
+ * one on behalf of the caller, owned by us, and replace it in the caller.
+ *
+ * For netlink operations, there's always a double permission check:
+ * both opener of the socket and sender of the messages need to have matching
+ * capabilities, see netlink_ns_capable() in net/netlink/af_netlink.c.
+ *
+ * This block takes care of the first part.
+ */
+ {
+ "match": {
+ "socket": {
+ "family" : "netlink",
+ "type" : "raw",
+ "protocol" : "nl_netfilter"
+ }
+ /* socket(2) doesn't point to memory, so we can safely let any unrelated
+ * system calls proceed, directly in the caller, without replaying it
+ * ourselves.
+ */
+ },
+ "call": {
+ "socket": {
+ "family" : "netlink",
+ "type" : "raw",
+ "flags" : 0,
+ "protocol" : "nl_netfilter"
+ },
+ "ret": "fd"
+ },
+ "fd": {
+ "src": { "get": "fd" },
+ "close_on_exec": true,
+ "return": true
+ }
+ },
+
+ /* Second part: send messages on behalf of the target process.
+ *
+ * First, the ones iptables needs to check for nftables compatibility, and to
+ * fetch tables, chains, rules and sets, including their generation (version)
+ * identifier.
+ *
+ * These are simple messages, without batches, using sendto().
+ */
+ {
+ "match": {
+ "sendto": {
+ "fd": {
+ "set": "fd"
+ },
+ "buf": {
+ "set": "buf",
+ "value": {
+ "netlink": {
+ "type": {
+ "in": [
+ "nf_compat_get",
+ "nf_getgen",
+ "nf_gettable",
+ "nf_getchain",
+ "nf_getrule",
+ "nf_getset"
+ ]
+ }
+ }
+ }
+ },
+ "len" : { "set": "len" },
+ "addr": { "set": "addr" }
+ }
+ },
+ "call": {
+ "sendto": {
+ "fd": { "get": "fd" },
+ "buf": { "get": "buf" },
+ "len": { "get": "len" },
+ "addr": { "get": "addr" },
+ "flags": 0
+ },
+ "ret": "rc"
+ },
+ "return": { "value": "rc", "error": "rc" }
+ },
+
+ /* sendto(2) points to memory, so we need to match on any unrelated sendto()
+ * call and replay it ourselves, but pretending we're the original process
+ * (see "context" below). Otherwise, a thread of the target process can fool
+ * us into sending other messages with our capability set.
+ */
+ {
+ "match": {
+ "sendto": {
+ "fd": { "set": "fd" },
+ "buf": { "set": "buf" },
+ "len": { "set": "len" },
+ "addr": { "set": "addr" },
+ "flags": { "set": "flags" }
+ }
+ },
+ "call": {
+ "sendto": {
+ "fd": { "get": "fd" },
+ "buf": { "get": "buf" },
+ "len": { "get": "len" },
+ "addr": { "get": "addr" },
+ "flags": { "get": "flags" }
+ },
+ "context": { "uid": "caller", "gid": "caller" }
+ }
+ },
+
+ /* Now deal with the actual rule insertion or deletion: those are batched
+ * messages, using sendmsg(). Replay the message and relay return code and any
+ * error back.
+ */
+ {
+ "match": {
+ "sendmsg": {
+ "fd": { "set": "fd" },
+ "msg": {
+ "iov": {
+ "set": "buf",
+ "value": {
+ "netlink": {
+ "type": { "in": [ "nf_newrule", "nf_delrule" ] }
+ }
+ }
+ }
+ }
+ }
+ },
+ "call": {
+ "sendmsg": {
+ "fd": { "get": "fd" },
+ "msg": {
+ "name": {
+ "family": "netlink",
+ "pid": 0,
+ "groups": 0
+ },
+ "iovlen": 1,
+ "iov": { "get": "buf" }
+ },
+ "flags": 0
+ },
+ "ret": "rc"
+ },
+ "return": { "value": "rc", "error": "rc" }
+ },
+
+ /* Same as sendto(2): sendmsg(2) points to memory. Replay any unrelated system
+ * call with the credentials from the target process.
+ */
+ {
+ "match": {
+ "sendmsg": {
+ "fd": { "set": "fd" },
+ "msg": {
+ "name": { "set": "name" },
+ "namelen": { "set": "namelen" },
+ "iov": { "set": "buf" },
+ "control": { "set": "control" },
+ "controllen": { "set": "controllen" }
+ },
+ "flags": { "set": "flags" }
+ }
+ },
+ "call": {
+ "sendmsg": {
+ "fd": { "get": "fd" },
+ "msg": {
+ "name": { "get": "name" },
+ "namelen": { "get": "namelen" },
+ "iov": { "get": "buf" },
+ "iovlen": 1,
+ "control": { "get": "control" },
+ "controllen": { "get": "controllen" }
+ },
+ "flags": { "get": "flags" }
+ },
+ "context": { "uid": "caller", "gid": "caller" }
+ }
+ }
+]
diff --git a/demo/nft.hjson b/demo/nft.hjson
new file mode 100644
index 0000000..4ad7f1d
--- /dev/null
+++ b/demo/nft.hjson
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
+ *
+ * demo/nft.hjson - Example for nft(8): fetch ruleset, add/delete set elements
+ *
+ * Copyright (c) 2024 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ *
+ * Example of stand-alone usage. Prepare a table and a set for testing, first:
+ *
+ * nft add table ip test_t
+ * nft add set ip test_t test_s '{ type ipv4_addr ; }'
+ *
+ * Now add the set element:
+ *
+ * ./seitan-cooker \
+ * -i demo/nft.hjson -g demo/nft.gluten -f demo/nft.bpf
+ * ./seitan-eater \
+ * -i demo/nft.bpf -- /sbin/nft add element ip test_t test_s { 1.2.3.4 }
+ * # blocks
+ *
+ * ./seitan -p $(pgrep seitan-eater) -i demo/nft.gluten
+ * # as root or with CAP_NET_ADMIN: add element on behalf of caller
+ *
+ * nft list ruleset # Check that the new element is there
+ *
+ * ./seitan-eater \
+ * -i demo/nft.bpf -- /sbin/nft delete element ip test_t test_s { 1.2.3.4 }
+ * # blocks
+ *
+ * ./seitan -p $(pgrep seitan-eater) -i demo/nft.gluten
+ * # as root or with CAP_NET_ADMIN: deletes element on behalf of caller
+ *
+ */
+
+[
+ /* Open netlink socket on behalf of the caller, owned by us, and replace in
+ * the caller.
+ *
+ * For netlink operations, there's always a double permission check:
+ * both opener of the socket and sender of the messages need to have matching
+ * capabilities, see netlink_ns_capable() in net/netlink/af_netlink.c.
+ *
+ * This block takes care of the first part.
+ */
+ {
+ "match": {
+ "socket": {
+ "family" : "netlink",
+ "type" : "raw",
+ "protocol" : "nl_netfilter"
+ }
+ /* socket(2) doesn't point to memory, so we can safely let any unrelated
+ * system calls proceed, directly in the caller, without replaying it
+ * ourselves.
+ */
+ },
+ "call": {
+ "socket": {
+ "family" : "netlink",
+ "type" : "raw",
+ "flags" : 0,
+ "protocol" : "nl_netfilter"
+ },
+ "ret": "fd"
+ },
+ "fd": {
+ "src": { "get": "fd" },
+ "close_on_exec": true,
+ "return": true
+ }
+ },
+
+ /* Second part: send messages on behalf of the target process.
+ *
+ * First, the ones to fetch tables, chains, sets, flow tables, and objects,
+ * including their generation (version) identifier.
+ *
+ * These are simple messages, without batches, using sendto().
+ */
+ {
+ "match": {
+ "sendto": {
+ "fd": {
+ "set": "fd"
+ },
+ "buf": {
+ "set": "buf",
+ "value": {
+ "netlink": {
+ "type": {
+ "in": [
+ "nf_getgen",
+ "nf_gettable",
+ "nf_getchain",
+ "nf_getobj",
+ "nf_getflowtable",
+ "nf_getset"
+ ]
+ }
+ }
+ }
+ },
+ "len" : { "set": "len" },
+ "addr": { "set": "addr" }
+ }
+ },
+ "call": {
+ "sendto": {
+ "fd": { "get": "fd" },
+ "buf": { "get": "buf" },
+ "len": { "get": "len" },
+ "addr": { "get": "addr" },
+ "flags": 0
+ },
+ "ret": "rc"
+ },
+ "return": { "value": "rc", "error": "rc" }
+ },
+
+ /* sendto(2) points to memory, so we need to match on any unrelated sendto()
+ * call and replay it ourselves, but pretending we're the original process
+ * (see "context" below). Otherwise, a thread of the target process can fool
+ * us into sending other messages with our capability set.
+ */
+ {
+ "match": {
+ "sendto": {
+ "fd": { "set": "fd" },
+ "buf": { "set": "buf" },
+ "len": { "set": "len" },
+ "addr": { "set": "addr" },
+ "flags": { "set": "flags" }
+ }
+ },
+ "call": {
+ "sendto": {
+ "fd": { "get": "fd" },
+ "buf": { "get": "buf" },
+ "len": { "get": "len" },
+ "addr": { "get": "addr" },
+ "flags": { "get": "flags" }
+ },
+ "context": { "uid": "caller", "gid": "caller" }
+ }
+ },
+
+ /* Now deal with the actual element insertion or deletion: those are batched
+ * messages, using sendmsg(). Replay the message and relay return code and any
+ * error back.
+ */
+ {
+ "match": {
+ "sendmsg": {
+ "fd": { "set": "fd" },
+ "msg": {
+ "iov": {
+ "set": "buf",
+ "value": {
+ "netlink": {
+ "type": { "in": [ "nf_newsetelem", "nf_delsetelem" ] }
+ }
+ }
+ }
+ }
+ }
+ },
+ "call": {
+ "sendmsg": {
+ "fd": { "get": "fd" },
+ "msg": {
+ "name": {
+ "family": "netlink",
+ "pid": 0,
+ "groups": 0
+ },
+ "iovlen": 1,
+ "iov": { "get": "buf" }
+ },
+ "flags": 0
+ },
+ "ret": "rc"
+ },
+ "return": { "value": "rc", "error": "rc" }
+ },
+
+ /* Same as sendto(2): sendmsg(2) points to memory. Replay any unrelated system
+ * call with the credentials from the target process.
+ */
+ {
+ "match": {
+ "sendmsg": {
+ "fd": { "set": "fd" },
+ "msg": {
+ "name": { "set": "name" },
+ "namelen": { "set": "namelen" },
+ "iov": { "set": "buf" },
+ "control": { "set": "control" },
+ "controllen": { "set": "controllen" }
+ },
+ "flags": { "set": "flags" }
+ }
+ },
+ "call": {
+ "sendmsg": {
+ "fd": { "get": "fd" },
+ "msg": {
+ "name": { "get": "name" },
+ "namelen": { "get": "namelen" },
+ "iov": { "get": "buf" },
+ "iovlen": 1,
+ "control": { "get": "control" },
+ "controllen": { "get": "controllen" }
+ },
+ "flags": { "get": "flags" }
+ },
+ "context": { "uid": "caller", "gid": "caller" }
+ }
+ }
+]
diff --git a/demo/open_by_handle_at.c b/demo/open_by_handle_at.c
new file mode 100644
index 0000000..1f48eca
--- /dev/null
+++ b/demo/open_by_handle_at.c
@@ -0,0 +1,22 @@
+#define _GNU_SOURCE
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+ struct {
+ struct file_handle f;
+ unsigned char h[MAX_HANDLE_SZ];
+ } handle = { .f.handle_bytes = MAX_HANDLE_SZ };
+ int mount_fd, fd;
+ char buf[BUFSIZ];
+
+ name_to_handle_at(AT_FDCWD, argv[1], &handle.f, &mount_fd, 0);
+ fd = open_by_handle_at(mount_fd, &handle.f, 0);
+ read(fd, buf, BUFSIZ);
+ fprintf(stdout, "%s", buf);
+}
diff --git a/demo/virtiofsd.hjson b/demo/virtiofsd.hjson
new file mode 100644
index 0000000..8dfde03
--- /dev/null
+++ b/demo/virtiofsd.hjson
@@ -0,0 +1,93 @@
+[
+ {
+ "match": [
+ {
+ "fsetxattr": {
+ "fd": { "set": "fd" },
+ "name": "security.overrated",
+ "value": { "set": "label" },
+ "size": { "set": "size" },
+ "flags": { "set": "flags" }
+ }
+ }
+ ],
+
+ "call": [
+ {
+ "fsetxattr": {
+ "fd": { "get": "fd" },
+ "name": "security.overrated",
+ "value": { "get": "label" },
+ "size": { "get": "size" },
+ "flags": { "get": "flags" }
+ },
+ "ret": "rc"
+ }
+ ],
+
+ "return": { "value": "rc", "error": "rc" }
+ },
+
+ {
+ "match": [
+ {
+ "fremovexattr": {
+ "fd": { "set": "fd" },
+ "name": "security.overrated"
+ }
+ }
+ ],
+
+ "call": [
+ {
+ "fremovexattr": {
+ "fd": { "get": "fd" },
+ "name": "security.overrated"
+ },
+ "ret": "rc"
+ }
+ ],
+
+ "return": { "value": "rc", "error": "rc" }
+ },
+
+ {
+ "match": [
+ {
+ "open_by_handle_at": {
+ "mount": "/",
+ "handle": {
+ "handle_bytes": { "set": "len" },
+ "f_handle": { "set": "handle" }
+ },
+ "flags": { "set": "flags" }
+ }
+ }
+ ],
+
+ "call": [
+ {
+ "open": {
+ "path": "/",
+ "flags": "rdonly",
+ "mode": "S_IRUSR"
+ },
+ "ret": "fdmount"
+ },
+ {
+ "open_by_handle_at": {
+ "mount": { "get": "fdmount" },
+ "handle": {
+ "handle_type": "ino32_gen",
+ "handle_bytes": { "get": "len" },
+ "f_handle": { "get": "handle" }
+ },
+ "flags": { "get": "flags" }
+ },
+ "ret": "new_fd"
+ }
+ ],
+
+ "fd": { "src": { "get": "new_fd" }, "close_on_exec": false, "return": true }
+ }
+]
diff --git a/operations.c b/operations.c
index 507a2f7..4438879 100644
--- a/operations.c
+++ b/operations.c
@@ -18,6 +18,7 @@
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/uio.h>
@@ -160,7 +161,7 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g,
* value.
*/
if (GET_BIT(s->arg_deref, i) == 1) {
- c->args[i] = gluten_ptr(NULL, g, s->args[i]);
+ c->args[i] = gluten_ptr(&req->data, g, s->args[i]);
debug(" read pointer arg%d at offset %d", i,
s->args[i].offset);
} else if (s->args[i].type == OFFSET_METADATA) {
@@ -169,7 +170,7 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g,
debug(" read metadata value %s: %d",
metadata_type_str[s->args[i].offset], (int *)c->args[i]);
} else {
- if (gluten_read(NULL, g, &arg, s->args[i],
+ if (gluten_read(&req->data, g, &arg, s->args[i],
sizeof(arg)) == -1)
ret_err(-1, " failed reading arg %d", i);
debug(" read arg%d at offset %d v=%ld", i,
@@ -458,6 +459,8 @@ int op_iovload(const struct seccomp_notif *req, int notifier, struct gluten *g,
close(fd);
+ memset(dst + count, 0, load->size - count + load->zero_fill);
+
return 0;
}
@@ -566,12 +569,36 @@ int op_fd(const struct seccomp_notif *req, int notifier,
return 0;
}
+int op_fdget(const struct seccomp_notif *req, int notifier,
+ struct gluten *g, struct op_fdget *op)
+{
+ int pidfd, fd, n;
+
+ (void)notifier;
+
+ if (gluten_read(&req->data, g, &n, op->src, sizeof(n)) == -1)
+ return -1;
+
+ if ((pidfd = syscall(SYS_pidfd_open, req->pid, 0)) < 0)
+ die(" pidfd_open");
+
+ if ((fd = syscall(SYS_pidfd_getfd, pidfd, n, 0)) < 0)
+ die(" pidfd_getfd");
+
+ close(pidfd);
+
+ return gluten_write(g, op->dst, &fd, sizeof(fd));
+}
+
int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g,
struct op_bitwise *op)
{
const struct bitwise_desc *desc = gluten_ptr(&req->data, g, op->desc);
- const unsigned char *x, *y;
- unsigned char *dst;
+ const struct vec_desc vec = desc->vec;
+ const unsigned char *x_ptr, *y_ptr;
+ struct gluten_offset x, dst;
+ unsigned char *dst_ptr;
+ uint32_t *vlen;
unsigned i;
(void)notifier;
@@ -579,10 +606,6 @@ int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g,
if (!desc)
return -1;
- dst = gluten_write_ptr( g, desc->dst);
- x = gluten_ptr(&req->data, g, desc->x);
- y = gluten_ptr(&req->data, g, desc->y);
-
/*
if (!dst || !src || !mask ||
!check_gluten_limits(desc->dst, desc->size) ||
@@ -590,6 +613,7 @@ int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g,
!check_gluten_limits(desc->mask, desc->size))
return -1;
*/
+
debug(" op_bitwise: dst=(%s %d) := x=(%s %d) %s y=(%s %d) size=%d",
gluten_offset_name[desc->dst.type], desc->dst.offset,
gluten_offset_name[desc->x.type], desc->x.offset,
@@ -597,13 +621,37 @@ int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g,
gluten_offset_name[desc->y.type], desc->y.offset,
desc->size);
- for (i = 0; i < desc->size; i++) {
- if (desc->type == BITWISE_AND)
- dst[i] = x[i] & y[i];
- else if (desc->type == BITWISE_OR)
- dst[i] = x[i] | y[i];
- else
- return -1;
+ if (vec.start.type != OFFSET_NULL) {
+ debug(" vector start=(%s %d), length offset: %i",
+ gluten_offset_name[vec.start.type], vec.start.offset,
+ vec.len_offset);
+ }
+
+ if (vec.start.type == OFFSET_NULL)
+ vlen = &((uint32_t){ 1 });
+ else
+ vlen = (uint32_t *)gluten_ptr(&req->data, g, vec.start);
+
+ y_ptr = gluten_ptr(&req->data, g, desc->y);
+
+ for (x = desc->x, dst = desc->dst;
+ *vlen;
+ x.offset += *vlen, vlen += *vlen, dst.offset += desc->size) {
+
+ x_ptr = gluten_ptr(&req->data, g, x);
+ dst_ptr = gluten_write_ptr( g, dst);
+
+ for (i = 0; i < desc->size; i++) {
+ if (desc->type == BITWISE_AND)
+ dst_ptr[i] = x_ptr[i] & y_ptr[i];
+ else if (desc->type == BITWISE_OR)
+ dst_ptr[i] = x_ptr[i] | y_ptr[i];
+ else
+ return -1;
+ }
+
+ if (vec.start.type == OFFSET_NULL)
+ break;
}
return 0;
@@ -661,9 +709,13 @@ int op_cmp(const struct seccomp_notif *req, int notifier, struct gluten *g,
struct op_cmp *op)
{
const struct cmp_desc *desc = gluten_ptr(&req->data, g, op->desc);
+ bool some = false, all = true, verdict;
+ const struct vec_desc vec = desc->vec;
char str_x[PATH_MAX], str_y[PATH_MAX];
+ const void *x_ptr, *y_ptr;
+ struct gluten_offset x;
enum op_cmp_type cmp;
- const void *px, *py;
+ uint32_t *vlen;
int res;
(void)notifier;
@@ -671,29 +723,88 @@ int op_cmp(const struct seccomp_notif *req, int notifier, struct gluten *g,
if (!desc)
return -1;
- px = gluten_ptr(&req->data, g, desc->x);
- py = gluten_ptr(&req->data, g, desc->y);
- cmp = desc->cmp;
-
- if (!px || !py ||
- !check_gluten_limits(desc->x, desc->size) ||
- !check_gluten_limits(desc->y, desc->size))
- return -1;
str_offset_value(req, g, &desc->x, desc->size, str_x, PATH_MAX);
str_offset_value(req, g, &desc->y, desc->size, str_y, PATH_MAX);
debug(" op_cmp: operands x=%s y=%s", str_x, str_y);
- res = memcmp(px, py, desc->size);
+ if (vec.start.type != OFFSET_NULL) {
+ debug(" vector start=(%s %d), length offset: %i",
+ gluten_offset_name[vec.start.type], vec.start.offset,
+ vec.len_offset);
+ }
+
+ if (vec.start.type == OFFSET_NULL)
+ vlen = &((uint32_t){ 1 });
+ else
+ vlen = (uint32_t *)gluten_ptr(&req->data, g, vec.start);
+
+ y_ptr = gluten_ptr(&req->data, g, desc->y);
+
+ if (!y_ptr || !check_gluten_limits(desc->y, desc->size))
+ return -1;
+
+ cmp = desc->cmp;
+
+ for (x = desc->x;
+ *vlen;
+ x.offset += *vlen, vlen = (uint32_t *)((uint8_t *)vlen + *vlen)) {
+ unsigned char *c;
+ int __i;
+
+ debug(" in loop, vlen: %u", *vlen);
+
+ x_ptr = gluten_ptr(&req->data, g, x);
+
+ if (!x_ptr || !check_gluten_limits(x, desc->size))
+ return -1;
+
+ debug(" in loop #2");
+
+ res = memcmp(x_ptr, y_ptr, desc->size);
+
+ debug("=== x: %04x, y: %04x", *(uint16_t *)x_ptr, *(uint16_t *)y_ptr);
- if ((res == 0 && (cmp == CMP_EQ || cmp == CMP_LE || cmp == CMP_GE)) ||
- (res < 0 && (cmp == CMP_LT || cmp == CMP_LE)) ||
- (res > 0 && (cmp == CMP_GT || cmp == CMP_GE)) ||
- (res != 0 && (cmp == CMP_NE))) {
- debug(" op_cmp: successful comparison, jump to %d",
- desc->jmp.offset);
+ c = (unsigned char *)x_ptr;
+ for (__i = 0; __i < 32; __i += 4)
+ debug("%02x %02x %02x %02x", c[__i], c[__i + 1], c[__i + 2], c[__i + 3]);
+
+ if ((res == 0 &&
+ (cmp == CMP_EQ || cmp == CMP_LE || cmp == CMP_GE)) ||
+ (res < 0 &&
+ (cmp == CMP_LT || cmp == CMP_LE)) ||
+ (res > 0 &&
+ (cmp == CMP_GT || cmp == CMP_GE)) ||
+ (res != 0 &&
+ (cmp == CMP_NE)))
+ some = true;
+ else
+ all = false;
+
+ if (vec.start.type == OFFSET_NULL)
+ break;
+
+ debug(" in loop #3");
+ }
+
+ /* FIXME: vectors always imply the existential quantifier for now:
+ * CMP_NE and all non-equal: jump
+ * CMP_EQ and some equal: jump
+ */
+ if (vec.start.type == OFFSET_NULL)
+ verdict = some;
+ else if (cmp == CMP_NE)
+ verdict = all;
+ else
+ verdict = some;
+
+ debug(" op_cmp: comparison: %s for some, %s for all, verdict: %s",
+ some ? "true" : "false", all ? "true" : "false",
+ verdict ? "true" : "false");
+
+ if (verdict) {
+ debug(" -> jump to %d", desc->jmp.offset);
return desc->jmp.offset;
}
- debug(" op_cmp: comparison is false");
return 0;
}
@@ -702,13 +813,14 @@ int op_resolve_fd(const struct seccomp_notif *req, int notifier,
struct gluten *g, struct op_resolvefd *op)
{
const struct resolvefd_desc *desc = gluten_ptr(&req->data, g, op->desc);
- char fdpath[PATH_MAX], buf[PATH_MAX];
+ char fdpath[PATH_MAX], buf[BUFSIZ], *p = NULL;
ssize_t nbytes;
int fd;
(void)notifier;
- debug(" op_resolvefd: fd=(%s %d) path=(%s %d) path_max=%d",
+ debug(" op_resolvefd: %s fd=(%s %d) path=(%s %d) path_max=%d",
+ resolvefd_type_str[desc->type],
gluten_offset_name[desc->fd.type], desc->fd.offset,
gluten_offset_name[desc->path.type], desc->path.offset,
desc->path_max);
@@ -716,12 +828,51 @@ int op_resolve_fd(const struct seccomp_notif *req, int notifier,
if (gluten_read(&req->data, g, &fd, desc->fd, sizeof(fd)) == -1)
return -1;
- snprintf(fdpath, PATH_MAX, "/proc/%d/fd/%d", req->pid, fd);
- if ((nbytes = readlink(fdpath, buf, desc->path_max)) < 0)
- ret_err(-1, "error reading %s", buf);
+ if (desc->type == RESOLVEFD_PATH) {
+ snprintf(fdpath, PATH_MAX, "/proc/%d/fd/%d", req->pid, fd);
+ if ((nbytes = readlink(fdpath, buf, desc->path_max)) < 0)
+ ret_err(-1, "error reading %s", buf);
+
+ p = buf;
+ } else if (desc->type == RESOLVEFD_MOUNT) {
+ FILE *fp;
+
+ snprintf(fdpath, PATH_MAX, "/proc/%d/mountinfo", req->pid);
+ if (!(fp = fopen(fdpath, "r"))) {
+ err("Couldn't open %s", fdpath);
+ return -1;
+ }
+
+ while (fgets(buf, BUFSIZ, fp)) {
+ long n;
+
+ errno = 0;
+ n = strtoul(buf, NULL, 0);
+ if (errno) {
+ fclose(fp);
+ return -errno;
+ }
+
+ if (n == fd) {
+ int i;
+
+ for (i = 0; i < 5; i++)
+ p = strtok(i ? NULL : buf, " ");
+
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ if (!p) {
+ err("No mountpoint for mount_fd %i", fd);
+ return -1;
+ }
+ }
- debug(" op_resolvefd: fd %d -> path: %s", fd, buf);
- gluten_write(g, desc->path, &buf, desc->path_max);
+ debug(" op_resolvefd: fd %d -> path: %s", fd, p);
+ gluten_write(g, desc->path, p, desc->path_max);
return 0;
}
@@ -764,12 +915,13 @@ int eval(struct gluten *g, const struct seccomp_notif *req,
HANDLE_OP(OP_CALL, op_call, call, g);
HANDLE_OP(OP_RETURN, op_return, ret, g);
HANDLE_OP(OP_FD, op_fd, fd, g);
+ HANDLE_OP(OP_FDGET, op_fdget, fdget, g);
HANDLE_OP(OP_LOAD, op_load, load, g);
HANDLE_OP(OP_STORE, op_store, store, g);
HANDLE_OP(OP_IOVLOAD, op_iovload, iovload, g);
HANDLE_OP(OP_BITWISE, op_bitwise, bitwise, g);
HANDLE_OP(OP_CMP, op_cmp, cmp, g);
- HANDLE_OP(OP_RESOLVEDFD, op_resolve_fd, resfd, g);
+ HANDLE_OP(OP_RESOLVEFD, op_resolve_fd, resfd, g);
HANDLE_OP(OP_NR, op_nr, nr, g);
HANDLE_OP(OP_COPY, op_copy, copy, g);
default:
diff --git a/operations.h b/operations.h
index 6cff7a8..4c827f9 100644
--- a/operations.h
+++ b/operations.h
@@ -61,4 +61,6 @@ int op_copy(const struct seccomp_notif *req, int notifier, struct gluten *g,
struct op_copy *op);
int op_fd(const struct seccomp_notif *req, int notifier, struct gluten *g,
struct op_fd *op);
+int op_fdget(const struct seccomp_notif *req, int notifier, struct gluten *g,
+ struct op_fdget *op);
#endif /* ACTIONS_H */