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 --- Makefile | 7 +- common/gluten.h | 34 ++++++--- common/util.c | 4 +- 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 +- demo/Makefile | 9 +++ demo/fremovexattr.c | 11 +++ demo/fsetxattr.c | 11 +++ demo/open_by_handle_at.c | 22 ++++++ demo/virtiofsd.hjson | 93 +++++++++++++++++++++++ operations.c | 83 ++++++++++++++++++--- operations.h | 2 + 18 files changed, 524 insertions(+), 31 deletions(-) create mode 100644 demo/Makefile create mode 100644 demo/fremovexattr.c create mode 100644 demo/fsetxattr.c create mode 100644 demo/open_by_handle_at.c create mode 100644 demo/virtiofsd.hjson 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 +#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; 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 +#include +#include +#include + +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 +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +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 #include #include +#include #include #include #include @@ -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 */ -- cgit v1.2.3