]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
namespace: Clone root dir descriptor before use
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 28 Nov 2025 19:28:01 +0000 (20:28 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Sun, 14 Dec 2025 10:32:06 +0000 (11:32 +0100)
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.

src/core/dbus-service.c
src/core/execute.c
src/core/service.c

index b6632e56cbe4c5d9ba4ea3fb9903999e464703b4..74a294e4945f98bcaf3408e07905809a7a25f696 100644 (file)
@@ -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;
 }
index 08210edb1e870c60e8846e64f3b86fb6140fe6ff..116496b91b8845aa2567df7f01cc27eb4b4571d2 100644 (file)
@@ -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;
index 3f555912ca0e1f2a84d13eb764edcb61371edcf2..1ca676329a910462aa87ec73f70057724f364bc2 100644 (file)
@@ -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,