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
36 SOCK_PACKET = 10,36 SOCK_PACKET = 10,
37};37};
38 38
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
48#define ARCH_HAS_SOCKET_TYPES 139#define ARCH_HAS_SOCKET_TYPES 1
49 40
50#endif /* _ASM_SOCKET_H */41#endif /* _ASM_SOCKET_H */
fs/coredump.c+310−92
@@ -44,7 +44,13 @@ · 43 unmodified lines
44#include <linux/sysctl.h>44#include <linux/sysctl.h>
45#include <linux/elf.h>45#include <linux/elf.h>
46#include <linux/pidfs.h>46#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>
47#include <uapi/linux/pidfd.h>52#include <uapi/linux/pidfd.h>
53#include <uapi/linux/un.h>
48 54
49#include <linux/uaccess.h>55#include <linux/uaccess.h>
50#include <asm/mmu_context.h>56#include <asm/mmu_context.h>
@@ -76,9 +82,16 @@ static char core_pattern[CORENAME_MAX_SIZE] = "core"; · 25 unmodified lines
76static int core_name_size = CORENAME_MAX_SIZE;82static int core_name_size = CORENAME_MAX_SIZE;
77unsigned int core_file_note_size_limit = CORE_FILE_NOTE_SIZE_DEFAULT;83unsigned int core_file_note_size_limit = CORE_FILE_NOTE_SIZE_DEFAULT;
78 84
85enum coredump_type_t {
86 COREDUMP_FILE = 1,
87 COREDUMP_PIPE = 2,
88 COREDUMP_SOCK = 3,
89};
90
79struct core_name {91struct core_name {
80 char *corename;92 char *corename;
81 int used, size;93 int used, size;
94 enum coredump_type_t core_type;
82};95};
83 96
84static int expand_corename(struct core_name *cn, int size)97static 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
218{231{
219 const struct cred *cred = current_cred();232 const struct cred *cred = current_cred();
220 const char *pat_ptr = core_pattern;233 const char *pat_ptr = core_pattern;
221 int ispipe = (*pat_ptr == '|');
222 bool was_space = false;234 bool was_space = false;
223 int pid_in_pattern = 0;235 int pid_in_pattern = 0;
224 int err = 0;236 int err = 0;
225 237
226 cn->used = 0;238 cn->used = 0;
227 cn->corename = NULL;239 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;
228 if (expand_corename(cn, core_name_size))246 if (expand_corename(cn, core_name_size))
229 return -ENOMEM;247 return -ENOMEM;
230 cn->corename[0] = '\0';248 cn->corename[0] = '\0';
231 249
232 if (ispipe) {250 switch (cn->core_type) {
251 case COREDUMP_PIPE: {
233 int argvs = sizeof(core_pattern) / 2;252 int argvs = sizeof(core_pattern) / 2;
234 (*argv) = kmalloc_array(argvs, sizeof(**argv), GFP_KERNEL);253 (*argv) = kmalloc_array(argvs, sizeof(**argv), GFP_KERNEL);
235 if (!(*argv))254 if (!(*argv))
@@ -238,6 +257,45 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm, · 2 unmodified lines
238 ++pat_ptr;257 ++pat_ptr;
239 if (!(*pat_ptr))258 if (!(*pat_ptr))
240 return -ENOMEM;259 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;
241 }299 }
242 300
243 /* Repeat as long as we have more pattern to process and more output301 /* 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
247 * Split on spaces before doing template expansion so that305 * Split on spaces before doing template expansion so that
248 * %e and %E don't get split if they have spaces in them306 * %e and %E don't get split if they have spaces in them
249 */307 */
250 if (ispipe) {308 if (cn->core_type == COREDUMP_PIPE) {
251 if (isspace(*pat_ptr)) {309 if (isspace(*pat_ptr)) {
252 if (cn->used != 0)310 if (cn->used != 0)
253 was_space = true;311 was_space = true;
@@ -353,7 +411,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm, · 99 unmodified lines
353 * Installing a pidfd only makes sense if411 * Installing a pidfd only makes sense if
354 * we actually spawn a usermode helper.412 * we actually spawn a usermode helper.
355 */413 */
356 if (!ispipe)414 if (cn->core_type != COREDUMP_PIPE)
357 break;415 break;
358 416
359 /*417 /*
@@ -384,12 +442,10 @@ out: · 24 unmodified lines
384 * If core_pattern does not include a %p (as is the default)442 * If core_pattern does not include a %p (as is the default)
385 * and core_uses_pid is set, then .%pid will be appended to443 * and core_uses_pid is set, then .%pid will be appended to
386 * the filename. Do not do this for piped commands. */444 * the filename. Do not do this for piped commands. */
387 if (!ispipe && !pid_in_pattern && core_uses_pid) {445 if (cn->core_type == COREDUMP_FILE && !pid_in_pattern && core_uses_pid)
388 err = cn_printf(cn, ".%d", task_tgid_vnr(current));446 return cn_printf(cn, ".%d", task_tgid_vnr(current));
389 if (err)447
390 return err;448 return 0;
391 }
392 return ispipe;
393}449}
394 450
395static int zap_process(struct signal_struct *signal, int exit_code)451static 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
545 if (IS_ERR(pidfs_file))601 if (IS_ERR(pidfs_file))
546 return PTR_ERR(pidfs_file);602 return PTR_ERR(pidfs_file);
547 603
604 pidfs_coredump(cp);
605
548 /*606 /*
549 * Usermode helpers are childen of either607 * Usermode helpers are childen of either
550 * system_unbound_wq or of kthreadd. So we know that608 * 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
583 const struct cred *old_cred;641 const struct cred *old_cred;
584 struct cred *cred;642 struct cred *cred;
585 int retval = 0;643 int retval = 0;
586 int ispipe;
587 size_t *argv = NULL;644 size_t *argv = NULL;
588 int argc = 0;645 int argc = 0;
589 /* require nonrelative corefile path and be extra careful */646 /* require nonrelative corefile path and be extra careful */
@@ -632,70 +689,14 @@ void do_coredump(const kernel_siginfo_t *siginfo) · 42 unmodified lines
632 689
633 old_cred = override_creds(cred);690 old_cred = override_creds(cred);
634 691
635 ispipe = format_corename(&cn, &cprm, &argv, &argc);692 retval = format_corename(&cn, &cprm, &argv, &argc);
636 693 if (retval < 0) {
637 if (ispipe) {694 coredump_report_failure("format_corename failed, aborting core");
638 int argi;695 goto fail_unlock;
639 int dump_count;696 }
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 697
693 kfree(helper_argv);698 switch (cn.core_type) {
694 if (retval) {699 case COREDUMP_FILE: {
695 coredump_report_failure("|%s pipe failed", cn.corename);
696 goto close_fail;
697 }
698 } else {
699 struct mnt_idmap *idmap;700 struct mnt_idmap *idmap;
700 struct inode *inode;701 struct inode *inode;
701 int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW |702 int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW |
@@ -789,6 +790,143 @@ void do_coredump(const kernel_siginfo_t *siginfo) · 87 unmodified lines
789 if (do_truncate(idmap, cprm.file->f_path.dentry,790 if (do_truncate(idmap, cprm.file->f_path.dentry,
790 0, 0, cprm.file))791 0, 0, cprm.file))
791 goto close_fail;792 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;
792 }930 }
793 931
794 /* get us an unshared descriptor table; almost always a no-op */932 /* 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
823 file_end_write(cprm.file);961 file_end_write(cprm.file);
824 free_vma_snapshot(&cprm);962 free_vma_snapshot(&cprm);
825 }963 }
826 if (ispipe && core_pipe_limit)964
827 wait_for_dump_helpers(cprm.file);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
828close_fail:1002close_fail:
829 if (cprm.file)1003 if (cprm.file)
830 filp_close(cprm.file, NULL);1004 filp_close(cprm.file, NULL);
831fail_dropcount:1005fail_dropcount:
832 if (ispipe)1006 if (cn.core_type == COREDUMP_PIPE)
833 atomic_dec(&core_dump_count);1007 atomic_dec(&core_dump_count);
834fail_unlock:1008fail_unlock:
835 kfree(argv);1009 kfree(argv);
@@ -852,10 +1026,9 @@ static int __dump_emit(struct coredump_params *cprm, const void *addr, int nr) · 16 unmodified lines
852 struct file *file = cprm->file;1026 struct file *file = cprm->file;
853 loff_t pos = file->f_pos;1027 loff_t pos = file->f_pos;
854 ssize_t n;1028 ssize_t n;
1029
855 if (cprm->written + nr > cprm->limit)1030 if (cprm->written + nr > cprm->limit)
856 return 0;1031 return 0;
857
858
859 if (dump_interrupted())1032 if (dump_interrupted())
860 return 0;1033 return 0;
861 n = __kernel_write(file, addr, nr, &pos);1034 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
872{1045{
873 static char zeroes[PAGE_SIZE];1046 static char zeroes[PAGE_SIZE];
874 struct file *file = cprm->file;1047 struct file *file = cprm->file;
1048
875 if (file->f_mode & FMODE_LSEEK) {1049 if (file->f_mode & FMODE_LSEEK) {
876 if (dump_interrupted() ||1050 if (dump_interrupted() || vfs_llseek(file, nr, SEEK_CUR) < 0)
877 vfs_llseek(file, nr, SEEK_CUR) < 0)
878 return 0;1051 return 0;
879 cprm->pos += nr;1052 cprm->pos += nr;
880 return 1;1053 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);
888 }1054 }
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);
889}1063}
890 1064
891int dump_emit(struct coredump_params *cprm, const void *addr, int nr)1065int dump_emit(struct coredump_params *cprm, const void *addr, int nr)
@@ -1054,7 +1228,7 @@ EXPORT_SYMBOL(dump_align); · 162 unmodified lines
1054void validate_coredump_safety(void)1228void validate_coredump_safety(void)
1055{1229{
1056 if (suid_dumpable == SUID_DUMP_ROOT &&1230 if (suid_dumpable == SUID_DUMP_ROOT &&
1057 core_pattern[0] != '/' && core_pattern[0] != '|') {1231 core_pattern[0] != '/' && core_pattern[0] != '|' && core_pattern[0] != '@') {
1058 1232
1059 coredump_report_failure("Unsafe core_pattern used with fs.suid_dumpable=2: "1233 coredump_report_failure("Unsafe core_pattern used with fs.suid_dumpable=2: "
1060 "pipe handler or fully qualified core dump path required. "1234 "pipe handler or fully qualified core dump path required. "
@@ -1062,18 +1236,55 @@ void validate_coredump_safety(void) · 1 unmodified line
1062 }1236 }
1063}1237}
1064 1238
1239static 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
1065static int proc_dostring_coredump(const struct ctl_table *table, int write,1259static int proc_dostring_coredump(const struct ctl_table *table, int write,
1066 void *buffer, size_t *lenp, loff_t *ppos)1260 void *buffer, size_t *lenp, loff_t *ppos)
1067{1261{
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 }
1069 1275
1070 if (!error)1276 validate_coredump_safety();
1071 validate_coredump_safety();
1072 return error;1277 return error;
1073}1278}
1074 1279
1075static const unsigned int core_file_note_size_min = CORE_FILE_NOTE_SIZE_DEFAULT;1280static const unsigned int core_file_note_size_min = CORE_FILE_NOTE_SIZE_DEFAULT;
1076static const unsigned int core_file_note_size_max = CORE_FILE_NOTE_SIZE_MAX;1281static const unsigned int core_file_note_size_max = CORE_FILE_NOTE_SIZE_MAX;
1282static char core_modes[] = {
1283 "file\npipe"
1284#ifdef CONFIG_UNIX
1285 "\nsocket"
1286#endif
1287};
1077 1288
1078static const struct ctl_table coredump_sysctls[] = {1289static const struct ctl_table coredump_sysctls[] = {
1079 {1290 {
@@ -1117,6 +1328,13 @@ static const struct ctl_table coredump_sysctls[] = { · 37 unmodified lines
1117 .extra1 = SYSCTL_ZERO,1328 .extra1 = SYSCTL_ZERO,
1118 .extra2 = SYSCTL_ONE,1329 .extra2 = SYSCTL_ONE,
1119 },1330 },
1331 {
1332 .procname = "core_modes",
1333 .data = core_modes,
1334 .maxlen = sizeof(core_modes) - 1,
1335 .mode = 0444,
1336 .proc_handler = proc_dostring,
1337 },
1120};1338};
1121 1339
1122static int __init init_fs_coredump_sysctls(void)1340static int __init init_fs_coredump_sysctls(void)
fs/pidfs.c+55−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
20#include <linux/time_namespace.h>20#include <linux/time_namespace.h>
21#include <linux/utsname.h>21#include <linux/utsname.h>
22#include <net/net_namespace.h>22#include <net/net_namespace.h>
23#include <linux/coredump.h>
23 24
24#include "internal.h"25#include "internal.h"
25#include "mount.h"26#include "mount.h"
@@ -33,6 +34,7 @@ static struct kmem_cache *pidfs_cachep __ro_after_init; · 7 unmodified lines
33struct pidfs_exit_info {34struct pidfs_exit_info {
34 __u64 cgroupid;35 __u64 cgroupid;
35 __s32 exit_code;36 __s32 exit_code;
37 __u32 coredump_mask;
36};38};
37 39
38struct pidfs_inode {40struct pidfs_inode {
@@ -240,6 +242,22 @@ static inline bool pid_in_current_pidns(const struct pid *pid) · 201 unmodified lines
240 return false;242 return false;
241}243}
242 244
245static __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
243static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)261static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
244{262{
245 struct pidfd_info __user *uinfo = (struct pidfd_info __user *)arg;263 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
280 }298 }
281 }299 }
282 300
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
283 task = get_pid_task(pid, PIDTYPE_PID);306 task = get_pid_task(pid, PIDTYPE_PID);
284 if (!task) {307 if (!task) {
285 /*308 /*
@@ -296,6 +319,13 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg) · 10 unmodified lines
296 if (!c)319 if (!c)
297 return -ESRCH;320 return -ESRCH;
298 321
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
299 /* Unconditionally return identifiers and credentials, the rest only on request */329 /* Unconditionally return identifiers and credentials, the rest only on request */
300 330
301 user_ns = current_user_ns();331 user_ns = current_user_ns();
@@ -559,6 +589,31 @@ void pidfs_exit(struct task_struct *tsk) · 257 unmodified lines
559 }589 }
560}590}
561 591
592#ifdef CONFIG_COREDUMP
593void 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
562static struct vfsmount *pidfs_mnt __ro_after_init;617static struct vfsmount *pidfs_mnt __ro_after_init;
563 618
564/*619/*
include/linux/net.h+2−2
@@ -70,6 +70,7 @@ enum sock_type { · 69 unmodified lines
70 SOCK_DCCP = 6,70 SOCK_DCCP = 6,
71 SOCK_PACKET = 10,71 SOCK_PACKET = 10,
72};72};
73#endif /* ARCH_HAS_SOCKET_TYPES */
73 74
74#define SOCK_MAX (SOCK_PACKET + 1)75#define SOCK_MAX (SOCK_PACKET + 1)
75/* Mask which covers at least up to SOCK_MASK-1. The76/* Mask which covers at least up to SOCK_MASK-1. The
@@ -81,8 +82,7 @@ enum sock_type { · 5 unmodified lines
81#ifndef SOCK_NONBLOCK82#ifndef SOCK_NONBLOCK
82#define SOCK_NONBLOCK O_NONBLOCK83#define SOCK_NONBLOCK O_NONBLOCK
83#endif84#endif
84 85#define SOCK_COREDUMP O_NOCTTY
85#endif /* ARCH_HAS_SOCKET_TYPES */
86 86
87/**87/**
88 * enum sock_shutdown_cmd - Shutdown types88 * enum sock_shutdown_cmd - Shutdown types
include/linux/pidfs.h+5−0
@@ -2,11 +2,16 @@ · 1 unmodified line
2#ifndef _LINUX_PID_FS_H2#ifndef _LINUX_PID_FS_H
3#define _LINUX_PID_FS_H3#define _LINUX_PID_FS_H
4 4
5struct coredump_params;
6
5struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags);7struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags);
6void __init pidfs_init(void);8void __init pidfs_init(void);
7void pidfs_add_pid(struct pid *pid);9void pidfs_add_pid(struct pid *pid);
8void pidfs_remove_pid(struct pid *pid);10void pidfs_remove_pid(struct pid *pid);
9void pidfs_exit(struct task_struct *tsk);11void pidfs_exit(struct task_struct *tsk);
12#ifdef CONFIG_COREDUMP
13void pidfs_coredump(const struct coredump_params *cprm);
14#endif
10extern const struct dentry_operations pidfs_dentry_operations;15extern const struct dentry_operations pidfs_dentry_operations;
11int pidfs_register_pid(struct pid *pid);16int pidfs_register_pid(struct pid *pid);
12void pidfs_get_pid(struct pid *pid);17void pidfs_get_pid(struct pid *pid);
include/uapi/linux/pidfd.h+16−0
@@ -25,9 +25,23 @@ · 24 unmodified lines
25#define PIDFD_INFO_CREDS (1UL << 1) /* Always returned, even if not requested */25#define PIDFD_INFO_CREDS (1UL << 1) /* Always returned, even if not requested */
26#define PIDFD_INFO_CGROUPID (1UL << 2) /* Always returned if available, even if not requested */26#define PIDFD_INFO_CGROUPID (1UL << 2) /* Always returned if available, even if not requested */
27#define PIDFD_INFO_EXIT (1UL << 3) /* Only returned if requested. */27#define PIDFD_INFO_EXIT (1UL << 3) /* Only returned if requested. */
28#define PIDFD_INFO_COREDUMP (1UL << 4) /* Only returned if requested. */
28 29
29#define PIDFD_INFO_SIZE_VER0 64 /* sizeof first published struct */30#define PIDFD_INFO_SIZE_VER0 64 /* sizeof first published struct */
30 31
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
31/*45/*
32 * The concept of process and threads in userland and the kernel is a confusing46 * The concept of process and threads in userland and the kernel is a confusing
33 * one - within the kernel every thread is a 'task' with its own individual PID,47 * one - within the kernel every thread is a 'task' with its own individual PID,
@@ -92,6 +106,8 @@ struct pidfd_info { · 58 unmodified lines
92 __u32 fsuid;106 __u32 fsuid;
93 __u32 fsgid;107 __u32 fsgid;
94 __s32 exit_code;108 __s32 exit_code;
109 __u32 coredump_mask;
110 __u32 __spare1;
95};111};
96 112
97#define PIDFS_IOCTL_MAGIC 0xFF113#define PIDFS_IOCTL_MAGIC 0xFF
net/unix/af_unix.c+41−13
@@ -85,10 +85,13 @@ · 84 unmodified lines
85#include <linux/file.h>85#include <linux/file.h>
86#include <linux/filter.h>86#include <linux/filter.h>
87#include <linux/fs.h>87#include <linux/fs.h>
88#include <linux/fs_struct.h>
88#include <linux/init.h>89#include <linux/init.h>
89#include <linux/kernel.h>90#include <linux/kernel.h>
90#include <linux/mount.h>91#include <linux/mount.h>
91#include <linux/namei.h>92#include <linux/namei.h>
93#include <linux/net.h>
94#include <linux/pidfs.h>
92#include <linux/poll.h>95#include <linux/poll.h>
93#include <linux/proc_fs.h>96#include <linux/proc_fs.h>
94#include <linux/sched/signal.h>97#include <linux/sched/signal.h>
@@ -100,7 +103,6 @@ · 5 unmodified lines
100#include <linux/splice.h>103#include <linux/splice.h>
101#include <linux/string.h>104#include <linux/string.h>
102#include <linux/uaccess.h>105#include <linux/uaccess.h>
103#include <linux/pidfs.h>
104#include <net/af_unix.h>106#include <net/af_unix.h>
105#include <net/net_namespace.h>107#include <net/net_namespace.h>
106#include <net/scm.h>108#include <net/scm.h>
@@ -1146,7 +1148,7 @@ static int unix_release(struct socket *sock) · 1039 unmodified lines
1146}1148}
1147 1149
1148static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,1150static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
1149 int type)1151 int type, int flags)
1150{1152{
1151 struct inode *inode;1153 struct inode *inode;
1152 struct path path;1154 struct path path;
@@ -1154,13 +1156,39 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len, · 1 unmodified line
1154 int err;1156 int err;
1155 1157
1156 unix_mkname_bsd(sunaddr, addr_len);1158 unix_mkname_bsd(sunaddr, addr_len);
1157 err = kern_path(sunaddr->sun_path, LOOKUP_FOLLOW, &path);
1158 if (err)
1159 goto fail;
1160 1159
1161 err = path_permission(&path, MAY_WRITE);1160 if (flags & SOCK_COREDUMP) {
1162 if (err)1161 const struct cred *cred;
1163 goto path_put;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 }
1164 1192
1165 err = -ECONNREFUSED;1193 err = -ECONNREFUSED;
1166 inode = d_backing_inode(path.dentry);1194 inode = d_backing_inode(path.dentry);
@@ -1210,12 +1238,12 @@ static struct sock *unix_find_abstract(struct net *net, · 43 unmodified lines
1210 1238
1211static struct sock *unix_find_other(struct net *net,1239static struct sock *unix_find_other(struct net *net,
1212 struct sockaddr_un *sunaddr,1240 struct sockaddr_un *sunaddr,
1213 int addr_len, int type)1241 int addr_len, int type, int flags)
1214{1242{
1215 struct sock *sk;1243 struct sock *sk;
1216 1244
1217 if (sunaddr->sun_path[0])1245 if (sunaddr->sun_path[0])
1218 sk = unix_find_bsd(sunaddr, addr_len, type);1246 sk = unix_find_bsd(sunaddr, addr_len, type, flags);
1219 else1247 else
1220 sk = unix_find_abstract(net, sunaddr, addr_len, type);1248 sk = unix_find_abstract(net, sunaddr, addr_len, type);
1221 1249
@@ -1473,7 +1501,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, · 251 unmodified lines
1473 }1501 }
1474 1502
1475restart:1503restart:
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);
1477 if (IS_ERR(other)) {1505 if (IS_ERR(other)) {
1478 err = PTR_ERR(other);1506 err = PTR_ERR(other);
1479 goto out;1507 goto out;
@@ -1620,7 +1648,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, · 140 unmodified lines
1620 1648
1621restart:1649restart:
1622 /* Find listening sock. */1650 /* 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);
1624 if (IS_ERR(other)) {1652 if (IS_ERR(other)) {
1625 err = PTR_ERR(other);1653 err = PTR_ERR(other);
1626 goto out_free_skb;1654 goto out_free_skb;
@@ -2089,7 +2117,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, · 462 unmodified lines
2089 if (msg->msg_namelen) {2117 if (msg->msg_namelen) {
2090lookup:2118lookup:
2091 other = unix_find_other(sock_net(sk), msg->msg_name,2119 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);
2093 if (IS_ERR(other)) {2121 if (IS_ERR(other)) {
2094 err = PTR_ERR(other);2122 err = PTR_ERR(other);
2095 goto out_free;2123 goto out_free;
tools/testing/selftests/coredump/stackdump_test.c+466−1
@@ -1,14 +1,20 @@
1// SPDX-License-Identifier: GPL-2.01// SPDX-License-Identifier: GPL-2.0
2 2
3#include <fcntl.h>3#include <fcntl.h>
4#include <inttypes.h>
4#include <libgen.h>5#include <libgen.h>
5#include <linux/limits.h>6#include <linux/limits.h>
6#include <pthread.h>7#include <pthread.h>
7#include <string.h>8#include <string.h>
9#include <sys/mount.h>
8#include <sys/resource.h>10#include <sys/resource.h>
11#include <sys/stat.h>
12#include <sys/socket.h>
13#include <sys/un.h>
9#include <unistd.h>14#include <unistd.h>
10 15
11#include "../kselftest_harness.h"16#include "../kselftest_harness.h"
17#include "../pidfd/pidfd.h"
12 18
13#define STACKDUMP_FILE "stack_values"19#define STACKDUMP_FILE "stack_values"
14#define STACKDUMP_SCRIPT "stackdump"20#define STACKDUMP_SCRIPT "stackdump"
@@ -35,6 +41,7 @@ static void crashing_child(void) · 20 unmodified lines
35FIXTURE(coredump)41FIXTURE(coredump)
36{42{
37 char original_core_pattern[256];43 char original_core_pattern[256];
44 pid_t pid_coredump_server;
38};45};
39 46
40FIXTURE_SETUP(coredump)47FIXTURE_SETUP(coredump)
@@ -44,6 +51,7 @@ FIXTURE_SETUP(coredump) · 3 unmodified lines
44 char *dir;51 char *dir;
45 int ret;52 int ret;
46 53
54 self->pid_coredump_server = -ESRCH;
47 file = fopen("/proc/sys/kernel/core_pattern", "r");55 file = fopen("/proc/sys/kernel/core_pattern", "r");
48 ASSERT_NE(NULL, file);56 ASSERT_NE(NULL, file);
49 57
@@ -61,10 +69,17 @@ FIXTURE_TEARDOWN(coredump) · 11 unmodified lines
61{69{
62 const char *reason;70 const char *reason;
63 FILE *file;71 FILE *file;
64 int ret;72 int ret, status;
65 73
66 unlink(STACKDUMP_FILE);74 unlink(STACKDUMP_FILE);
67 75
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
68 file = fopen("/proc/sys/kernel/core_pattern", "w");83 file = fopen("/proc/sys/kernel/core_pattern", "w");
69 if (!file) {84 if (!file) {
70 reason = "Unable to open core_pattern";85 reason = "Unable to open core_pattern";
@@ -154,4 +169,454 @@ TEST_F_TIMEOUT(coredump, stackdump, 120) · 83 unmodified lines
154 fclose(file);169 fclose(file);
155}170}
156 171
172TEST_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
357TEST_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
517TEST_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
544TEST_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
157TEST_HARNESS_MAIN622TEST_HARNESS_MAIN
tools/testing/selftests/pidfd/pidfd.h+22−0
@@ -131,6 +131,26 @@ · 130 unmodified lines
131#define PIDFD_INFO_EXIT (1UL << 3) /* Always returned if available, even if not requested */131#define PIDFD_INFO_EXIT (1UL << 3) /* Always returned if available, even if not requested */
132#endif132#endif
133 133
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
134#ifndef PIDFD_THREAD154#ifndef PIDFD_THREAD
135#define PIDFD_THREAD O_EXCL155#define PIDFD_THREAD O_EXCL
136#endif156#endif
@@ -150,6 +170,8 @@ struct pidfd_info { · 13 unmodified lines
150 __u32 fsuid;170 __u32 fsuid;
151 __u32 fsgid;171 __u32 fsgid;
152 __s32 exit_code;172 __s32 exit_code;
173 __u32 coredump_mask;
174 __u32 __spare1;
153};175};
154 176
155/*177/*