From 4e02162a6e88a38e509e0646d6d39f14a8e14490 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 22 May 2026 08:49:27 -0700 Subject: [PATCH] core/exec-invoke: chdir("/") after chroot in apply_root_directory apply_root_directory() calls chroot() but never pairs it with a chdir("/"). Moving cwd into the new root is left to the subsequent apply_working_directory() call. That delegation breaks when the unit sets WorkingDirectory=-/path (working_directory_missing_ok): apply_root_directory() apply_working_directory() chroot(new_root) --> chdir(wd) fails (ENOENT) return 0 return 0 /* missing_ok */ chroot(2) changes only the process root, not cwd. With cwd still resolving to the pre-chroot host dentry, the process runs with the kernel root pointing at the new root while relative-path accesses and /proc/self/cwd reach paths outside RootDirectory=. Only the chroot-only branch (exec_needs_mount_namespace() == false) is affected. The mount-namespace branch uses pivot_root(), which restructures the mount tree and makes the pre-chroot dentry unreachable. Fix by issuing chdir("/") immediately after the successful chroot() in apply_root_directory() and propagating any failure with EXIT_CHROOT, matching the standard chroot+chdir idiom and making the chroot self-contained regardless of how apply_working_directory() later handles a missing directory. Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason --- src/core/exec-invoke.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 4f935c1439..183442aef3 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -4133,12 +4133,22 @@ static int apply_root_directory( assert(exit_status); if (params->flags & EXEC_APPLY_CHROOT) - if (!needs_mount_ns && context->root_directory) + if (!needs_mount_ns && context->root_directory) { if (chroot(runtime->ephemeral_copy ?: context->root_directory) < 0) { *exit_status = EXIT_CHROOT; return -errno; } + /* chroot(2) changes only the process root, not cwd. Move cwd into the new root + * immediately so that a subsequent apply_working_directory() failure that is + * silently ignored via working_directory_missing_ok cannot leave cwd pointing + * at a pre-chroot dentry outside the new root. */ + if (chdir("/") < 0) { + *exit_status = EXIT_CHROOT; + return -errno; + } + } + return 0; }