aboutgitcodelistschat:MatrixIRC
path: root/cooker
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2024-05-15 08:49:56 +0200
committerStefano Brivio <sbrivio@redhat.com>2024-05-15 08:49:56 +0200
commit5a9302bab9c9bb3d1577f04678d074fb7af4115f (patch)
tree21d04573dfa733e020315d08853c00fc119fb959 /cooker
parentbdbec30a849807fb5e6841a38cfe0d168e5962b9 (diff)
downloadseitan-5a9302bab9c9bb3d1577f04678d074fb7af4115f.tar
seitan-5a9302bab9c9bb3d1577f04678d074fb7af4115f.tar.gz
seitan-5a9302bab9c9bb3d1577f04678d074fb7af4115f.tar.bz2
seitan-5a9302bab9c9bb3d1577f04678d074fb7af4115f.tar.lz
seitan-5a9302bab9c9bb3d1577f04678d074fb7af4115f.tar.xz
seitan-5a9302bab9c9bb3d1577f04678d074fb7af4115f.tar.zst
seitan-5a9302bab9c9bb3d1577f04678d074fb7af4115f.zip
Add fsetxattr(), fremovexattr(), open_by_handle_at(), and "virtiofsd demo"
Mostly assorted fixes, a new FDGET operation (get a copy of the target file descriptor via pidfd_getfd()) and a new "FD" flag that means we have to do that on direct tag reference. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Diffstat (limited to 'cooker')
-rw-r--r--cooker/call.c10
-rw-r--r--cooker/calls/fs.c190
-rw-r--r--cooker/cooker.h5
-rw-r--r--cooker/emit.c49
-rw-r--r--cooker/emit.h8
-rw-r--r--cooker/filter.c12
-rw-r--r--cooker/gluten.c2
-rw-r--r--cooker/match.c3
8 files changed, 269 insertions, 10 deletions
diff --git a/cooker/call.c b/cooker/call.c
index dd37fe9..dbdc52b 100644
--- a/cooker/call.c
+++ b/cooker/call.c
@@ -72,7 +72,9 @@ 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,
@@ -374,6 +376,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 +436,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 +463,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)
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/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..33355b6 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
@@ -218,19 +250,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 +272,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);
diff --git a/cooker/emit.h b/cooker/emit.h
index 5557187..abdeda9 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);
@@ -38,8 +41,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..1fd726f 100644
--- a/cooker/match.c
+++ b/cooker/match.c
@@ -321,12 +321,13 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
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_resolvefd(g, f->type, seccomp_offset, offset, size);
emit_cmp(g, CMP_NE, offset, const_offset, size,
JUMP_NEXT_BLOCK);
break;