From 5a9302bab9c9bb3d1577f04678d074fb7af4115f Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Wed, 15 May 2024 08:49:56 +0200 Subject: 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 --- cooker/call.c | 10 ++- cooker/calls/fs.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ cooker/cooker.h | 5 ++ cooker/emit.c | 49 ++++++++++++-- cooker/emit.h | 8 ++- cooker/filter.c | 12 ++++ cooker/gluten.c | 2 + cooker/match.c | 3 +- 8 files changed, 269 insertions(+), 10 deletions(-) (limited to 'cooker') 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 +#include #include #include @@ -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 }; @@ -76,6 +77,37 @@ void emit_fd(struct gluten_ctx *g, struct fd_desc *desc) die("Too many instructions"); } +/** + * 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 @@ -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; -- cgit v1.2.3