]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
exec-invoke: don't double-close FDs on error
authorLuca Boccassi <bluca@debian.org>
Fri, 27 Oct 2023 15:33:49 +0000 (16:33 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 28 Oct 2023 14:56:25 +0000 (16:56 +0200)
When a late error occurs in sd-executor, the cleanup-on-close of the
context structs happen, but at that time all FDs might have already
been closed via close_all_fds(), so a double-close happens. This
can be seen when DynamicUser is enabled, with a non-existing
WorkingDirectory.

Invalidate the FDs in the context structs if close_all_fds succeeds.

src/core/dynamic-user.c
src/core/dynamic-user.h
src/core/exec-invoke.c
test/units/testsuite-07.exec-context.sh

index 76842e5f24cc35b5832e6d5c39757f959c0a409d..12724c682c02f3d5573f537fb1393e627c2e133b 100644 (file)
@@ -858,3 +858,14 @@ void dynamic_creds_done(DynamicCreds *creds) {
                 dynamic_user_free(creds->group);
         creds->group = creds->user = dynamic_user_free(creds->user);
 }
+
+void dynamic_creds_close(DynamicCreds *creds) {
+        if (!creds)
+                return;
+
+        if (creds->user)
+                safe_close_pair(creds->user->storage_socket);
+
+        if (creds->group && creds->group != creds->user)
+                safe_close_pair(creds->group->storage_socket);
+}
index e86ee02796b1282f10f247db08ffa1952cf4ace6..303a7d081c912b75e44b80622bdcdf280e5e6036 100644 (file)
@@ -41,6 +41,7 @@ int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *ui
 DynamicCreds *dynamic_creds_unref(DynamicCreds *creds);
 DynamicCreds *dynamic_creds_destroy(DynamicCreds *creds);
 void dynamic_creds_done(DynamicCreds *creds);
+void dynamic_creds_close(DynamicCreds *creds);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DynamicCreds*, dynamic_creds_unref);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DynamicCreds*, dynamic_creds_destroy);
index 9be39a561088bc52997f22a4b8ce75a62cb9e6f6..886e087bf0d641724e1a4f2c9d933a4e6d2a81b7 100644 (file)
@@ -3862,6 +3862,33 @@ static int exec_context_named_iofds(
         return targets == 0 ? 0 : -ENOENT;
 }
 
+static void exec_shared_runtime_close(ExecSharedRuntime *shared) {
+        if (!shared)
+                return;
+
+        safe_close_pair(shared->netns_storage_socket);
+        safe_close_pair(shared->ipcns_storage_socket);
+}
+
+static void exec_runtime_close(ExecRuntime *rt) {
+        if (!rt)
+                return;
+
+        safe_close_pair(rt->ephemeral_storage_socket);
+
+        exec_shared_runtime_close(rt->shared);
+        dynamic_creds_close(rt->dynamic_creds);
+}
+
+static void exec_params_close(ExecParameters *p) {
+        if (!p)
+                return;
+
+        p->stdin_fd = safe_close(p->stdin_fd);
+        p->stdout_fd = safe_close(p->stdout_fd);
+        p->stderr_fd = safe_close(p->stderr_fd);
+}
+
 int exec_invoke(
                 const ExecCommand *command,
                 const ExecContext *context,
@@ -4734,7 +4761,10 @@ int exec_invoke(
         /* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that
          * we are more aggressive this time, since we don't need socket_fd and the netns and ipcns fds any
          * more. We do keep exec_fd however, if we have it, since we need to keep it open until the final
-         * execve(). */
+         * execve(). But first, close the remaining sockets in the context objects. */
+
+        exec_runtime_close(runtime);
+        exec_params_close(params);
 
         r = close_all_fds(keep_fds, n_keep_fds);
         if (r >= 0)
index 477b9e84dac4848b1e8d5fb40c4585b097299c33..0943300424db860c0391eb4a0ccad9b25fe6dc98 100755 (executable)
@@ -186,3 +186,6 @@ systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/u
     bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
 systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
     bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
+
+# Ensure that clean-up codepaths work correctly if activation ultimately fails
+(! systemd-run --wait --pipe -p DynamicUser=yes -p WorkingDirectory=/nonexistent echo hello)