History

Merge tag 'vfs-6.16-rc1.coredump' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull coredump updates from Christian Brauner: "This adds support for sending coredumps over an AF_UNIX socket. It also makes (implicit) use of the new SO_PEERPIDFD ability to hand out pidfds for reaped peer tasks The new coredump socket will allow userspace to not have to rely on usermode helpers for processing coredumps and provides a saf way to handle them instead of relying on super privileged coredumping helpers This will also be significantly more lightweight since the kernel doens't have to do a fork()+exec() for each crashing process to spawn a usermodehelper. Instead the kernel just connects to the AF_UNIX socket and userspace can process it concurrently however it sees fit. Support for userspace is incoming starting with systemd-coredump There's more work coming in that direction next cycle. The rest below goes into some details and background Coredumping currently supports two modes: (1) Dumping directly into a file somewhere on the filesystem. (2) Dumping into a pipe connected to a usermode helper process spawned as a child of the system_unbound_wq or kthreadd For simplicity I'm mostly ignoring (1). There's probably still some users of (1) out there but processing coredumps in this way can be considered adventurous especially in the face of set*id binaries The most common option should be (2) by now. It works by allowing userspace to put a string into /proc/sys/kernel/core_pattern like: |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h The "|" at the beginning indicates to the kernel that a pipe must be used. The path following the pipe indicator is a path to a binary that will be spawned as a usermode helper process. Any additional parameters pass information about the task that is generating the coredump to the binary that processes the coredump In the example the core_pattern shown causes the kernel to spawn systemd-coredump as a usermode helper. There's various conceptual consequences of this (non-exhaustive list): - systemd-coredump is spawned with file descriptor number 0 (stdin) connected to the read-end of the pipe. All other file descriptors are closed. That specifically includes 1 (stdout) and 2 (stderr). This has already caused bugs because userspace assumed that this cannot happen (Whether or not this is a sane assumption is irrelevant) - systemd-coredump will be spawned as a child of system_unbound_wq. So it is not a child of any userspace process and specifically not a child of PID 1. It cannot be waited upon and is in a weird hybrid upcall which are difficult for userspace to control correctly - systemd-coredump is spawned with full kernel privileges. This necessitates all kinds of weird privilege dropping excercises in userspace to make this safe - A new usermode helper has to be spawned for each crashing process This adds a new mode: (3) Dumping into an AF_UNIX socket Userspace can set /proc/sys/kernel/core_pattern to: @/path/to/coredump.socket The "@" at the beginning indicates to the kernel that an AF_UNIX coredump socket will be used to process coredumps The coredump socket must be located in the initial mount namespace. When a task coredumps it opens a client socket in the initial network namespace and connects to the coredump socket: - The coredump server uses SO_PEERPIDFD to get a stable handle on the connected crashing task. The retrieved pidfd will provide a stable reference even if the crashing task gets SIGKILLed while generating the coredump. That is a huge attack vector right now - By setting core_pipe_limit non-zero userspace can guarantee that the crashing task cannot be reaped behind it's back and thus process all necessary information in /proc/<pid>. The SO_PEERPIDFD can be used to detect whether /proc/<pid> still refers to the same process The core_pipe_limit isn't used to rate-limit connections to the socket. This can simply be done via AF_UNIX socket directly - The pidfd for the crashing task will contain information how the task coredumps. The PIDFD_GET_INFO ioctl gained a new flag PIDFD_INFO_COREDUMP which can be used to retreive the coredump information If the coredump gets a new coredump client connection the kernel guarantees that PIDFD_INFO_COREDUMP information is available. Currently the following information is provided in the new @coredump_mask extension to struct pidfd_info: * PIDFD_COREDUMPED is raised if the task did actually coredump * PIDFD_COREDUMP_SKIP is raised if the task skipped coredumping (e.g., undumpable) * PIDFD_COREDUMP_USER is raised if this is a regular coredump and doesn't need special care by the coredump server * PIDFD_COREDUMP_ROOT is raised if the generated coredump should be treated as sensitive and the coredump server should restrict access to the generated coredump to sufficiently privileged users" * tag 'vfs-6.16-rc1.coredump' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: mips, net: ensure that SOCK_COREDUMP is defined selftests/coredump: add tests for AF_UNIX coredumps selftests/pidfd: add PIDFD_INFO_COREDUMP infrastructure coredump: validate socket name as it is written coredump: show supported coredump modes pidfs, coredump: add PIDFD_INFO_COREDUMP coredump: add coredump socket coredump: reflow dump helpers a little coredump: massage do_coredump() coredump: massage format_corename()

Linus Torvalds committed 1 year ago

Commit c5bfc482 parents 7d7a103 4e83ae69 files · +917 −117Browse files

Unified · Split

9 files
  1. arch/mips/include/asm/socket.h +0 −9
  2. fs/coredump.c +310 −92
  3. fs/pidfs.c +55 −0
  4. include/linux/net.h +2 −2
  5. include/linux/pidfs.h +5 −0
  6. include/uapi/linux/pidfd.h +16 −0
  7. net/unix/af_unix.c +41 −13
  8. tools/testing/selftests/coredump/stackdump_test.c +466 −1
  9. tools/testing/selftests/pidfd/pidfd.h +22 −0
arch/mips/include/asm/socket.h+0−9
@@ -36,15 +36,6 @@ enum sock_type { · 35 unmodified lines
3636 SOCK_PACKET = 10,
3737 };
3838
39−#define SOCK_MAX (SOCK_PACKET + 1)
40−/* Mask which covers at least up to SOCK_MASK-1. The
41− * * remaining bits are used as flags. */
42−#define SOCK_TYPE_MASK 0xf
43−
44−/* Flags for socket, socketpair, paccept */
45−#define SOCK_CLOEXEC O_CLOEXEC
46−#define SOCK_NONBLOCK O_NONBLOCK
47−
4839 #define ARCH_HAS_SOCKET_TYPES 1
4940
5041 #endif /* _ASM_SOCKET_H */
fs/coredump.c+310−92
@@ -44,7 +44,13 @@ · 43 unmodified lines
4444 #include <linux/sysctl.h>
4545 #include <linux/elf.h>
4646 #include <linux/pidfs.h>
47+#include <linux/net.h>
48+#include <linux/socket.h>
49+#include <net/af_unix.h>
50+#include <net/net_namespace.h>
51+#include <net/sock.h>
4752 #include <uapi/linux/pidfd.h>
53+#include <uapi/linux/un.h>
4854
4955 #include <linux/uaccess.h>
5056 #include <asm/mmu_context.h>
@@ -76,9 +82,16 @@ static char core_pattern[CORENAME_MAX_SIZE] = "core"; · 25 unmodified lines
7682 static int core_name_size = CORENAME_MAX_SIZE;
7783 unsigned int core_file_note_size_limit = CORE_FILE_NOTE_SIZE_DEFAULT;
7884
85+enum coredump_type_t {
86+ COREDUMP_FILE = 1,
87+ COREDUMP_PIPE = 2,
88+ COREDUMP_SOCK = 3,
89+};
90+
7991 struct core_name {
8092 char *corename;
8193 int used, size;
94+ enum coredump_type_t core_type;
8295 };
8396
8497 static int expand_corename(struct core_name *cn, int size)
@@ -218,18 +231,24 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm, · 133 unmodified lines
218231 {
219232 const struct cred *cred = current_cred();
220233 const char *pat_ptr = core_pattern;
221− int ispipe = (*pat_ptr == '|');
222234 bool was_space = false;
223235 int pid_in_pattern = 0;
224236 int err = 0;
225237
226238 cn->used = 0;
227239 cn->corename = NULL;
240+ if (*pat_ptr == '|')
241+ cn->core_type = COREDUMP_PIPE;
242+ else if (*pat_ptr == '@')
243+ cn->core_type = COREDUMP_SOCK;
244+ else
245+ cn->core_type = COREDUMP_FILE;
228246 if (expand_corename(cn, core_name_size))
229247 return -ENOMEM;
230248 cn->corename[0] = '\0';
231249
232− if (ispipe) {
250+ switch (cn->core_type) {
251+ case COREDUMP_PIPE: {
233252 int argvs = sizeof(core_pattern) / 2;
234253 (*argv) = kmalloc_array(argvs, sizeof(**argv), GFP_KERNEL);
235254 if (!(*argv))
@@ -238,6 +257,45 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm, · 2 unmodified lines
238257 ++pat_ptr;
239258 if (!(*pat_ptr))
240259 return -ENOMEM;
260+ break;
261+ }
262+ case COREDUMP_SOCK: {
263+ /* skip the @ */
264+ pat_ptr++;
265+ if (!(*pat_ptr))
266+ return -ENOMEM;
267+
268+ err = cn_printf(cn, "%s", pat_ptr);
269+ if (err)
270+ return err;
271+
272+ /* Require absolute paths. */
273+ if (cn->corename[0] != '/')
274+ return -EINVAL;
275+
276+ /*
277+ * Ensure we can uses spaces to indicate additional
278+ * parameters in the future.
279+ */
280+ if (strchr(cn->corename, ' ')) {
281+ coredump_report_failure("Coredump socket may not %s contain spaces", cn->corename);
282+ return -EINVAL;
283+ }
284+
285+ /*
286+ * Currently no need to parse any other options.
287+ * Relevant information can be retrieved from the peer
288+ * pidfd retrievable via SO_PEERPIDFD by the receiver or
289+ * via /proc/<pid>, using the SO_PEERPIDFD to guard
290+ * against pid recycling when opening /proc/<pid>.
291+ */
292+ return 0;
293+ }
294+ case COREDUMP_FILE:
295+ break;
296+ default:
297+ WARN_ON_ONCE(true);
298+ return -EINVAL;
241299 }
242300
243301 /* Repeat as long as we have more pattern to process and more output
@@ -247,7 +305,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm, · 3 unmodified lines
247305 * Split on spaces before doing template expansion so that
248306 * %e and %E don't get split if they have spaces in them
249307 */
250− if (ispipe) {
308+ if (cn->core_type == COREDUMP_PIPE) {
251309 if (isspace(*pat_ptr)) {
252310 if (cn->used != 0)
253311 was_space = true;
@@ -353,7 +411,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm, · 99 unmodified lines
353411 * Installing a pidfd only makes sense if
354412 * we actually spawn a usermode helper.
355413 */
356− if (!ispipe)
414+ if (cn->core_type != COREDUMP_PIPE)
357415 break;
358416
359417 /*
@@ -384,12 +442,10 @@ out: · 24 unmodified lines
384442 * If core_pattern does not include a %p (as is the default)
385443 * and core_uses_pid is set, then .%pid will be appended to
386444 * the filename. Do not do this for piped commands. */
387− if (!ispipe && !pid_in_pattern && core_uses_pid) {
388− err = cn_printf(cn, ".%d", task_tgid_vnr(current));
389− if (err)
390− return err;
391− }
392− return ispipe;
445+ if (cn->core_type == COREDUMP_FILE && !pid_in_pattern && core_uses_pid)
446+ return cn_printf(cn, ".%d", task_tgid_vnr(current));
447+
448+ return 0;
393449 }
394450
395451 static int zap_process(struct signal_struct *signal, int exit_code)
@@ -545,6 +601,8 @@ static int umh_coredump_setup(struct subprocess_info *info, struct cred *new) · 149 unmodified lines
545601 if (IS_ERR(pidfs_file))
546602 return PTR_ERR(pidfs_file);
547603
604+ pidfs_coredump(cp);
605+
548606 /*
549607 * Usermode helpers are childen of either
550608 * system_unbound_wq or of kthreadd. So we know that
@@ -583,7 +641,6 @@ void do_coredump(const kernel_siginfo_t *siginfo) · 32 unmodified lines
583641 const struct cred *old_cred;
584642 struct cred *cred;
585643 int retval = 0;
586− int ispipe;
587644 size_t *argv = NULL;
588645 int argc = 0;
589646 /* require nonrelative corefile path and be extra careful */
@@ -632,70 +689,14 @@ void do_coredump(const kernel_siginfo_t *siginfo) · 42 unmodified lines
632689
633690 old_cred = override_creds(cred);
634691
635− ispipe = format_corename(&cn, &cprm, &argv, &argc);
636−
637− if (ispipe) {
638− int argi;
639− int dump_count;
640− char **helper_argv;
641− struct subprocess_info *sub_info;
642−
643− if (ispipe < 0) {
644− coredump_report_failure("format_corename failed, aborting core");
645− goto fail_unlock;
646− }
647−
648− if (cprm.limit == 1) {
649− /* See umh_coredump_setup() which sets RLIMIT_CORE = 1.
650− *
651− * Normally core limits are irrelevant to pipes, since
652− * we're not writing to the file system, but we use
653− * cprm.limit of 1 here as a special value, this is a
654− * consistent way to catch recursive crashes.
655− * We can still crash if the core_pattern binary sets
656− * RLIM_CORE = !1, but it runs as root, and can do
657− * lots of stupid things.
658− *
659− * Note that we use task_tgid_vnr here to grab the pid
660− * of the process group leader. That way we get the
661− * right pid if a thread in a multi-threaded
662− * core_pattern process dies.
663− */
664− coredump_report_failure("RLIMIT_CORE is set to 1, aborting core");
665− goto fail_unlock;
666− }
667− cprm.limit = RLIM_INFINITY;
668−
669− dump_count = atomic_inc_return(&core_dump_count);
670− if (core_pipe_limit && (core_pipe_limit < dump_count)) {
671− coredump_report_failure("over core_pipe_limit, skipping core dump");
672− goto fail_dropcount;
673− }
674−
675− helper_argv = kmalloc_array(argc + 1, sizeof(*helper_argv),
676− GFP_KERNEL);
677− if (!helper_argv) {
678− coredump_report_failure("%s failed to allocate memory", __func__);
679− goto fail_dropcount;
680− }
681− for (argi = 0; argi < argc; argi++)
682− helper_argv[argi] = cn.corename + argv[argi];
683− helper_argv[argi] = NULL;
684−
685− retval = -ENOMEM;
686− sub_info = call_usermodehelper_setup(helper_argv[0],
687− helper_argv, NULL, GFP_KERNEL,
688− umh_coredump_setup, NULL, &cprm);
689− if (sub_info)
690− retval = call_usermodehelper_exec(sub_info,
691− UMH_WAIT_EXEC);
692+ retval = format_corename(&cn, &cprm, &argv, &argc);
693+ if (retval < 0) {
694+ coredump_report_failure("format_corename failed, aborting core");
695+ goto fail_unlock;
696+ }
692697
693− kfree(helper_argv);
694− if (retval) {
695− coredump_report_failure("|%s pipe failed", cn.corename);
696− goto close_fail;
697− }
698− } else {
698+ switch (cn.core_type) {
699+ case COREDUMP_FILE: {
699700 struct mnt_idmap *idmap;
700701 struct inode *inode;
701702 int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW |
@@ -789,6 +790,143 @@ void do_coredump(const kernel_siginfo_t *siginfo) · 87 unmodified lines
789790 if (do_truncate(idmap, cprm.file->f_path.dentry,
790791 0, 0, cprm.file))
791792 goto close_fail;
793+ break;
794+ }
795+ case COREDUMP_PIPE: {
796+ int argi;
797+ int dump_count;
798+ char **helper_argv;
799+ struct subprocess_info *sub_info;
800+
801+ if (cprm.limit == 1) {
802+ /* See umh_coredump_setup() which sets RLIMIT_CORE = 1.
803+ *
804+ * Normally core limits are irrelevant to pipes, since
805+ * we're not writing to the file system, but we use
806+ * cprm.limit of 1 here as a special value, this is a
807+ * consistent way to catch recursive crashes.
808+ * We can still crash if the core_pattern binary sets
809+ * RLIM_CORE = !1, but it runs as root, and can do
810+ * lots of stupid things.
811+ *
812+ * Note that we use task_tgid_vnr here to grab the pid
813+ * of the process group leader. That way we get the
814+ * right pid if a thread in a multi-threaded
815+ * core_pattern process dies.
816+ */
817+ coredump_report_failure("RLIMIT_CORE is set to 1, aborting core");
818+ goto fail_unlock;
819+ }
820+ cprm.limit = RLIM_INFINITY;
821+
822+ dump_count = atomic_inc_return(&core_dump_count);
823+ if (core_pipe_limit && (core_pipe_limit < dump_count)) {
824+ coredump_report_failure("over core_pipe_limit, skipping core dump");
825+ goto fail_dropcount;
826+ }
827+
828+ helper_argv = kmalloc_array(argc + 1, sizeof(*helper_argv),
829+ GFP_KERNEL);
830+ if (!helper_argv) {
831+ coredump_report_failure("%s failed to allocate memory", __func__);
832+ goto fail_dropcount;
833+ }
834+ for (argi = 0; argi < argc; argi++)
835+ helper_argv[argi] = cn.corename + argv[argi];
836+ helper_argv[argi] = NULL;
837+
838+ retval = -ENOMEM;
839+ sub_info = call_usermodehelper_setup(helper_argv[0],
840+ helper_argv, NULL, GFP_KERNEL,
841+ umh_coredump_setup, NULL, &cprm);
842+ if (sub_info)
843+ retval = call_usermodehelper_exec(sub_info,
844+ UMH_WAIT_EXEC);
845+
846+ kfree(helper_argv);
847+ if (retval) {
848+ coredump_report_failure("|%s pipe failed", cn.corename);
849+ goto close_fail;
850+ }
851+ break;
852+ }
853+ case COREDUMP_SOCK: {
854+#ifdef CONFIG_UNIX
855+ struct file *file __free(fput) = NULL;
856+ struct sockaddr_un addr = {
857+ .sun_family = AF_UNIX,
858+ };
859+ ssize_t addr_len;
860+ struct socket *socket;
861+
862+ addr_len = strscpy(addr.sun_path, cn.corename);
863+ if (addr_len < 0)
864+ goto close_fail;
865+ addr_len += offsetof(struct sockaddr_un, sun_path) + 1;
866+
867+ /*
868+ * It is possible that the userspace process which is
869+ * supposed to handle the coredump and is listening on
870+ * the AF_UNIX socket coredumps. Userspace should just
871+ * mark itself non dumpable.
872+ */
873+
874+ retval = sock_create_kern(&init_net, AF_UNIX, SOCK_STREAM, 0, &socket);
875+ if (retval < 0)
876+ goto close_fail;
877+
878+ file = sock_alloc_file(socket, 0, NULL);
879+ if (IS_ERR(file))
880+ goto close_fail;
881+
882+ /*
883+ * Set the thread-group leader pid which is used for the
884+ * peer credentials during connect() below. Then
885+ * immediately register it in pidfs...
886+ */
887+ cprm.pid = task_tgid(current);
888+ retval = pidfs_register_pid(cprm.pid);
889+ if (retval)
890+ goto close_fail;
891+
892+ /*
893+ * ... and set the coredump information so userspace
894+ * has it available after connect()...
895+ */
896+ pidfs_coredump(&cprm);
897+
898+ retval = kernel_connect(socket, (struct sockaddr *)(&addr),
899+ addr_len, O_NONBLOCK | SOCK_COREDUMP);
900+
901+ /*
902+ * ... Make sure to only put our reference after connect() took
903+ * its own reference keeping the pidfs entry alive ...
904+ */
905+ pidfs_put_pid(cprm.pid);
906+
907+ if (retval) {
908+ if (retval == -EAGAIN)
909+ coredump_report_failure("Coredump socket %s receive queue full", addr.sun_path);
910+ else
911+ coredump_report_failure("Coredump socket connection %s failed %d", addr.sun_path, retval);
912+ goto close_fail;
913+ }
914+
915+ /* ... and validate that @sk_peer_pid matches @cprm.pid. */
916+ if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid != cprm.pid))
917+ goto close_fail;
918+
919+ cprm.limit = RLIM_INFINITY;
920+ cprm.file = no_free_ptr(file);
921+#else
922+ coredump_report_failure("Core dump socket support %s disabled", cn.corename);
923+ goto close_fail;
924+#endif
925+ break;
926+ }
927+ default:
928+ WARN_ON_ONCE(true);
929+ goto close_fail;
792930 }
793931
794932 /* get us an unshared descriptor table; almost always a no-op */
@@ -823,13 +961,49 @@ void do_coredump(const kernel_siginfo_t *siginfo) · 28 unmodified lines
823961 file_end_write(cprm.file);
824962 free_vma_snapshot(&cprm);
825963 }
826− if (ispipe && core_pipe_limit)
827− wait_for_dump_helpers(cprm.file);
964+
965+#ifdef CONFIG_UNIX
966+ /* Let userspace know we're done processing the coredump. */
967+ if (sock_from_file(cprm.file))
968+ kernel_sock_shutdown(sock_from_file(cprm.file), SHUT_WR);
969+#endif
970+
971+ /*
972+ * When core_pipe_limit is set we wait for the coredump server
973+ * or usermodehelper to finish before exiting so it can e.g.,
974+ * inspect /proc/<pid>.
975+ */
976+ if (core_pipe_limit) {
977+ switch (cn.core_type) {
978+ case COREDUMP_PIPE:
979+ wait_for_dump_helpers(cprm.file);
980+ break;
981+#ifdef CONFIG_UNIX
982+ case COREDUMP_SOCK: {
983+ ssize_t n;
984+
985+ /*
986+ * We use a simple read to wait for the coredump
987+ * processing to finish. Either the socket is
988+ * closed or we get sent unexpected data. In
989+ * both cases, we're done.
990+ */
991+ n = __kernel_read(cprm.file, &(char){ 0 }, 1, NULL);
992+ if (n != 0)
993+ coredump_report_failure("Unexpected data on coredump socket");
994+ break;
995+ }
996+#endif
997+ default:
998+ break;
999+ }
1000+ }
1001+
8281002 close_fail:
8291003 if (cprm.file)
8301004 filp_close(cprm.file, NULL);
8311005 fail_dropcount:
832− if (ispipe)
1006+ if (cn.core_type == COREDUMP_PIPE)
8331007 atomic_dec(&core_dump_count);
8341008 fail_unlock:
8351009 kfree(argv);
@@ -852,10 +1026,9 @@ static int __dump_emit(struct coredump_params *cprm, const void *addr, int nr) · 16 unmodified lines
8521026 struct file *file = cprm->file;
8531027 loff_t pos = file->f_pos;
8541028 ssize_t n;
1029+
8551030 if (cprm->written + nr > cprm->limit)
8561031 return 0;
857−
858−
8591032 if (dump_interrupted())
8601033 return 0;
8611034 n = __kernel_write(file, addr, nr, &pos);
@@ -872,20 +1045,21 @@ static int __dump_skip(struct coredump_params *cprm, size_t nr) · 10 unmodified lines
8721045 {
8731046 static char zeroes[PAGE_SIZE];
8741047 struct file *file = cprm->file;
1048+
8751049 if (file->f_mode & FMODE_LSEEK) {
876− if (dump_interrupted() ||
877− vfs_llseek(file, nr, SEEK_CUR) < 0)
1050+ if (dump_interrupted() || vfs_llseek(file, nr, SEEK_CUR) < 0)
8781051 return 0;
8791052 cprm->pos += nr;
8801053 return 1;
881− } else {
882− while (nr > PAGE_SIZE) {
883− if (!__dump_emit(cprm, zeroes, PAGE_SIZE))
884− return 0;
885− nr -= PAGE_SIZE;
886− }
887− return __dump_emit(cprm, zeroes, nr);
8881054 }
1055+
1056+ while (nr > PAGE_SIZE) {
1057+ if (!__dump_emit(cprm, zeroes, PAGE_SIZE))
1058+ return 0;
1059+ nr -= PAGE_SIZE;
1060+ }
1061+
1062+ return __dump_emit(cprm, zeroes, nr);
8891063 }
8901064
8911065 int dump_emit(struct coredump_params *cprm, const void *addr, int nr)
@@ -1054,7 +1228,7 @@ EXPORT_SYMBOL(dump_align); · 162 unmodified lines
10541228 void validate_coredump_safety(void)
10551229 {
10561230 if (suid_dumpable == SUID_DUMP_ROOT &&
1057− core_pattern[0] != '/' && core_pattern[0] != '|') {
1231+ core_pattern[0] != '/' && core_pattern[0] != '|' && core_pattern[0] != '@') {
10581232
10591233 coredump_report_failure("Unsafe core_pattern used with fs.suid_dumpable=2: "
10601234 "pipe handler or fully qualified core dump path required. "
@@ -1062,18 +1236,55 @@ void validate_coredump_safety(void) · 1 unmodified line
10621236 }
10631237 }
10641238
1239+static inline bool check_coredump_socket(void)
1240+{
1241+ if (core_pattern[0] != '@')
1242+ return true;
1243+
1244+ /*
1245+ * Coredump socket must be located in the initial mount
1246+ * namespace. Don't give the impression that anything else is
1247+ * supported right now.
1248+ */
1249+ if (current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns)
1250+ return false;
1251+
1252+ /* Must be an absolute path. */
1253+ if (*(core_pattern + 1) != '/')
1254+ return false;
1255+
1256+ return true;
1257+}
1258+
10651259 static int proc_dostring_coredump(const struct ctl_table *table, int write,
10661260 void *buffer, size_t *lenp, loff_t *ppos)
10671261 {
1068− int error = proc_dostring(table, write, buffer, lenp, ppos);
1262+ int error;
1263+ ssize_t retval;
1264+ char old_core_pattern[CORENAME_MAX_SIZE];
1265+
1266+ retval = strscpy(old_core_pattern, core_pattern, CORENAME_MAX_SIZE);
1267+
1268+ error = proc_dostring(table, write, buffer, lenp, ppos);
1269+ if (error)
1270+ return error;
1271+ if (!check_coredump_socket()) {
1272+ strscpy(core_pattern, old_core_pattern, retval + 1);
1273+ return -EINVAL;
1274+ }
10691275
1070− if (!error)
1071− validate_coredump_safety();
1276+ validate_coredump_safety();
10721277 return error;
10731278 }
10741279
10751280 static const unsigned int core_file_note_size_min = CORE_FILE_NOTE_SIZE_DEFAULT;
10761281 static const unsigned int core_file_note_size_max = CORE_FILE_NOTE_SIZE_MAX;
1282+static char core_modes[] = {
1283+ "file\npipe"
1284+#ifdef CONFIG_UNIX
1285+ "\nsocket"
1286+#endif
1287+};
10771288
10781289 static const struct ctl_table coredump_sysctls[] = {
10791290 {
@@ -1117,6 +1328,13 @@ static const struct ctl_table coredump_sysctls[] = { · 37 unmodified lines
11171328 .extra1 = SYSCTL_ZERO,
11181329 .extra2 = SYSCTL_ONE,
11191330 },
1331+ {
1332+ .procname = "core_modes",
1333+ .data = core_modes,
1334+ .maxlen = sizeof(core_modes) - 1,
1335+ .mode = 0444,
1336+ .proc_handler = proc_dostring,
1337+ },
11201338 };
11211339
11221340 static int __init init_fs_coredump_sysctls(void)
fs/pidfs.c+55−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
2020 #include <linux/time_namespace.h>
2121 #include <linux/utsname.h>
2222 #include <net/net_namespace.h>
23+#include <linux/coredump.h>
2324
2425 #include "internal.h"
2526 #include "mount.h"
@@ -33,6 +34,7 @@ static struct kmem_cache *pidfs_cachep __ro_after_init; · 7 unmodified lines
3334 struct pidfs_exit_info {
3435 __u64 cgroupid;
3536 __s32 exit_code;
37+ __u32 coredump_mask;
3638 };
3739
3840 struct pidfs_inode {
@@ -240,6 +242,22 @@ static inline bool pid_in_current_pidns(const struct pid *pid) · 201 unmodified lines
240242 return false;
241243 }
242244
245+static __u32 pidfs_coredump_mask(unsigned long mm_flags)
246+{
247+ switch (__get_dumpable(mm_flags)) {
248+ case SUID_DUMP_USER:
249+ return PIDFD_COREDUMP_USER;
250+ case SUID_DUMP_ROOT:
251+ return PIDFD_COREDUMP_ROOT;
252+ case SUID_DUMP_DISABLE:
253+ return PIDFD_COREDUMP_SKIP;
254+ default:
255+ WARN_ON_ONCE(true);
256+ }
257+
258+ return 0;
259+}
260+
243261 static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
244262 {
245263 struct pidfd_info __user *uinfo = (struct pidfd_info __user *)arg;
@@ -280,6 +298,11 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg) · 34 unmodified lines
280298 }
281299 }
282300
301+ if (mask & PIDFD_INFO_COREDUMP) {
302+ kinfo.mask |= PIDFD_INFO_COREDUMP;
303+ kinfo.coredump_mask = READ_ONCE(pidfs_i(inode)->__pei.coredump_mask);
304+ }
305+
283306 task = get_pid_task(pid, PIDTYPE_PID);
284307 if (!task) {
285308 /*
@@ -296,6 +319,13 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg) · 10 unmodified lines
296319 if (!c)
297320 return -ESRCH;
298321
322+ if (!(kinfo.mask & PIDFD_INFO_COREDUMP)) {
323+ task_lock(task);
324+ if (task->mm)
325+ kinfo.coredump_mask = pidfs_coredump_mask(task->mm->flags);
326+ task_unlock(task);
327+ }
328+
299329 /* Unconditionally return identifiers and credentials, the rest only on request */
300330
301331 user_ns = current_user_ns();
@@ -559,6 +589,31 @@ void pidfs_exit(struct task_struct *tsk) · 257 unmodified lines
559589 }
560590 }
561591
592+#ifdef CONFIG_COREDUMP
593+void pidfs_coredump(const struct coredump_params *cprm)
594+{
595+ struct pid *pid = cprm->pid;
596+ struct pidfs_exit_info *exit_info;
597+ struct dentry *dentry;
598+ struct inode *inode;
599+ __u32 coredump_mask = 0;
600+
601+ dentry = pid->stashed;
602+ if (WARN_ON_ONCE(!dentry))
603+ return;
604+
605+ inode = d_inode(dentry);
606+ exit_info = &pidfs_i(inode)->__pei;
607+ /* Note how we were coredumped. */
608+ coredump_mask = pidfs_coredump_mask(cprm->mm_flags);
609+ /* Note that we actually did coredump. */
610+ coredump_mask |= PIDFD_COREDUMPED;
611+ /* If coredumping is set to skip we should never end up here. */
612+ VFS_WARN_ON_ONCE(coredump_mask & PIDFD_COREDUMP_SKIP);
613+ smp_store_release(&exit_info->coredump_mask, coredump_mask);
614+}
615+#endif
616+
562617 static struct vfsmount *pidfs_mnt __ro_after_init;
563618
564619 /*
include/linux/net.h+2−2
@@ -70,6 +70,7 @@ enum sock_type { · 69 unmodified lines
7070 SOCK_DCCP = 6,
7171 SOCK_PACKET = 10,
7272 };
73+#endif /* ARCH_HAS_SOCKET_TYPES */
7374
7475 #define SOCK_MAX (SOCK_PACKET + 1)
7576 /* Mask which covers at least up to SOCK_MASK-1. The
@@ -81,8 +82,7 @@ enum sock_type { · 5 unmodified lines
8182 #ifndef SOCK_NONBLOCK
8283 #define SOCK_NONBLOCK O_NONBLOCK
8384 #endif
84−
85−#endif /* ARCH_HAS_SOCKET_TYPES */
85+#define SOCK_COREDUMP O_NOCTTY
8686
8787 /**
8888 * enum sock_shutdown_cmd - Shutdown types
include/linux/pidfs.h+5−0
@@ -2,11 +2,16 @@ · 1 unmodified line
22 #ifndef _LINUX_PID_FS_H
33 #define _LINUX_PID_FS_H
44
5+struct coredump_params;
6+
57 struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags);
68 void __init pidfs_init(void);
79 void pidfs_add_pid(struct pid *pid);
810 void pidfs_remove_pid(struct pid *pid);
911 void pidfs_exit(struct task_struct *tsk);
12+#ifdef CONFIG_COREDUMP
13+void pidfs_coredump(const struct coredump_params *cprm);
14+#endif
1015 extern const struct dentry_operations pidfs_dentry_operations;
1116 int pidfs_register_pid(struct pid *pid);
1217 void pidfs_get_pid(struct pid *pid);
include/uapi/linux/pidfd.h+16−0
@@ -25,9 +25,23 @@ · 24 unmodified lines
2525 #define PIDFD_INFO_CREDS (1UL << 1) /* Always returned, even if not requested */
2626 #define PIDFD_INFO_CGROUPID (1UL << 2) /* Always returned if available, even if not requested */
2727 #define PIDFD_INFO_EXIT (1UL << 3) /* Only returned if requested. */
28+#define PIDFD_INFO_COREDUMP (1UL << 4) /* Only returned if requested. */
2829
2930 #define PIDFD_INFO_SIZE_VER0 64 /* sizeof first published struct */
3031
32+/*
33+ * Values for @coredump_mask in pidfd_info.
34+ * Only valid if PIDFD_INFO_COREDUMP is set in @mask.
35+ *
36+ * Note, the @PIDFD_COREDUMP_ROOT flag indicates that the generated
37+ * coredump should be treated as sensitive and access should only be
38+ * granted to privileged users.
39+ */
40+#define PIDFD_COREDUMPED (1U << 0) /* Did crash and... */
41+#define PIDFD_COREDUMP_SKIP (1U << 1) /* coredumping generation was skipped. */
42+#define PIDFD_COREDUMP_USER (1U << 2) /* coredump was done as the user. */
43+#define PIDFD_COREDUMP_ROOT (1U << 3) /* coredump was done as root. */
44+
3145 /*
3246 * The concept of process and threads in userland and the kernel is a confusing
3347 * one - within the kernel every thread is a 'task' with its own individual PID,
@@ -92,6 +106,8 @@ struct pidfd_info { · 58 unmodified lines
92106 __u32 fsuid;
93107 __u32 fsgid;
94108 __s32 exit_code;
109+ __u32 coredump_mask;
110+ __u32 __spare1;
95111 };
96112
97113 #define PIDFS_IOCTL_MAGIC 0xFF
net/unix/af_unix.c+41−13
@@ -85,10 +85,13 @@ · 84 unmodified lines
8585 #include <linux/file.h>
8686 #include <linux/filter.h>
8787 #include <linux/fs.h>
88+#include <linux/fs_struct.h>
8889 #include <linux/init.h>
8990 #include <linux/kernel.h>
9091 #include <linux/mount.h>
9192 #include <linux/namei.h>
93+#include <linux/net.h>
94+#include <linux/pidfs.h>
9295 #include <linux/poll.h>
9396 #include <linux/proc_fs.h>
9497 #include <linux/sched/signal.h>
@@ -100,7 +103,6 @@ · 5 unmodified lines
100103 #include <linux/splice.h>
101104 #include <linux/string.h>
102105 #include <linux/uaccess.h>
103−#include <linux/pidfs.h>
104106 #include <net/af_unix.h>
105107 #include <net/net_namespace.h>
106108 #include <net/scm.h>
@@ -1146,7 +1148,7 @@ static int unix_release(struct socket *sock) · 1039 unmodified lines
11461148 }
11471149
11481150 static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
1149− int type)
1151+ int type, int flags)
11501152 {
11511153 struct inode *inode;
11521154 struct path path;
@@ -1154,13 +1156,39 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len, · 1 unmodified line
11541156 int err;
11551157
11561158 unix_mkname_bsd(sunaddr, addr_len);
1157− err = kern_path(sunaddr->sun_path, LOOKUP_FOLLOW, &path);
1158− if (err)
1159− goto fail;
11601159
1161− err = path_permission(&path, MAY_WRITE);
1162− if (err)
1163− goto path_put;
1160+ if (flags & SOCK_COREDUMP) {
1161+ const struct cred *cred;
1162+ struct cred *kcred;
1163+ struct path root;
1164+
1165+ kcred = prepare_kernel_cred(&init_task);
1166+ if (!kcred) {
1167+ err = -ENOMEM;
1168+ goto fail;
1169+ }
1170+
1171+ task_lock(&init_task);
1172+ get_fs_root(init_task.fs, &root);
1173+ task_unlock(&init_task);
1174+
1175+ cred = override_creds(kcred);
1176+ err = vfs_path_lookup(root.dentry, root.mnt, sunaddr->sun_path,
1177+ LOOKUP_BENEATH | LOOKUP_NO_SYMLINKS |
1178+ LOOKUP_NO_MAGICLINKS, &path);
1179+ put_cred(revert_creds(cred));
1180+ path_put(&root);
1181+ if (err)
1182+ goto fail;
1183+ } else {
1184+ err = kern_path(sunaddr->sun_path, LOOKUP_FOLLOW, &path);
1185+ if (err)
1186+ goto fail;
1187+
1188+ err = path_permission(&path, MAY_WRITE);
1189+ if (err)
1190+ goto path_put;
1191+ }
11641192
11651193 err = -ECONNREFUSED;
11661194 inode = d_backing_inode(path.dentry);
@@ -1210,12 +1238,12 @@ static struct sock *unix_find_abstract(struct net *net, · 43 unmodified lines
12101238
12111239 static struct sock *unix_find_other(struct net *net,
12121240 struct sockaddr_un *sunaddr,
1213− int addr_len, int type)
1241+ int addr_len, int type, int flags)
12141242 {
12151243 struct sock *sk;
12161244
12171245 if (sunaddr->sun_path[0])
1218− sk = unix_find_bsd(sunaddr, addr_len, type);
1246+ sk = unix_find_bsd(sunaddr, addr_len, type, flags);
12191247 else
12201248 sk = unix_find_abstract(net, sunaddr, addr_len, type);
12211249
@@ -1473,7 +1501,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, · 251 unmodified lines
14731501 }
14741502
14751503 restart:
1476− other = unix_find_other(sock_net(sk), sunaddr, alen, sock->type);
1504+ other = unix_find_other(sock_net(sk), sunaddr, alen, sock->type, 0);
14771505 if (IS_ERR(other)) {
14781506 err = PTR_ERR(other);
14791507 goto out;
@@ -1620,7 +1648,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, · 140 unmodified lines
16201648
16211649 restart:
16221650 /* Find listening sock. */
1623− other = unix_find_other(net, sunaddr, addr_len, sk->sk_type);
1651+ other = unix_find_other(net, sunaddr, addr_len, sk->sk_type, flags);
16241652 if (IS_ERR(other)) {
16251653 err = PTR_ERR(other);
16261654 goto out_free_skb;
@@ -2089,7 +2117,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, · 462 unmodified lines
20892117 if (msg->msg_namelen) {
20902118 lookup:
20912119 other = unix_find_other(sock_net(sk), msg->msg_name,
2092− msg->msg_namelen, sk->sk_type);
2120+ msg->msg_namelen, sk->sk_type, 0);
20932121 if (IS_ERR(other)) {
20942122 err = PTR_ERR(other);
20952123 goto out_free;
tools/testing/selftests/coredump/stackdump_test.c+466−1
@@ -1,14 +1,20 @@
11 // SPDX-License-Identifier: GPL-2.0
22
33 #include <fcntl.h>
4+#include <inttypes.h>
45 #include <libgen.h>
56 #include <linux/limits.h>
67 #include <pthread.h>
78 #include <string.h>
9+#include <sys/mount.h>
810 #include <sys/resource.h>
11+#include <sys/stat.h>
12+#include <sys/socket.h>
13+#include <sys/un.h>
914 #include <unistd.h>
1015
1116 #include "../kselftest_harness.h"
17+#include "../pidfd/pidfd.h"
1218
1319 #define STACKDUMP_FILE "stack_values"
1420 #define STACKDUMP_SCRIPT "stackdump"
@@ -35,6 +41,7 @@ static void crashing_child(void) · 20 unmodified lines
3541 FIXTURE(coredump)
3642 {
3743 char original_core_pattern[256];
44+ pid_t pid_coredump_server;
3845 };
3946
4047 FIXTURE_SETUP(coredump)
@@ -44,6 +51,7 @@ FIXTURE_SETUP(coredump) · 3 unmodified lines
4451 char *dir;
4552 int ret;
4653
54+ self->pid_coredump_server = -ESRCH;
4755 file = fopen("/proc/sys/kernel/core_pattern", "r");
4856 ASSERT_NE(NULL, file);
4957
@@ -61,10 +69,17 @@ FIXTURE_TEARDOWN(coredump) · 11 unmodified lines
6169 {
6270 const char *reason;
6371 FILE *file;
64− int ret;
72+ int ret, status;
6573
6674 unlink(STACKDUMP_FILE);
6775
76+ if (self->pid_coredump_server > 0) {
77+ kill(self->pid_coredump_server, SIGTERM);
78+ waitpid(self->pid_coredump_server, &status, 0);
79+ }
80+ unlink("/tmp/coredump.file");
81+ unlink("/tmp/coredump.socket");
82+
6883 file = fopen("/proc/sys/kernel/core_pattern", "w");
6984 if (!file) {
7085 reason = "Unable to open core_pattern";
@@ -154,4 +169,454 @@ TEST_F_TIMEOUT(coredump, stackdump, 120) · 83 unmodified lines
154169 fclose(file);
155170 }
156171
172+TEST_F(coredump, socket)
173+{
174+ int fd, pidfd, ret, status;
175+ FILE *file;
176+ pid_t pid, pid_coredump_server;
177+ struct stat st;
178+ char core_file[PATH_MAX];
179+ struct pidfd_info info = {};
180+ int ipc_sockets[2];
181+ char c;
182+ const struct sockaddr_un coredump_sk = {
183+ .sun_family = AF_UNIX,
184+ .sun_path = "/tmp/coredump.socket",
185+ };
186+ size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
187+ sizeof("/tmp/coredump.socket");
188+
189+ ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
190+ ASSERT_EQ(ret, 0);
191+
192+ file = fopen("/proc/sys/kernel/core_pattern", "w");
193+ ASSERT_NE(file, NULL);
194+
195+ ret = fprintf(file, "@/tmp/coredump.socket");
196+ ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
197+ ASSERT_EQ(fclose(file), 0);
198+
199+ pid_coredump_server = fork();
200+ ASSERT_GE(pid_coredump_server, 0);
201+ if (pid_coredump_server == 0) {
202+ int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file;
203+ socklen_t fd_peer_pidfd_len;
204+
205+ close(ipc_sockets[0]);
206+
207+ fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
208+ if (fd_server < 0)
209+ _exit(EXIT_FAILURE);
210+
211+ ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
212+ if (ret < 0) {
213+ fprintf(stderr, "Failed to bind coredump socket\n");
214+ close(fd_server);
215+ close(ipc_sockets[1]);
216+ _exit(EXIT_FAILURE);
217+ }
218+
219+ ret = listen(fd_server, 1);
220+ if (ret < 0) {
221+ fprintf(stderr, "Failed to listen on coredump socket\n");
222+ close(fd_server);
223+ close(ipc_sockets[1]);
224+ _exit(EXIT_FAILURE);
225+ }
226+
227+ if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
228+ close(fd_server);
229+ close(ipc_sockets[1]);
230+ _exit(EXIT_FAILURE);
231+ }
232+
233+ close(ipc_sockets[1]);
234+
235+ fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
236+ if (fd_coredump < 0) {
237+ fprintf(stderr, "Failed to accept coredump socket connection\n");
238+ close(fd_server);
239+ _exit(EXIT_FAILURE);
240+ }
241+
242+ fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
243+ ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD,
244+ &fd_peer_pidfd, &fd_peer_pidfd_len);
245+ if (ret < 0) {
246+ fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n");
247+ close(fd_coredump);
248+ close(fd_server);
249+ _exit(EXIT_FAILURE);
250+ }
251+
252+ memset(&info, 0, sizeof(info));
253+ info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
254+ ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info);
255+ if (ret < 0) {
256+ fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n");
257+ close(fd_coredump);
258+ close(fd_server);
259+ close(fd_peer_pidfd);
260+ _exit(EXIT_FAILURE);
261+ }
262+
263+ if (!(info.mask & PIDFD_INFO_COREDUMP)) {
264+ fprintf(stderr, "Missing coredump information from coredumping task\n");
265+ close(fd_coredump);
266+ close(fd_server);
267+ close(fd_peer_pidfd);
268+ _exit(EXIT_FAILURE);
269+ }
270+
271+ if (!(info.coredump_mask & PIDFD_COREDUMPED)) {
272+ fprintf(stderr, "Received connection from non-coredumping task\n");
273+ close(fd_coredump);
274+ close(fd_server);
275+ close(fd_peer_pidfd);
276+ _exit(EXIT_FAILURE);
277+ }
278+
279+ fd_core_file = creat("/tmp/coredump.file", 0644);
280+ if (fd_core_file < 0) {
281+ fprintf(stderr, "Failed to create coredump file\n");
282+ close(fd_coredump);
283+ close(fd_server);
284+ close(fd_peer_pidfd);
285+ _exit(EXIT_FAILURE);
286+ }
287+
288+ for (;;) {
289+ char buffer[4096];
290+ ssize_t bytes_read, bytes_write;
291+
292+ bytes_read = read(fd_coredump, buffer, sizeof(buffer));
293+ if (bytes_read < 0) {
294+ close(fd_coredump);
295+ close(fd_server);
296+ close(fd_peer_pidfd);
297+ close(fd_core_file);
298+ _exit(EXIT_FAILURE);
299+ }
300+
301+ if (bytes_read == 0)
302+ break;
303+
304+ bytes_write = write(fd_core_file, buffer, bytes_read);
305+ if (bytes_read != bytes_write) {
306+ close(fd_coredump);
307+ close(fd_server);
308+ close(fd_peer_pidfd);
309+ close(fd_core_file);
310+ _exit(EXIT_FAILURE);
311+ }
312+ }
313+
314+ close(fd_coredump);
315+ close(fd_server);
316+ close(fd_peer_pidfd);
317+ close(fd_core_file);
318+ _exit(EXIT_SUCCESS);
319+ }
320+ self->pid_coredump_server = pid_coredump_server;
321+
322+ EXPECT_EQ(close(ipc_sockets[1]), 0);
323+ ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
324+ EXPECT_EQ(close(ipc_sockets[0]), 0);
325+
326+ pid = fork();
327+ ASSERT_GE(pid, 0);
328+ if (pid == 0)
329+ crashing_child();
330+
331+ pidfd = sys_pidfd_open(pid, 0);
332+ ASSERT_GE(pidfd, 0);
333+
334+ waitpid(pid, &status, 0);
335+ ASSERT_TRUE(WIFSIGNALED(status));
336+ ASSERT_TRUE(WCOREDUMP(status));
337+
338+ info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
339+ ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0);
340+ ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
341+ ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
342+
343+ waitpid(pid_coredump_server, &status, 0);
344+ self->pid_coredump_server = -ESRCH;
345+ ASSERT_TRUE(WIFEXITED(status));
346+ ASSERT_EQ(WEXITSTATUS(status), 0);
347+
348+ ASSERT_EQ(stat("/tmp/coredump.file", &st), 0);
349+ ASSERT_GT(st.st_size, 0);
350+ /*
351+ * We should somehow validate the produced core file.
352+ * For now just allow for visual inspection
353+ */
354+ system("file /tmp/coredump.file");
355+}
356+
357+TEST_F(coredump, socket_detect_userspace_client)
358+{
359+ int fd, pidfd, ret, status;
360+ FILE *file;
361+ pid_t pid, pid_coredump_server;
362+ struct stat st;
363+ char core_file[PATH_MAX];
364+ struct pidfd_info info = {};
365+ int ipc_sockets[2];
366+ char c;
367+ const struct sockaddr_un coredump_sk = {
368+ .sun_family = AF_UNIX,
369+ .sun_path = "/tmp/coredump.socket",
370+ };
371+ size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
372+ sizeof("/tmp/coredump.socket");
373+
374+ file = fopen("/proc/sys/kernel/core_pattern", "w");
375+ ASSERT_NE(file, NULL);
376+
377+ ret = fprintf(file, "@/tmp/coredump.socket");
378+ ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
379+ ASSERT_EQ(fclose(file), 0);
380+
381+ ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
382+ ASSERT_EQ(ret, 0);
383+
384+ pid_coredump_server = fork();
385+ ASSERT_GE(pid_coredump_server, 0);
386+ if (pid_coredump_server == 0) {
387+ int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file;
388+ socklen_t fd_peer_pidfd_len;
389+
390+ close(ipc_sockets[0]);
391+
392+ fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
393+ if (fd_server < 0)
394+ _exit(EXIT_FAILURE);
395+
396+ ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
397+ if (ret < 0) {
398+ fprintf(stderr, "Failed to bind coredump socket\n");
399+ close(fd_server);
400+ close(ipc_sockets[1]);
401+ _exit(EXIT_FAILURE);
402+ }
403+
404+ ret = listen(fd_server, 1);
405+ if (ret < 0) {
406+ fprintf(stderr, "Failed to listen on coredump socket\n");
407+ close(fd_server);
408+ close(ipc_sockets[1]);
409+ _exit(EXIT_FAILURE);
410+ }
411+
412+ if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
413+ close(fd_server);
414+ close(ipc_sockets[1]);
415+ _exit(EXIT_FAILURE);
416+ }
417+
418+ close(ipc_sockets[1]);
419+
420+ fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
421+ if (fd_coredump < 0) {
422+ fprintf(stderr, "Failed to accept coredump socket connection\n");
423+ close(fd_server);
424+ _exit(EXIT_FAILURE);
425+ }
426+
427+ fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
428+ ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD,
429+ &fd_peer_pidfd, &fd_peer_pidfd_len);
430+ if (ret < 0) {
431+ fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n");
432+ close(fd_coredump);
433+ close(fd_server);
434+ _exit(EXIT_FAILURE);
435+ }
436+
437+ memset(&info, 0, sizeof(info));
438+ info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
439+ ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info);
440+ if (ret < 0) {
441+ fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n");
442+ close(fd_coredump);
443+ close(fd_server);
444+ close(fd_peer_pidfd);
445+ _exit(EXIT_FAILURE);
446+ }
447+
448+ if (!(info.mask & PIDFD_INFO_COREDUMP)) {
449+ fprintf(stderr, "Missing coredump information from coredumping task\n");
450+ close(fd_coredump);
451+ close(fd_server);
452+ close(fd_peer_pidfd);
453+ _exit(EXIT_FAILURE);
454+ }
455+
456+ if (info.coredump_mask & PIDFD_COREDUMPED) {
457+ fprintf(stderr, "Received unexpected connection from coredumping task\n");
458+ close(fd_coredump);
459+ close(fd_server);
460+ close(fd_peer_pidfd);
461+ _exit(EXIT_FAILURE);
462+ }
463+
464+ close(fd_coredump);
465+ close(fd_server);
466+ close(fd_peer_pidfd);
467+ close(fd_core_file);
468+ _exit(EXIT_SUCCESS);
469+ }
470+ self->pid_coredump_server = pid_coredump_server;
471+
472+ EXPECT_EQ(close(ipc_sockets[1]), 0);
473+ ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
474+ EXPECT_EQ(close(ipc_sockets[0]), 0);
475+
476+ pid = fork();
477+ ASSERT_GE(pid, 0);
478+ if (pid == 0) {
479+ int fd_socket;
480+ ssize_t ret;
481+
482+ fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
483+ if (fd_socket < 0)
484+ _exit(EXIT_FAILURE);
485+
486+
487+ ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
488+ if (ret < 0)
489+ _exit(EXIT_FAILURE);
490+
491+ (void *)write(fd_socket, &(char){ 0 }, 1);
492+ close(fd_socket);
493+ _exit(EXIT_SUCCESS);
494+ }
495+
496+ pidfd = sys_pidfd_open(pid, 0);
497+ ASSERT_GE(pidfd, 0);
498+
499+ waitpid(pid, &status, 0);
500+ ASSERT_TRUE(WIFEXITED(status));
501+ ASSERT_EQ(WEXITSTATUS(status), 0);
502+
503+ info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
504+ ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0);
505+ ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
506+ ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0);
507+
508+ waitpid(pid_coredump_server, &status, 0);
509+ self->pid_coredump_server = -ESRCH;
510+ ASSERT_TRUE(WIFEXITED(status));
511+ ASSERT_EQ(WEXITSTATUS(status), 0);
512+
513+ ASSERT_NE(stat("/tmp/coredump.file", &st), 0);
514+ ASSERT_EQ(errno, ENOENT);
515+}
516+
517+TEST_F(coredump, socket_enoent)
518+{
519+ int pidfd, ret, status;
520+ FILE *file;
521+ pid_t pid;
522+ char core_file[PATH_MAX];
523+
524+ file = fopen("/proc/sys/kernel/core_pattern", "w");
525+ ASSERT_NE(file, NULL);
526+
527+ ret = fprintf(file, "@/tmp/coredump.socket");
528+ ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
529+ ASSERT_EQ(fclose(file), 0);
530+
531+ pid = fork();
532+ ASSERT_GE(pid, 0);
533+ if (pid == 0)
534+ crashing_child();
535+
536+ pidfd = sys_pidfd_open(pid, 0);
537+ ASSERT_GE(pidfd, 0);
538+
539+ waitpid(pid, &status, 0);
540+ ASSERT_TRUE(WIFSIGNALED(status));
541+ ASSERT_FALSE(WCOREDUMP(status));
542+}
543+
544+TEST_F(coredump, socket_no_listener)
545+{
546+ int pidfd, ret, status;
547+ FILE *file;
548+ pid_t pid, pid_coredump_server;
549+ int ipc_sockets[2];
550+ char c;
551+ const struct sockaddr_un coredump_sk = {
552+ .sun_family = AF_UNIX,
553+ .sun_path = "/tmp/coredump.socket",
554+ };
555+ size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
556+ sizeof("/tmp/coredump.socket");
557+
558+ ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
559+ ASSERT_EQ(ret, 0);
560+
561+ file = fopen("/proc/sys/kernel/core_pattern", "w");
562+ ASSERT_NE(file, NULL);
563+
564+ ret = fprintf(file, "@/tmp/coredump.socket");
565+ ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
566+ ASSERT_EQ(fclose(file), 0);
567+
568+ pid_coredump_server = fork();
569+ ASSERT_GE(pid_coredump_server, 0);
570+ if (pid_coredump_server == 0) {
571+ int fd_server;
572+ socklen_t fd_peer_pidfd_len;
573+
574+ close(ipc_sockets[0]);
575+
576+ fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
577+ if (fd_server < 0)
578+ _exit(EXIT_FAILURE);
579+
580+ ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
581+ if (ret < 0) {
582+ fprintf(stderr, "Failed to bind coredump socket\n");
583+ close(fd_server);
584+ close(ipc_sockets[1]);
585+ _exit(EXIT_FAILURE);
586+ }
587+
588+ if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
589+ close(fd_server);
590+ close(ipc_sockets[1]);
591+ _exit(EXIT_FAILURE);
592+ }
593+
594+ close(fd_server);
595+ close(ipc_sockets[1]);
596+ _exit(EXIT_SUCCESS);
597+ }
598+ self->pid_coredump_server = pid_coredump_server;
599+
600+ EXPECT_EQ(close(ipc_sockets[1]), 0);
601+ ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
602+ EXPECT_EQ(close(ipc_sockets[0]), 0);
603+
604+ pid = fork();
605+ ASSERT_GE(pid, 0);
606+ if (pid == 0)
607+ crashing_child();
608+
609+ pidfd = sys_pidfd_open(pid, 0);
610+ ASSERT_GE(pidfd, 0);
611+
612+ waitpid(pid, &status, 0);
613+ ASSERT_TRUE(WIFSIGNALED(status));
614+ ASSERT_FALSE(WCOREDUMP(status));
615+
616+ waitpid(pid_coredump_server, &status, 0);
617+ self->pid_coredump_server = -ESRCH;
618+ ASSERT_TRUE(WIFEXITED(status));
619+ ASSERT_EQ(WEXITSTATUS(status), 0);
620+}
621+
157622 TEST_HARNESS_MAIN
tools/testing/selftests/pidfd/pidfd.h+22−0
@@ -131,6 +131,26 @@ · 130 unmodified lines
131131 #define PIDFD_INFO_EXIT (1UL << 3) /* Always returned if available, even if not requested */
132132 #endif
133133
134+#ifndef PIDFD_INFO_COREDUMP
135+#define PIDFD_INFO_COREDUMP (1UL << 4)
136+#endif
137+
138+#ifndef PIDFD_COREDUMPED
139+#define PIDFD_COREDUMPED (1U << 0) /* Did crash and... */
140+#endif
141+
142+#ifndef PIDFD_COREDUMP_SKIP
143+#define PIDFD_COREDUMP_SKIP (1U << 1) /* coredumping generation was skipped. */
144+#endif
145+
146+#ifndef PIDFD_COREDUMP_USER
147+#define PIDFD_COREDUMP_USER (1U << 2) /* coredump was done as the user. */
148+#endif
149+
150+#ifndef PIDFD_COREDUMP_ROOT
151+#define PIDFD_COREDUMP_ROOT (1U << 3) /* coredump was done as root. */
152+#endif
153+
134154 #ifndef PIDFD_THREAD
135155 #define PIDFD_THREAD O_EXCL
136156 #endif
@@ -150,6 +170,8 @@ struct pidfd_info { · 13 unmodified lines
150170 __u32 fsuid;
151171 __u32 fsgid;
152172 __s32 exit_code;
173+ __u32 coredump_mask;
174+ __u32 __spare1;
153175 };
154176
155177 /*