From: Daan De Meyer Date: Fri, 28 Nov 2025 19:28:01 +0000 (+0100) Subject: namespace: Clone root dir descriptor before use X-Git-Tag: v259~30^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5eb0639faac128ecde6cd0bfc9b484efddfdc49c;p=thirdparty%2Fsystemd.git namespace: Clone root dir descriptor before use Before doing anything with the root directory file descriptor, let's make sure we clone it first so that the caller can't mess with mount fd attributes via mount_setattr() anymore. We clone during parsing instead of in executor so that the caller can't mess with the mount fd between invocations. --- diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index b6632e56cbe..74a294e4945 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -502,16 +502,13 @@ static int bus_set_transient_exec_context_fd( assert(name); assert(p); assert(b); - assert(verify_mode == O_DIRECTORY || (verify_mode & ~O_ACCMODE_STRICT) == 0); + assert((verify_mode & ~O_ACCMODE_STRICT) == 0); r = sd_bus_message_read(message, "h", &fd); if (r < 0) return r; - if (verify_mode == O_DIRECTORY) - r = fd_verify_directory(fd); - else - r = fd_vet_accmode(fd, verify_mode); + r = fd_vet_accmode(fd, verify_mode); if (r < 0) return sd_bus_error_set_errnof(reterr_error, r, "%s passed is of incompatible type: %m", name); @@ -813,8 +810,34 @@ static int bus_service_set_transient_property( return 1; } - if (streq(name, "RootDirectoryFileDescriptor")) - return bus_set_transient_exec_context_fd(u, name, &s->root_directory_fd, &s->exec_context.root_directory_as_fd, O_DIRECTORY, message, flags, reterr_error); + if (streq(name, "RootDirectoryFileDescriptor")) { + int fd; + + r = sd_bus_message_read(message, "h", &fd); + if (r < 0) + return r; + + r = fd_verify_directory(fd); + if (r < 0) + return sd_bus_error_set_errnof(reterr_error, r, "RootDirectoryFileDescriptor= is not a directory: %m"); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + int fd_clone; + + /* Note that this invalidates the fd we got from the client. They won't be able to + * move_mount() it themselves. If they already move_mount()'ed it themselves, this + * will fail to clone the fd. */ + fd_clone = mount_fd_clone(fd, /* recursive= */ true, /* replacement_fd= */ NULL); + if (fd_clone < 0) + return fd_clone; + + /* We're closing our own clone here, which shouldn't need an asynchronous_close(). */ + close_and_replace(s->root_directory_fd, fd_clone); + s->exec_context.root_directory_as_fd = true; + } + + return 1; + } return 0; } diff --git a/src/core/execute.c b/src/core/execute.c index 08210edb1e8..116496b91b8 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2877,6 +2877,7 @@ void exec_params_shallow_clear(ExecParameters *p) { p->fd_names = strv_free(p->fd_names); p->files_env = strv_free(p->files_env); p->fds = mfree(p->fds); + p->root_directory_fd = safe_close(p->root_directory_fd); p->exec_fd = safe_close(p->exec_fd); p->user_lookup_fd = -EBADF; p->bpf_restrict_fs_map_fd = -EBADF; diff --git a/src/core/service.c b/src/core/service.c index 3f555912ca0..1ca676329a9 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1951,7 +1951,14 @@ static int service_spawn_internal( exec_params.stdin_fd = s->stdin_fd; exec_params.stdout_fd = s->stdout_fd; exec_params.stderr_fd = s->stderr_fd; - exec_params.root_directory_fd = s->root_directory_fd; + + if (s->root_directory_fd >= 0) { + r = mount_fd_clone(s->root_directory_fd, /* recursive= */ true, &s->root_directory_fd); + if (r < 0) + return r; + + exec_params.root_directory_fd = r; + } r = exec_spawn(UNIT(s), c,