aboutgitcodelistschat:MatrixIRC
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
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>
-rw-r--r--Makefile7
-rw-r--r--common/gluten.h34
-rw-r--r--common/util.c4
-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
-rw-r--r--demo/Makefile9
-rw-r--r--demo/fremovexattr.c11
-rw-r--r--demo/fsetxattr.c11
-rw-r--r--demo/open_by_handle_at.c22
-rw-r--r--demo/virtiofsd.hjson93
-rw-r--r--operations.c83
-rw-r--r--operations.h2
18 files changed, 524 insertions, 31 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..d06362a 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;
@@ -261,14 +267,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 +299,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..3164126 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;
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;
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/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..eb8d614 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,
@@ -566,6 +567,27 @@ 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)
{
@@ -702,13 +724,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 +739,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 +826,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 */