]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
coredump: hand a pidfd to the usermode coredump helper
authorChristian Brauner <brauner@kernel.org>
Mon, 14 Apr 2025 13:55:07 +0000 (15:55 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Jun 2025 12:45:09 +0000 (14:45 +0200)
commit b5325b2a270fcaf7b2a9a0f23d422ca8a5a8bdea upstream.

Give userspace a way to instruct the kernel to install a pidfd into the
usermode helper process. This makes coredump handling a lot more
reliable for userspace. In parallel with this commit we already have
systemd adding support for this in [1].

We create a pidfs file for the coredumping process when we process the
corename pattern. When the usermode helper process is forked we then
install the pidfs file as file descriptor three into the usermode
helpers file descriptor table so it's available to the exec'd program.

Since usermode helpers are either children of the system_unbound_wq
workqueue or kthreadd we know that the file descriptor table is empty
and can thus always use three as the file descriptor number.

Note, that we'll install a pidfd for the thread-group leader even if a
subthread is calling do_coredump(). We know that task linkage hasn't
been removed due to delay_group_leader() and even if this @current isn't
the actual thread-group leader we know that the thread-group leader
cannot be reaped until @current has exited.

[brauner: This is a backport for the v6.12 series. The upstream kernel
has changed pidfs_alloc_file() to set O_RDWR implicitly instead of
forcing callers to set it. Let's minimize the churn and just let the
coredump umh handler raise O_RDWR.]

Link: https://github.com/systemd/systemd/pull/37125
Link: https://lore.kernel.org/20250414-work-coredump-v2-3-685bf231f828@kernel.org
Tested-by: Luca Boccassi <luca.boccassi@gmail.com>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/coredump.c
include/linux/coredump.h

index e2a9234473766c4d3ca96a9d69d316967c724d8d..d56f5421e62c6e6a6c768daa00059d1f2ee87eb9 100644 (file)
@@ -43,6 +43,8 @@
 #include <linux/timekeeping.h>
 #include <linux/sysctl.h>
 #include <linux/elf.h>
+#include <linux/pidfs.h>
+#include <uapi/linux/pidfd.h>
 
 #include <linux/uaccess.h>
 #include <asm/mmu_context.h>
@@ -60,6 +62,12 @@ static void free_vma_snapshot(struct coredump_params *cprm);
 #define CORE_FILE_NOTE_SIZE_DEFAULT (4*1024*1024)
 /* Define a reasonable max cap */
 #define CORE_FILE_NOTE_SIZE_MAX (16*1024*1024)
+/*
+ * File descriptor number for the pidfd for the thread-group leader of
+ * the coredumping task installed into the usermode helper's file
+ * descriptor table.
+ */
+#define COREDUMP_PIDFD_NUMBER 3
 
 static int core_uses_pid;
 static unsigned int core_pipe_limit;
@@ -339,6 +347,27 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
                        case 'C':
                                err = cn_printf(cn, "%d", cprm->cpu);
                                break;
+                       /* pidfd number */
+                       case 'F': {
+                               /*
+                                * Installing a pidfd only makes sense if
+                                * we actually spawn a usermode helper.
+                                */
+                               if (!ispipe)
+                                       break;
+
+                               /*
+                                * Note that we'll install a pidfd for the
+                                * thread-group leader. We know that task
+                                * linkage hasn't been removed yet and even if
+                                * this @current isn't the actual thread-group
+                                * leader we know that the thread-group leader
+                                * cannot be reaped until @current has exited.
+                                */
+                               cprm->pid = task_tgid(current);
+                               err = cn_printf(cn, "%d", COREDUMP_PIDFD_NUMBER);
+                               break;
+                       }
                        default:
                                break;
                        }
@@ -493,7 +522,7 @@ static void wait_for_dump_helpers(struct file *file)
 }
 
 /*
- * umh_pipe_setup
+ * umh_coredump_setup
  * helper function to customize the process used
  * to collect the core in userspace.  Specifically
  * it sets up a pipe and installs it as fd 0 (stdin)
@@ -503,12 +532,31 @@ static void wait_for_dump_helpers(struct file *file)
  * is a special value that we use to trap recursive
  * core dumps
  */
-static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
+static int umh_coredump_setup(struct subprocess_info *info, struct cred *new)
 {
        struct file *files[2];
        struct coredump_params *cp = (struct coredump_params *)info->data;
        int err;
 
+       if (cp->pid) {
+               struct file *pidfs_file __free(fput) = NULL;
+
+               pidfs_file = pidfs_alloc_file(cp->pid, O_RDWR);
+               if (IS_ERR(pidfs_file))
+                       return PTR_ERR(pidfs_file);
+
+               /*
+                * Usermode helpers are childen of either
+                * system_unbound_wq or of kthreadd. So we know that
+                * we're starting off with a clean file descriptor
+                * table. So we should always be able to use
+                * COREDUMP_PIDFD_NUMBER as our file descriptor value.
+                */
+               err = replace_fd(COREDUMP_PIDFD_NUMBER, pidfs_file, 0);
+               if (err < 0)
+                       return err;
+       }
+
        err = create_pipe_files(files, 0);
        if (err)
                return err;
@@ -598,7 +646,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
                }
 
                if (cprm.limit == 1) {
-                       /* See umh_pipe_setup() which sets RLIMIT_CORE = 1.
+                       /* See umh_coredump_setup() which sets RLIMIT_CORE = 1.
                         *
                         * Normally core limits are irrelevant to pipes, since
                         * we're not writing to the file system, but we use
@@ -637,7 +685,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
                retval = -ENOMEM;
                sub_info = call_usermodehelper_setup(helper_argv[0],
                                                helper_argv, NULL, GFP_KERNEL,
-                                               umh_pipe_setup, NULL, &cprm);
+                                               umh_coredump_setup, NULL, &cprm);
                if (sub_info)
                        retval = call_usermodehelper_exec(sub_info,
                                                          UMH_WAIT_EXEC);
index 77e6e195d1d687aca412dd1d9b56ebd3c15139af..76e41805b92de9a92c8288f00c0fb7251e9be37a 100644 (file)
@@ -28,6 +28,7 @@ struct coredump_params {
        int vma_count;
        size_t vma_data_size;
        struct core_vma_metadata *vma_meta;
+       struct pid *pid;
 };
 
 extern unsigned int core_file_note_size_limit;