Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

Pull coredump updates from Christian Brauner:
"This contains an extension to the coredump socket and a proper rework
of the coredump code.

- This extends the coredump socket to allow the coredump server to
tell the kernel how to process individual coredumps. This allows
for fine-grained coredump management. Userspace can decide to just
let the kernel write out the coredump, or generate the coredump
itself, or just reject it.

* COREDUMP_KERNEL
The kernel will write the coredump data to the socket.

* COREDUMP_USERSPACE
The kernel will not write coredump data but will indicate to the
parent that a coredump has been generated. This is used when
userspace generates its own coredumps.

* COREDUMP_REJECT
The kernel will skip generating a coredump for this task.

* COREDUMP_WAIT
The kernel will prevent the task from exiting until the coredump
server has shutdown the socket connection.

The flexible coredump socket can be enabled by using the "@@"
prefix instead of the single "@" prefix for the regular coredump
socket:

@@/run/systemd/coredump.socket

- Cleanup the coredump code properly while we have to touch it
anyway.

Split out each coredump mode in a separate helper so it's easy to
grasp what is going on and make the code easier to follow. The core
coredump function should now be very trivial to follow"

* tag 'vfs-6.17-rc1.coredump' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (31 commits)
cleanup: add a scoped version of CLASS()
coredump: add coredump_skip() helper
coredump: avoid pointless variable
coredump: order auto cleanup variables at the top
coredump: add coredump_cleanup()
coredump: auto cleanup prepare_creds()
cred: add auto cleanup method
coredump: directly return
coredump: auto cleanup argv
coredump: add coredump_write()
coredump: use a single helper for the socket
coredump: move pipe specific file check into coredump_pipe()
coredump: split pipe coredumping into coredump_pipe()
coredump: move core_pipe_count to global variable
coredump: prepare to simplify exit paths
coredump: split file coredumping into coredump_file()
coredump: rename do_coredump() to vfs_coredump()
selftests/coredump: make sure invalid paths are rejected
coredump: validate socket path in coredump_parse()
coredump: don't allow ".." in coredump socket path
...

+2247 -608
+1 -1
Documentation/security/credentials.rst
··· 555 555 different set of credentials. This is done in the following places: 556 556 557 557 * ``sys_faccessat()``. 558 - * ``do_coredump()``. 558 + * ``vfs_coredump()``. 559 559 * nfs4recover.c.
+1 -1
Documentation/translations/zh_CN/security/credentials.rst
··· 475 475 如 ``vfs_mkdir()`` 来实现。以下是一些进行此操作的位置: 476 476 477 477 * ``sys_faccessat()``. 478 - * ``do_coredump()``. 478 + * ``vfs_coredump()``. 479 479 * nfs4recover.c.
+11 -20
drivers/base/firmware_loader/main.c
··· 822 822 {} 823 823 #endif 824 824 825 - /* 826 - * Reject firmware file names with ".." path components. 827 - * There are drivers that construct firmware file names from device-supplied 828 - * strings, and we don't want some device to be able to tell us "I would like to 829 - * be sent my firmware from ../../../etc/shadow, please". 830 - * 831 - * Search for ".." surrounded by either '/' or start/end of string. 832 - * 833 - * This intentionally only looks at the firmware name, not at the firmware base 834 - * directory or at symlink contents. 835 - */ 836 - static bool name_contains_dotdot(const char *name) 837 - { 838 - size_t name_len = strlen(name); 839 - 840 - return strcmp(name, "..") == 0 || strncmp(name, "../", 3) == 0 || 841 - strstr(name, "/../") != NULL || 842 - (name_len >= 3 && strcmp(name+name_len-3, "/..") == 0); 843 - } 844 - 845 825 /* called from request_firmware() and request_firmware_work_func() */ 846 826 static int 847 827 _request_firmware(const struct firmware **firmware_p, const char *name, ··· 842 862 goto out; 843 863 } 844 864 865 + 866 + /* 867 + * Reject firmware file names with ".." path components. 868 + * There are drivers that construct firmware file names from 869 + * device-supplied strings, and we don't want some device to be 870 + * able to tell us "I would like to be sent my firmware from 871 + * ../../../etc/shadow, please". 872 + * 873 + * This intentionally only looks at the firmware name, not at 874 + * the firmware base directory or at symlink contents. 875 + */ 845 876 if (name_contains_dotdot(name)) { 846 877 dev_warn(device, 847 878 "Firmware load for '%s' refused, path contains '..' component\n",
+538 -330
fs/coredump.c
··· 51 51 #include <net/sock.h> 52 52 #include <uapi/linux/pidfd.h> 53 53 #include <uapi/linux/un.h> 54 + #include <uapi/linux/coredump.h> 54 55 55 56 #include <linux/uaccess.h> 56 57 #include <asm/mmu_context.h> ··· 82 81 static char core_pattern[CORENAME_MAX_SIZE] = "core"; 83 82 static int core_name_size = CORENAME_MAX_SIZE; 84 83 unsigned int core_file_note_size_limit = CORE_FILE_NOTE_SIZE_DEFAULT; 84 + static atomic_t core_pipe_count = ATOMIC_INIT(0); 85 85 86 86 enum coredump_type_t { 87 - COREDUMP_FILE = 1, 88 - COREDUMP_PIPE = 2, 89 - COREDUMP_SOCK = 3, 87 + COREDUMP_FILE = 1, 88 + COREDUMP_PIPE = 2, 89 + COREDUMP_SOCK = 3, 90 + COREDUMP_SOCK_REQ = 4, 90 91 }; 91 92 92 93 struct core_name { 93 94 char *corename; 94 95 int used, size; 96 + unsigned int core_pipe_limit; 97 + bool core_dumped; 95 98 enum coredump_type_t core_type; 99 + u64 mask; 96 100 }; 97 101 98 102 static int expand_corename(struct core_name *cn, int size) ··· 228 222 return ret; 229 223 } 230 224 231 - /* format_corename will inspect the pattern parameter, and output a 232 - * name into corename, which must have space for at least 233 - * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator. 225 + /* 226 + * coredump_parse will inspect the pattern parameter, and output a name 227 + * into corename, which must have space for at least CORENAME_MAX_SIZE 228 + * bytes plus one byte for the zero terminator. 234 229 */ 235 - static int format_corename(struct core_name *cn, struct coredump_params *cprm, 230 + static bool coredump_parse(struct core_name *cn, struct coredump_params *cprm, 236 231 size_t **argv, int *argc) 237 232 { 238 233 const struct cred *cred = current_cred(); ··· 242 235 int pid_in_pattern = 0; 243 236 int err = 0; 244 237 238 + cn->mask = COREDUMP_KERNEL; 239 + if (core_pipe_limit) 240 + cn->mask |= COREDUMP_WAIT; 245 241 cn->used = 0; 246 242 cn->corename = NULL; 243 + cn->core_pipe_limit = 0; 244 + cn->core_dumped = false; 247 245 if (*pat_ptr == '|') 248 246 cn->core_type = COREDUMP_PIPE; 249 247 else if (*pat_ptr == '@') ··· 256 244 else 257 245 cn->core_type = COREDUMP_FILE; 258 246 if (expand_corename(cn, core_name_size)) 259 - return -ENOMEM; 247 + return false; 260 248 cn->corename[0] = '\0'; 261 249 262 250 switch (cn->core_type) { ··· 264 252 int argvs = sizeof(core_pattern) / 2; 265 253 (*argv) = kmalloc_array(argvs, sizeof(**argv), GFP_KERNEL); 266 254 if (!(*argv)) 267 - return -ENOMEM; 255 + return false; 268 256 (*argv)[(*argc)++] = 0; 269 257 ++pat_ptr; 270 258 if (!(*pat_ptr)) 271 - return -ENOMEM; 259 + return false; 272 260 break; 273 261 } 274 262 case COREDUMP_SOCK: { 275 263 /* skip the @ */ 276 264 pat_ptr++; 277 265 if (!(*pat_ptr)) 278 - return -ENOMEM; 266 + return false; 267 + if (*pat_ptr == '@') { 268 + pat_ptr++; 269 + if (!(*pat_ptr)) 270 + return false; 271 + 272 + cn->core_type = COREDUMP_SOCK_REQ; 273 + } 279 274 280 275 err = cn_printf(cn, "%s", pat_ptr); 281 276 if (err) 282 - return err; 277 + return false; 283 278 284 279 /* Require absolute paths. */ 285 280 if (cn->corename[0] != '/') 286 - return -EINVAL; 281 + return false; 287 282 288 283 /* 289 284 * Ensure we can uses spaces to indicate additional ··· 298 279 */ 299 280 if (strchr(cn->corename, ' ')) { 300 281 coredump_report_failure("Coredump socket may not %s contain spaces", cn->corename); 301 - return -EINVAL; 282 + return false; 283 + } 284 + 285 + /* Must not contain ".." in the path. */ 286 + if (name_contains_dotdot(cn->corename)) { 287 + coredump_report_failure("Coredump socket may not %s contain '..' spaces", cn->corename); 288 + return false; 289 + } 290 + 291 + if (strlen(cn->corename) >= UNIX_PATH_MAX) { 292 + coredump_report_failure("Coredump socket path %s too long", cn->corename); 293 + return false; 302 294 } 303 295 304 296 /* ··· 319 289 * via /proc/<pid>, using the SO_PEERPIDFD to guard 320 290 * against pid recycling when opening /proc/<pid>. 321 291 */ 322 - return 0; 292 + return true; 323 293 } 324 294 case COREDUMP_FILE: 325 295 break; 326 296 default: 327 297 WARN_ON_ONCE(true); 328 - return -EINVAL; 298 + return false; 329 299 } 330 300 331 301 /* Repeat as long as we have more pattern to process and more output ··· 463 433 } 464 434 465 435 if (err) 466 - return err; 436 + return false; 467 437 } 468 438 469 439 out: ··· 473 443 * and core_uses_pid is set, then .%pid will be appended to 474 444 * the filename. Do not do this for piped commands. */ 475 445 if (cn->core_type == COREDUMP_FILE && !pid_in_pattern && core_uses_pid) 476 - return cn_printf(cn, ".%d", task_tgid_vnr(current)); 446 + return cn_printf(cn, ".%d", task_tgid_vnr(current)) == 0; 477 447 478 - return 0; 448 + return true; 479 449 } 480 450 481 451 static int zap_process(struct signal_struct *signal, int exit_code) ··· 662 632 return 0; 663 633 } 664 634 665 - void do_coredump(const kernel_siginfo_t *siginfo) 635 + #ifdef CONFIG_UNIX 636 + static bool coredump_sock_connect(struct core_name *cn, struct coredump_params *cprm) 666 637 { 638 + struct file *file __free(fput) = NULL; 639 + struct sockaddr_un addr = { 640 + .sun_family = AF_UNIX, 641 + }; 642 + ssize_t addr_len; 643 + int retval; 644 + struct socket *socket; 645 + 646 + addr_len = strscpy(addr.sun_path, cn->corename); 647 + if (addr_len < 0) 648 + return false; 649 + addr_len += offsetof(struct sockaddr_un, sun_path) + 1; 650 + 651 + /* 652 + * It is possible that the userspace process which is supposed 653 + * to handle the coredump and is listening on the AF_UNIX socket 654 + * coredumps. Userspace should just mark itself non dumpable. 655 + */ 656 + 657 + retval = sock_create_kern(&init_net, AF_UNIX, SOCK_STREAM, 0, &socket); 658 + if (retval < 0) 659 + return false; 660 + 661 + file = sock_alloc_file(socket, 0, NULL); 662 + if (IS_ERR(file)) 663 + return false; 664 + 665 + /* 666 + * Set the thread-group leader pid which is used for the peer 667 + * credentials during connect() below. Then immediately register 668 + * it in pidfs... 669 + */ 670 + cprm->pid = task_tgid(current); 671 + retval = pidfs_register_pid(cprm->pid); 672 + if (retval) 673 + return false; 674 + 675 + /* 676 + * ... and set the coredump information so userspace has it 677 + * available after connect()... 678 + */ 679 + pidfs_coredump(cprm); 680 + 681 + retval = kernel_connect(socket, (struct sockaddr *)(&addr), addr_len, 682 + O_NONBLOCK | SOCK_COREDUMP); 683 + /* 684 + * ... Make sure to only put our reference after connect() took 685 + * its own reference keeping the pidfs entry alive ... 686 + */ 687 + pidfs_put_pid(cprm->pid); 688 + 689 + if (retval) { 690 + if (retval == -EAGAIN) 691 + coredump_report_failure("Coredump socket %s receive queue full", addr.sun_path); 692 + else 693 + coredump_report_failure("Coredump socket connection %s failed %d", addr.sun_path, retval); 694 + return false; 695 + } 696 + 697 + /* ... and validate that @sk_peer_pid matches @cprm.pid. */ 698 + if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid != cprm->pid)) 699 + return false; 700 + 701 + cprm->limit = RLIM_INFINITY; 702 + cprm->file = no_free_ptr(file); 703 + 704 + return true; 705 + } 706 + 707 + static inline bool coredump_sock_recv(struct file *file, struct coredump_ack *ack, size_t size, int flags) 708 + { 709 + struct msghdr msg = {}; 710 + struct kvec iov = { .iov_base = ack, .iov_len = size }; 711 + ssize_t ret; 712 + 713 + memset(ack, 0, size); 714 + ret = kernel_recvmsg(sock_from_file(file), &msg, &iov, 1, size, flags); 715 + return ret == size; 716 + } 717 + 718 + static inline bool coredump_sock_send(struct file *file, struct coredump_req *req) 719 + { 720 + struct msghdr msg = { .msg_flags = MSG_NOSIGNAL }; 721 + struct kvec iov = { .iov_base = req, .iov_len = sizeof(*req) }; 722 + ssize_t ret; 723 + 724 + ret = kernel_sendmsg(sock_from_file(file), &msg, &iov, 1, sizeof(*req)); 725 + return ret == sizeof(*req); 726 + } 727 + 728 + static_assert(sizeof(enum coredump_mark) == sizeof(__u32)); 729 + 730 + static inline bool coredump_sock_mark(struct file *file, enum coredump_mark mark) 731 + { 732 + struct msghdr msg = { .msg_flags = MSG_NOSIGNAL }; 733 + struct kvec iov = { .iov_base = &mark, .iov_len = sizeof(mark) }; 734 + ssize_t ret; 735 + 736 + ret = kernel_sendmsg(sock_from_file(file), &msg, &iov, 1, sizeof(mark)); 737 + return ret == sizeof(mark); 738 + } 739 + 740 + static inline void coredump_sock_wait(struct file *file) 741 + { 742 + ssize_t n; 743 + 744 + /* 745 + * We use a simple read to wait for the coredump processing to 746 + * finish. Either the socket is closed or we get sent unexpected 747 + * data. In both cases, we're done. 748 + */ 749 + n = __kernel_read(file, &(char){ 0 }, 1, NULL); 750 + if (n > 0) 751 + coredump_report_failure("Coredump socket had unexpected data"); 752 + else if (n < 0) 753 + coredump_report_failure("Coredump socket failed"); 754 + } 755 + 756 + static inline void coredump_sock_shutdown(struct file *file) 757 + { 758 + struct socket *socket; 759 + 760 + socket = sock_from_file(file); 761 + if (!socket) 762 + return; 763 + 764 + /* Let userspace know we're done processing the coredump. */ 765 + kernel_sock_shutdown(socket, SHUT_WR); 766 + } 767 + 768 + static bool coredump_sock_request(struct core_name *cn, struct coredump_params *cprm) 769 + { 770 + struct coredump_req req = { 771 + .size = sizeof(struct coredump_req), 772 + .mask = COREDUMP_KERNEL | COREDUMP_USERSPACE | 773 + COREDUMP_REJECT | COREDUMP_WAIT, 774 + .size_ack = sizeof(struct coredump_ack), 775 + }; 776 + struct coredump_ack ack = {}; 777 + ssize_t usize; 778 + 779 + if (cn->core_type != COREDUMP_SOCK_REQ) 780 + return true; 781 + 782 + /* Let userspace know what we support. */ 783 + if (!coredump_sock_send(cprm->file, &req)) 784 + return false; 785 + 786 + /* Peek the size of the coredump_ack. */ 787 + if (!coredump_sock_recv(cprm->file, &ack, sizeof(ack.size), 788 + MSG_PEEK | MSG_WAITALL)) 789 + return false; 790 + 791 + /* Refuse unknown coredump_ack sizes. */ 792 + usize = ack.size; 793 + if (usize < COREDUMP_ACK_SIZE_VER0) { 794 + coredump_sock_mark(cprm->file, COREDUMP_MARK_MINSIZE); 795 + return false; 796 + } 797 + 798 + if (usize > sizeof(ack)) { 799 + coredump_sock_mark(cprm->file, COREDUMP_MARK_MAXSIZE); 800 + return false; 801 + } 802 + 803 + /* Now retrieve the coredump_ack. */ 804 + if (!coredump_sock_recv(cprm->file, &ack, usize, MSG_WAITALL)) 805 + return false; 806 + if (ack.size != usize) 807 + return false; 808 + 809 + /* Refuse unknown coredump_ack flags. */ 810 + if (ack.mask & ~req.mask) { 811 + coredump_sock_mark(cprm->file, COREDUMP_MARK_UNSUPPORTED); 812 + return false; 813 + } 814 + 815 + /* Refuse mutually exclusive options. */ 816 + if (hweight64(ack.mask & (COREDUMP_USERSPACE | COREDUMP_KERNEL | 817 + COREDUMP_REJECT)) != 1) { 818 + coredump_sock_mark(cprm->file, COREDUMP_MARK_CONFLICTING); 819 + return false; 820 + } 821 + 822 + if (ack.spare) { 823 + coredump_sock_mark(cprm->file, COREDUMP_MARK_UNSUPPORTED); 824 + return false; 825 + } 826 + 827 + cn->mask = ack.mask; 828 + return coredump_sock_mark(cprm->file, COREDUMP_MARK_REQACK); 829 + } 830 + 831 + static bool coredump_socket(struct core_name *cn, struct coredump_params *cprm) 832 + { 833 + if (!coredump_sock_connect(cn, cprm)) 834 + return false; 835 + 836 + return coredump_sock_request(cn, cprm); 837 + } 838 + #else 839 + static inline void coredump_sock_wait(struct file *file) { } 840 + static inline void coredump_sock_shutdown(struct file *file) { } 841 + static inline bool coredump_socket(struct core_name *cn, struct coredump_params *cprm) { return false; } 842 + #endif 843 + 844 + /* cprm->mm_flags contains a stable snapshot of dumpability flags. */ 845 + static inline bool coredump_force_suid_safe(const struct coredump_params *cprm) 846 + { 847 + /* Require nonrelative corefile path and be extra careful. */ 848 + return __get_dumpable(cprm->mm_flags) == SUID_DUMP_ROOT; 849 + } 850 + 851 + static bool coredump_file(struct core_name *cn, struct coredump_params *cprm, 852 + const struct linux_binfmt *binfmt) 853 + { 854 + struct mnt_idmap *idmap; 855 + struct inode *inode; 856 + struct file *file __free(fput) = NULL; 857 + int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW | O_LARGEFILE | O_EXCL; 858 + 859 + if (cprm->limit < binfmt->min_coredump) 860 + return false; 861 + 862 + if (coredump_force_suid_safe(cprm) && cn->corename[0] != '/') { 863 + coredump_report_failure("this process can only dump core to a fully qualified path, skipping core dump"); 864 + return false; 865 + } 866 + 867 + /* 868 + * Unlink the file if it exists unless this is a SUID 869 + * binary - in that case, we're running around with root 870 + * privs and don't want to unlink another user's coredump. 871 + */ 872 + if (!coredump_force_suid_safe(cprm)) { 873 + /* 874 + * If it doesn't exist, that's fine. If there's some 875 + * other problem, we'll catch it at the filp_open(). 876 + */ 877 + do_unlinkat(AT_FDCWD, getname_kernel(cn->corename)); 878 + } 879 + 880 + /* 881 + * There is a race between unlinking and creating the 882 + * file, but if that causes an EEXIST here, that's 883 + * fine - another process raced with us while creating 884 + * the corefile, and the other process won. To userspace, 885 + * what matters is that at least one of the two processes 886 + * writes its coredump successfully, not which one. 887 + */ 888 + if (coredump_force_suid_safe(cprm)) { 889 + /* 890 + * Using user namespaces, normal user tasks can change 891 + * their current->fs->root to point to arbitrary 892 + * directories. Since the intention of the "only dump 893 + * with a fully qualified path" rule is to control where 894 + * coredumps may be placed using root privileges, 895 + * current->fs->root must not be used. Instead, use the 896 + * root directory of init_task. 897 + */ 898 + struct path root; 899 + 900 + task_lock(&init_task); 901 + get_fs_root(init_task.fs, &root); 902 + task_unlock(&init_task); 903 + file = file_open_root(&root, cn->corename, open_flags, 0600); 904 + path_put(&root); 905 + } else { 906 + file = filp_open(cn->corename, open_flags, 0600); 907 + } 908 + if (IS_ERR(file)) 909 + return false; 910 + 911 + inode = file_inode(file); 912 + if (inode->i_nlink > 1) 913 + return false; 914 + if (d_unhashed(file->f_path.dentry)) 915 + return false; 916 + /* 917 + * AK: actually i see no reason to not allow this for named 918 + * pipes etc, but keep the previous behaviour for now. 919 + */ 920 + if (!S_ISREG(inode->i_mode)) 921 + return false; 922 + /* 923 + * Don't dump core if the filesystem changed owner or mode 924 + * of the file during file creation. This is an issue when 925 + * a process dumps core while its cwd is e.g. on a vfat 926 + * filesystem. 927 + */ 928 + idmap = file_mnt_idmap(file); 929 + if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) { 930 + coredump_report_failure("Core dump to %s aborted: cannot preserve file owner", cn->corename); 931 + return false; 932 + } 933 + if ((inode->i_mode & 0677) != 0600) { 934 + coredump_report_failure("Core dump to %s aborted: cannot preserve file permissions", cn->corename); 935 + return false; 936 + } 937 + if (!(file->f_mode & FMODE_CAN_WRITE)) 938 + return false; 939 + if (do_truncate(idmap, file->f_path.dentry, 0, 0, file)) 940 + return false; 941 + 942 + cprm->file = no_free_ptr(file); 943 + return true; 944 + } 945 + 946 + static bool coredump_pipe(struct core_name *cn, struct coredump_params *cprm, 947 + size_t *argv, int argc) 948 + { 949 + int argi; 950 + char **helper_argv __free(kfree) = NULL; 951 + struct subprocess_info *sub_info; 952 + 953 + if (cprm->limit == 1) { 954 + /* See umh_coredump_setup() which sets RLIMIT_CORE = 1. 955 + * 956 + * Normally core limits are irrelevant to pipes, since 957 + * we're not writing to the file system, but we use 958 + * cprm.limit of 1 here as a special value, this is a 959 + * consistent way to catch recursive crashes. 960 + * We can still crash if the core_pattern binary sets 961 + * RLIM_CORE = !1, but it runs as root, and can do 962 + * lots of stupid things. 963 + * 964 + * Note that we use task_tgid_vnr here to grab the pid 965 + * of the process group leader. That way we get the 966 + * right pid if a thread in a multi-threaded 967 + * core_pattern process dies. 968 + */ 969 + coredump_report_failure("RLIMIT_CORE is set to 1, aborting core"); 970 + return false; 971 + } 972 + cprm->limit = RLIM_INFINITY; 973 + 974 + cn->core_pipe_limit = atomic_inc_return(&core_pipe_count); 975 + if (core_pipe_limit && (core_pipe_limit < cn->core_pipe_limit)) { 976 + coredump_report_failure("over core_pipe_limit, skipping core dump"); 977 + return false; 978 + } 979 + 980 + helper_argv = kmalloc_array(argc + 1, sizeof(*helper_argv), GFP_KERNEL); 981 + if (!helper_argv) { 982 + coredump_report_failure("%s failed to allocate memory", __func__); 983 + return false; 984 + } 985 + for (argi = 0; argi < argc; argi++) 986 + helper_argv[argi] = cn->corename + argv[argi]; 987 + helper_argv[argi] = NULL; 988 + 989 + sub_info = call_usermodehelper_setup(helper_argv[0], helper_argv, NULL, 990 + GFP_KERNEL, umh_coredump_setup, 991 + NULL, cprm); 992 + if (!sub_info) 993 + return false; 994 + 995 + if (call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC)) { 996 + coredump_report_failure("|%s pipe failed", cn->corename); 997 + return false; 998 + } 999 + 1000 + /* 1001 + * umh disabled with CONFIG_STATIC_USERMODEHELPER_PATH="" would 1002 + * have this set to NULL. 1003 + */ 1004 + if (!cprm->file) { 1005 + coredump_report_failure("Core dump to |%s disabled", cn->corename); 1006 + return false; 1007 + } 1008 + 1009 + return true; 1010 + } 1011 + 1012 + static bool coredump_write(struct core_name *cn, 1013 + struct coredump_params *cprm, 1014 + struct linux_binfmt *binfmt) 1015 + { 1016 + 1017 + if (dump_interrupted()) 1018 + return true; 1019 + 1020 + if (!dump_vma_snapshot(cprm)) 1021 + return false; 1022 + 1023 + file_start_write(cprm->file); 1024 + cn->core_dumped = binfmt->core_dump(cprm); 1025 + /* 1026 + * Ensures that file size is big enough to contain the current 1027 + * file postion. This prevents gdb from complaining about 1028 + * a truncated file if the last "write" to the file was 1029 + * dump_skip. 1030 + */ 1031 + if (cprm->to_skip) { 1032 + cprm->to_skip--; 1033 + dump_emit(cprm, "", 1); 1034 + } 1035 + file_end_write(cprm->file); 1036 + free_vma_snapshot(cprm); 1037 + return true; 1038 + } 1039 + 1040 + static void coredump_cleanup(struct core_name *cn, struct coredump_params *cprm) 1041 + { 1042 + if (cprm->file) 1043 + filp_close(cprm->file, NULL); 1044 + if (cn->core_pipe_limit) { 1045 + VFS_WARN_ON_ONCE(cn->core_type != COREDUMP_PIPE); 1046 + atomic_dec(&core_pipe_count); 1047 + } 1048 + kfree(cn->corename); 1049 + coredump_finish(cn->core_dumped); 1050 + } 1051 + 1052 + static inline bool coredump_skip(const struct coredump_params *cprm, 1053 + const struct linux_binfmt *binfmt) 1054 + { 1055 + if (!binfmt) 1056 + return true; 1057 + if (!binfmt->core_dump) 1058 + return true; 1059 + if (!__get_dumpable(cprm->mm_flags)) 1060 + return true; 1061 + return false; 1062 + } 1063 + 1064 + void vfs_coredump(const kernel_siginfo_t *siginfo) 1065 + { 1066 + struct cred *cred __free(put_cred) = NULL; 1067 + size_t *argv __free(kfree) = NULL; 667 1068 struct core_state core_state; 668 1069 struct core_name cn; 669 1070 struct mm_struct *mm = current->mm; 670 - struct linux_binfmt * binfmt; 1071 + struct linux_binfmt *binfmt = mm->binfmt; 671 1072 const struct cred *old_cred; 672 - struct cred *cred; 673 - int retval = 0; 674 - size_t *argv = NULL; 675 1073 int argc = 0; 676 - /* require nonrelative corefile path and be extra careful */ 677 - bool need_suid_safe = false; 678 - bool core_dumped = false; 679 - static atomic_t core_dump_count = ATOMIC_INIT(0); 680 1074 struct coredump_params cprm = { 681 1075 .siginfo = siginfo, 682 1076 .limit = rlimit(RLIMIT_CORE), ··· 1116 662 1117 663 audit_core_dumps(siginfo->si_signo); 1118 664 1119 - binfmt = mm->binfmt; 1120 - if (!binfmt || !binfmt->core_dump) 1121 - goto fail; 1122 - if (!__get_dumpable(cprm.mm_flags)) 1123 - goto fail; 665 + if (coredump_skip(&cprm, binfmt)) 666 + return; 1124 667 1125 668 cred = prepare_creds(); 1126 669 if (!cred) 1127 - goto fail; 670 + return; 1128 671 /* 1129 672 * We cannot trust fsuid as being the "true" uid of the process 1130 673 * nor do we know its entire history. We only know it was tainted 1131 674 * so we dump it as root in mode 2, and only into a controlled 1132 675 * environment (pipe handler or fully qualified path). 1133 676 */ 1134 - if (__get_dumpable(cprm.mm_flags) == SUID_DUMP_ROOT) { 1135 - /* Setuid core dump mode */ 1136 - cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ 1137 - need_suid_safe = true; 1138 - } 677 + if (coredump_force_suid_safe(&cprm)) 678 + cred->fsuid = GLOBAL_ROOT_UID; 1139 679 1140 - retval = coredump_wait(siginfo->si_signo, &core_state); 1141 - if (retval < 0) 1142 - goto fail_creds; 680 + if (coredump_wait(siginfo->si_signo, &core_state) < 0) 681 + return; 1143 682 1144 683 old_cred = override_creds(cred); 1145 684 1146 - retval = format_corename(&cn, &cprm, &argv, &argc); 1147 - if (retval < 0) { 685 + if (!coredump_parse(&cn, &cprm, &argv, &argc)) { 1148 686 coredump_report_failure("format_corename failed, aborting core"); 1149 - goto fail_unlock; 687 + goto close_fail; 1150 688 } 1151 689 1152 690 switch (cn.core_type) { 1153 - case COREDUMP_FILE: { 1154 - struct mnt_idmap *idmap; 1155 - struct inode *inode; 1156 - int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW | 1157 - O_LARGEFILE | O_EXCL; 1158 - 1159 - if (cprm.limit < binfmt->min_coredump) 1160 - goto fail_unlock; 1161 - 1162 - if (need_suid_safe && cn.corename[0] != '/') { 1163 - coredump_report_failure( 1164 - "this process can only dump core to a fully qualified path, skipping core dump"); 1165 - goto fail_unlock; 1166 - } 1167 - 1168 - /* 1169 - * Unlink the file if it exists unless this is a SUID 1170 - * binary - in that case, we're running around with root 1171 - * privs and don't want to unlink another user's coredump. 1172 - */ 1173 - if (!need_suid_safe) { 1174 - /* 1175 - * If it doesn't exist, that's fine. If there's some 1176 - * other problem, we'll catch it at the filp_open(). 1177 - */ 1178 - do_unlinkat(AT_FDCWD, getname_kernel(cn.corename)); 1179 - } 1180 - 1181 - /* 1182 - * There is a race between unlinking and creating the 1183 - * file, but if that causes an EEXIST here, that's 1184 - * fine - another process raced with us while creating 1185 - * the corefile, and the other process won. To userspace, 1186 - * what matters is that at least one of the two processes 1187 - * writes its coredump successfully, not which one. 1188 - */ 1189 - if (need_suid_safe) { 1190 - /* 1191 - * Using user namespaces, normal user tasks can change 1192 - * their current->fs->root to point to arbitrary 1193 - * directories. Since the intention of the "only dump 1194 - * with a fully qualified path" rule is to control where 1195 - * coredumps may be placed using root privileges, 1196 - * current->fs->root must not be used. Instead, use the 1197 - * root directory of init_task. 1198 - */ 1199 - struct path root; 1200 - 1201 - task_lock(&init_task); 1202 - get_fs_root(init_task.fs, &root); 1203 - task_unlock(&init_task); 1204 - cprm.file = file_open_root(&root, cn.corename, 1205 - open_flags, 0600); 1206 - path_put(&root); 1207 - } else { 1208 - cprm.file = filp_open(cn.corename, open_flags, 0600); 1209 - } 1210 - if (IS_ERR(cprm.file)) 1211 - goto fail_unlock; 1212 - 1213 - inode = file_inode(cprm.file); 1214 - if (inode->i_nlink > 1) 1215 - goto close_fail; 1216 - if (d_unhashed(cprm.file->f_path.dentry)) 1217 - goto close_fail; 1218 - /* 1219 - * AK: actually i see no reason to not allow this for named 1220 - * pipes etc, but keep the previous behaviour for now. 1221 - */ 1222 - if (!S_ISREG(inode->i_mode)) 1223 - goto close_fail; 1224 - /* 1225 - * Don't dump core if the filesystem changed owner or mode 1226 - * of the file during file creation. This is an issue when 1227 - * a process dumps core while its cwd is e.g. on a vfat 1228 - * filesystem. 1229 - */ 1230 - idmap = file_mnt_idmap(cprm.file); 1231 - if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), 1232 - current_fsuid())) { 1233 - coredump_report_failure("Core dump to %s aborted: " 1234 - "cannot preserve file owner", cn.corename); 1235 - goto close_fail; 1236 - } 1237 - if ((inode->i_mode & 0677) != 0600) { 1238 - coredump_report_failure("Core dump to %s aborted: " 1239 - "cannot preserve file permissions", cn.corename); 1240 - goto close_fail; 1241 - } 1242 - if (!(cprm.file->f_mode & FMODE_CAN_WRITE)) 1243 - goto close_fail; 1244 - if (do_truncate(idmap, cprm.file->f_path.dentry, 1245 - 0, 0, cprm.file)) 691 + case COREDUMP_FILE: 692 + if (!coredump_file(&cn, &cprm, binfmt)) 1246 693 goto close_fail; 1247 694 break; 1248 - } 1249 - case COREDUMP_PIPE: { 1250 - int argi; 1251 - int dump_count; 1252 - char **helper_argv; 1253 - struct subprocess_info *sub_info; 1254 - 1255 - if (cprm.limit == 1) { 1256 - /* See umh_coredump_setup() which sets RLIMIT_CORE = 1. 1257 - * 1258 - * Normally core limits are irrelevant to pipes, since 1259 - * we're not writing to the file system, but we use 1260 - * cprm.limit of 1 here as a special value, this is a 1261 - * consistent way to catch recursive crashes. 1262 - * We can still crash if the core_pattern binary sets 1263 - * RLIM_CORE = !1, but it runs as root, and can do 1264 - * lots of stupid things. 1265 - * 1266 - * Note that we use task_tgid_vnr here to grab the pid 1267 - * of the process group leader. That way we get the 1268 - * right pid if a thread in a multi-threaded 1269 - * core_pattern process dies. 1270 - */ 1271 - coredump_report_failure("RLIMIT_CORE is set to 1, aborting core"); 1272 - goto fail_unlock; 1273 - } 1274 - cprm.limit = RLIM_INFINITY; 1275 - 1276 - dump_count = atomic_inc_return(&core_dump_count); 1277 - if (core_pipe_limit && (core_pipe_limit < dump_count)) { 1278 - coredump_report_failure("over core_pipe_limit, skipping core dump"); 1279 - goto fail_dropcount; 1280 - } 1281 - 1282 - helper_argv = kmalloc_array(argc + 1, sizeof(*helper_argv), 1283 - GFP_KERNEL); 1284 - if (!helper_argv) { 1285 - coredump_report_failure("%s failed to allocate memory", __func__); 1286 - goto fail_dropcount; 1287 - } 1288 - for (argi = 0; argi < argc; argi++) 1289 - helper_argv[argi] = cn.corename + argv[argi]; 1290 - helper_argv[argi] = NULL; 1291 - 1292 - retval = -ENOMEM; 1293 - sub_info = call_usermodehelper_setup(helper_argv[0], 1294 - helper_argv, NULL, GFP_KERNEL, 1295 - umh_coredump_setup, NULL, &cprm); 1296 - if (sub_info) 1297 - retval = call_usermodehelper_exec(sub_info, 1298 - UMH_WAIT_EXEC); 1299 - 1300 - kfree(helper_argv); 1301 - if (retval) { 1302 - coredump_report_failure("|%s pipe failed", cn.corename); 695 + case COREDUMP_PIPE: 696 + if (!coredump_pipe(&cn, &cprm, argv, argc)) 1303 697 goto close_fail; 1304 - } 1305 698 break; 1306 - } 1307 - case COREDUMP_SOCK: { 1308 - #ifdef CONFIG_UNIX 1309 - struct file *file __free(fput) = NULL; 1310 - struct sockaddr_un addr = { 1311 - .sun_family = AF_UNIX, 1312 - }; 1313 - ssize_t addr_len; 1314 - struct socket *socket; 1315 - 1316 - addr_len = strscpy(addr.sun_path, cn.corename); 1317 - if (addr_len < 0) 699 + case COREDUMP_SOCK_REQ: 700 + fallthrough; 701 + case COREDUMP_SOCK: 702 + if (!coredump_socket(&cn, &cprm)) 1318 703 goto close_fail; 1319 - addr_len += offsetof(struct sockaddr_un, sun_path) + 1; 1320 - 1321 - /* 1322 - * It is possible that the userspace process which is 1323 - * supposed to handle the coredump and is listening on 1324 - * the AF_UNIX socket coredumps. Userspace should just 1325 - * mark itself non dumpable. 1326 - */ 1327 - 1328 - retval = sock_create_kern(&init_net, AF_UNIX, SOCK_STREAM, 0, &socket); 1329 - if (retval < 0) 1330 - goto close_fail; 1331 - 1332 - file = sock_alloc_file(socket, 0, NULL); 1333 - if (IS_ERR(file)) 1334 - goto close_fail; 1335 - 1336 - /* 1337 - * Set the thread-group leader pid which is used for the 1338 - * peer credentials during connect() below. Then 1339 - * immediately register it in pidfs... 1340 - */ 1341 - cprm.pid = task_tgid(current); 1342 - retval = pidfs_register_pid(cprm.pid); 1343 - if (retval) 1344 - goto close_fail; 1345 - 1346 - /* 1347 - * ... and set the coredump information so userspace 1348 - * has it available after connect()... 1349 - */ 1350 - pidfs_coredump(&cprm); 1351 - 1352 - retval = kernel_connect(socket, (struct sockaddr *)(&addr), 1353 - addr_len, O_NONBLOCK | SOCK_COREDUMP); 1354 - 1355 - /* 1356 - * ... Make sure to only put our reference after connect() took 1357 - * its own reference keeping the pidfs entry alive ... 1358 - */ 1359 - pidfs_put_pid(cprm.pid); 1360 - 1361 - if (retval) { 1362 - if (retval == -EAGAIN) 1363 - coredump_report_failure("Coredump socket %s receive queue full", addr.sun_path); 1364 - else 1365 - coredump_report_failure("Coredump socket connection %s failed %d", addr.sun_path, retval); 1366 - goto close_fail; 1367 - } 1368 - 1369 - /* ... and validate that @sk_peer_pid matches @cprm.pid. */ 1370 - if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid != cprm.pid)) 1371 - goto close_fail; 1372 - 1373 - cprm.limit = RLIM_INFINITY; 1374 - cprm.file = no_free_ptr(file); 1375 - #else 1376 - coredump_report_failure("Core dump socket support %s disabled", cn.corename); 1377 - goto close_fail; 1378 - #endif 1379 704 break; 1380 - } 1381 705 default: 1382 706 WARN_ON_ONCE(true); 1383 707 goto close_fail; 1384 708 } 1385 709 710 + /* Don't even generate the coredump. */ 711 + if (cn.mask & COREDUMP_REJECT) 712 + goto close_fail; 713 + 1386 714 /* get us an unshared descriptor table; almost always a no-op */ 1387 715 /* The cell spufs coredump code reads the file descriptor tables */ 1388 - retval = unshare_files(); 1389 - if (retval) 716 + if (unshare_files()) 1390 717 goto close_fail; 1391 - if (!dump_interrupted()) { 1392 - /* 1393 - * umh disabled with CONFIG_STATIC_USERMODEHELPER_PATH="" would 1394 - * have this set to NULL. 1395 - */ 1396 - if (!cprm.file) { 1397 - coredump_report_failure("Core dump to |%s disabled", cn.corename); 1398 - goto close_fail; 1399 - } 1400 - if (!dump_vma_snapshot(&cprm)) 1401 - goto close_fail; 1402 718 1403 - file_start_write(cprm.file); 1404 - core_dumped = binfmt->core_dump(&cprm); 1405 - /* 1406 - * Ensures that file size is big enough to contain the current 1407 - * file postion. This prevents gdb from complaining about 1408 - * a truncated file if the last "write" to the file was 1409 - * dump_skip. 1410 - */ 1411 - if (cprm.to_skip) { 1412 - cprm.to_skip--; 1413 - dump_emit(&cprm, "", 1); 1414 - } 1415 - file_end_write(cprm.file); 1416 - free_vma_snapshot(&cprm); 1417 - } 719 + if ((cn.mask & COREDUMP_KERNEL) && !coredump_write(&cn, &cprm, binfmt)) 720 + goto close_fail; 1418 721 1419 - #ifdef CONFIG_UNIX 1420 - /* Let userspace know we're done processing the coredump. */ 1421 - if (sock_from_file(cprm.file)) 1422 - kernel_sock_shutdown(sock_from_file(cprm.file), SHUT_WR); 1423 - #endif 722 + coredump_sock_shutdown(cprm.file); 723 + 724 + /* Let the parent know that a coredump was generated. */ 725 + if (cn.mask & COREDUMP_USERSPACE) 726 + cn.core_dumped = true; 1424 727 1425 728 /* 1426 729 * When core_pipe_limit is set we wait for the coredump server 1427 730 * or usermodehelper to finish before exiting so it can e.g., 1428 731 * inspect /proc/<pid>. 1429 732 */ 1430 - if (core_pipe_limit) { 733 + if (cn.mask & COREDUMP_WAIT) { 1431 734 switch (cn.core_type) { 1432 735 case COREDUMP_PIPE: 1433 736 wait_for_dump_helpers(cprm.file); 1434 737 break; 1435 - #ifdef CONFIG_UNIX 1436 - case COREDUMP_SOCK: { 1437 - ssize_t n; 1438 - 1439 - /* 1440 - * We use a simple read to wait for the coredump 1441 - * processing to finish. Either the socket is 1442 - * closed or we get sent unexpected data. In 1443 - * both cases, we're done. 1444 - */ 1445 - n = __kernel_read(cprm.file, &(char){ 0 }, 1, NULL); 1446 - if (n != 0) 1447 - coredump_report_failure("Unexpected data on coredump socket"); 738 + case COREDUMP_SOCK_REQ: 739 + fallthrough; 740 + case COREDUMP_SOCK: 741 + coredump_sock_wait(cprm.file); 1448 742 break; 1449 - } 1450 - #endif 1451 743 default: 1452 744 break; 1453 745 } 1454 746 } 1455 747 1456 748 close_fail: 1457 - if (cprm.file) 1458 - filp_close(cprm.file, NULL); 1459 - fail_dropcount: 1460 - if (cn.core_type == COREDUMP_PIPE) 1461 - atomic_dec(&core_dump_count); 1462 - fail_unlock: 1463 - kfree(argv); 1464 - kfree(cn.corename); 1465 - coredump_finish(core_dumped); 749 + coredump_cleanup(&cn, &cprm); 1466 750 revert_creds(old_cred); 1467 - fail_creds: 1468 - put_cred(cred); 1469 - fail: 1470 751 return; 1471 752 } 1472 753 ··· 1427 1238 1428 1239 static inline bool check_coredump_socket(void) 1429 1240 { 1241 + const char *p; 1242 + 1430 1243 if (core_pattern[0] != '@') 1431 1244 return true; 1432 1245 ··· 1440 1249 if (current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) 1441 1250 return false; 1442 1251 1443 - /* Must be an absolute path. */ 1444 - if (*(core_pattern + 1) != '/') 1252 + /* Must be an absolute path... */ 1253 + if (core_pattern[1] != '/') { 1254 + /* ... or the socket request protocol... */ 1255 + if (core_pattern[1] != '@') 1256 + return false; 1257 + /* ... and if so must be an absolute path. */ 1258 + if (core_pattern[2] != '/') 1259 + return false; 1260 + p = &core_pattern[2]; 1261 + } else { 1262 + p = &core_pattern[1]; 1263 + } 1264 + 1265 + /* The path obviously cannot exceed UNIX_PATH_MAX. */ 1266 + if (strlen(p) >= UNIX_PATH_MAX) 1267 + return false; 1268 + 1269 + /* Must not contain ".." in the path. */ 1270 + if (name_contains_dotdot(core_pattern)) 1445 1271 return false; 1446 1272 1447 1273 return true;
+8
include/linux/cleanup.h
··· 277 277 class_##_name##_t var __cleanup(class_##_name##_destructor) = \ 278 278 class_##_name##_constructor 279 279 280 + #define scoped_class(_name, var, args) \ 281 + for (CLASS(_name, var)(args); \ 282 + __guard_ptr(_name)(&var) || !__is_cond_ptr(_name); \ 283 + ({ goto _label; })) \ 284 + if (0) { \ 285 + _label: \ 286 + break; \ 287 + } else 280 288 281 289 /* 282 290 * DEFINE_GUARD(name, type, lock, unlock):
+2 -2
include/linux/coredump.h
··· 43 43 extern int dump_align(struct coredump_params *cprm, int align); 44 44 int dump_user_range(struct coredump_params *cprm, unsigned long start, 45 45 unsigned long len); 46 - extern void do_coredump(const kernel_siginfo_t *siginfo); 46 + extern void vfs_coredump(const kernel_siginfo_t *siginfo); 47 47 48 48 /* 49 49 * Logging for the coredump code, ratelimited. ··· 63 63 #define coredump_report_failure(fmt, ...) __COREDUMP_PRINTK(KERN_WARNING, fmt, ##__VA_ARGS__) 64 64 65 65 #else 66 - static inline void do_coredump(const kernel_siginfo_t *siginfo) {} 66 + static inline void vfs_coredump(const kernel_siginfo_t *siginfo) {} 67 67 68 68 #define coredump_report(...) 69 69 #define coredump_report_failure(...)
+2
include/linux/cred.h
··· 263 263 put_cred_many(cred, 1); 264 264 } 265 265 266 + DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_NULL(_T)) put_cred(_T)) 267 + 266 268 /** 267 269 * current_cred - Access the current task's subjective credentials 268 270 *
+16
include/linux/fs.h
··· 3269 3269 (len == 1 || (len == 2 && name[1] == '.')); 3270 3270 } 3271 3271 3272 + /** 3273 + * name_contains_dotdot - check if a file name contains ".." path components 3274 + * 3275 + * Search for ".." surrounded by either '/' or start/end of string. 3276 + */ 3277 + static inline bool name_contains_dotdot(const char *name) 3278 + { 3279 + size_t name_len; 3280 + 3281 + name_len = strlen(name); 3282 + return strcmp(name, "..") == 0 || 3283 + strncmp(name, "../", 3) == 0 || 3284 + strstr(name, "/../") != NULL || 3285 + (name_len >= 3 && strcmp(name + name_len - 3, "/..") == 0); 3286 + } 3287 + 3272 3288 #include <linux/err.h> 3273 3289 3274 3290 /* needed for stackable file system support */
+104
include/uapi/linux/coredump.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + 3 + #ifndef _UAPI_LINUX_COREDUMP_H 4 + #define _UAPI_LINUX_COREDUMP_H 5 + 6 + #include <linux/types.h> 7 + 8 + /** 9 + * coredump_{req,ack} flags 10 + * @COREDUMP_KERNEL: kernel writes coredump 11 + * @COREDUMP_USERSPACE: userspace writes coredump 12 + * @COREDUMP_REJECT: don't generate coredump 13 + * @COREDUMP_WAIT: wait for coredump server 14 + */ 15 + enum { 16 + COREDUMP_KERNEL = (1ULL << 0), 17 + COREDUMP_USERSPACE = (1ULL << 1), 18 + COREDUMP_REJECT = (1ULL << 2), 19 + COREDUMP_WAIT = (1ULL << 3), 20 + }; 21 + 22 + /** 23 + * struct coredump_req - message kernel sends to userspace 24 + * @size: size of struct coredump_req 25 + * @size_ack: known size of struct coredump_ack on this kernel 26 + * @mask: supported features 27 + * 28 + * When a coredump happens the kernel will connect to the coredump 29 + * socket and send a coredump request to the coredump server. The @size 30 + * member is set to the size of struct coredump_req and provides a hint 31 + * to userspace how much data can be read. Userspace may use MSG_PEEK to 32 + * peek the size of struct coredump_req and then choose to consume it in 33 + * one go. Userspace may also simply read a COREDUMP_ACK_SIZE_VER0 34 + * request. If the size the kernel sends is larger userspace simply 35 + * discards any remaining data. 36 + * 37 + * The coredump_req->mask member is set to the currently know features. 38 + * Userspace may only set coredump_ack->mask to the bits raised by the 39 + * kernel in coredump_req->mask. 40 + * 41 + * The coredump_req->size_ack member is set by the kernel to the size of 42 + * struct coredump_ack the kernel knows. Userspace may only send up to 43 + * coredump_req->size_ack bytes to the kernel and must set 44 + * coredump_ack->size accordingly. 45 + */ 46 + struct coredump_req { 47 + __u32 size; 48 + __u32 size_ack; 49 + __u64 mask; 50 + }; 51 + 52 + enum { 53 + COREDUMP_REQ_SIZE_VER0 = 16U, /* size of first published struct */ 54 + }; 55 + 56 + /** 57 + * struct coredump_ack - message userspace sends to kernel 58 + * @size: size of the struct 59 + * @spare: unused 60 + * @mask: features kernel is supposed to use 61 + * 62 + * The @size member must be set to the size of struct coredump_ack. It 63 + * may never exceed what the kernel returned in coredump_req->size_ack 64 + * but it may of course be smaller (>= COREDUMP_ACK_SIZE_VER0 and <= 65 + * coredump_req->size_ack). 66 + * 67 + * The @mask member must be set to the features the coredump server 68 + * wants the kernel to use. Only bits the kernel returned in 69 + * coredump_req->mask may be set. 70 + */ 71 + struct coredump_ack { 72 + __u32 size; 73 + __u32 spare; 74 + __u64 mask; 75 + }; 76 + 77 + enum { 78 + COREDUMP_ACK_SIZE_VER0 = 16U, /* size of first published struct */ 79 + }; 80 + 81 + /** 82 + * enum coredump_mark - Markers for the coredump socket 83 + * 84 + * The kernel will place a single byte on the coredump socket. The 85 + * markers notify userspace whether the coredump ack succeeded or 86 + * failed. 87 + * 88 + * @COREDUMP_MARK_MINSIZE: the provided coredump_ack size was too small 89 + * @COREDUMP_MARK_MAXSIZE: the provided coredump_ack size was too big 90 + * @COREDUMP_MARK_UNSUPPORTED: the provided coredump_ack mask was invalid 91 + * @COREDUMP_MARK_CONFLICTING: the provided coredump_ack mask has conflicting options 92 + * @COREDUMP_MARK_REQACK: the coredump request and ack was successful 93 + * @__COREDUMP_MARK_MAX: the maximum coredump mark value 94 + */ 95 + enum coredump_mark { 96 + COREDUMP_MARK_REQACK = 0U, 97 + COREDUMP_MARK_MINSIZE = 1U, 98 + COREDUMP_MARK_MAXSIZE = 2U, 99 + COREDUMP_MARK_UNSUPPORTED = 3U, 100 + COREDUMP_MARK_CONFLICTING = 4U, 101 + __COREDUMP_MARK_MAX = (1U << 31), 102 + }; 103 + 104 + #endif /* _UAPI_LINUX_COREDUMP_H */
+1 -1
kernel/signal.c
··· 3016 3016 * first and our do_group_exit call below will use 3017 3017 * that value and ignore the one we pass it. 3018 3018 */ 3019 - do_coredump(&ksig->info); 3019 + vfs_coredump(&ksig->info); 3020 3020 } 3021 3021 3022 3022 /*
+104
tools/include/uapi/linux/coredump.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + 3 + #ifndef _UAPI_LINUX_COREDUMP_H 4 + #define _UAPI_LINUX_COREDUMP_H 5 + 6 + #include <linux/types.h> 7 + 8 + /** 9 + * coredump_{req,ack} flags 10 + * @COREDUMP_KERNEL: kernel writes coredump 11 + * @COREDUMP_USERSPACE: userspace writes coredump 12 + * @COREDUMP_REJECT: don't generate coredump 13 + * @COREDUMP_WAIT: wait for coredump server 14 + */ 15 + enum { 16 + COREDUMP_KERNEL = (1ULL << 0), 17 + COREDUMP_USERSPACE = (1ULL << 1), 18 + COREDUMP_REJECT = (1ULL << 2), 19 + COREDUMP_WAIT = (1ULL << 3), 20 + }; 21 + 22 + /** 23 + * struct coredump_req - message kernel sends to userspace 24 + * @size: size of struct coredump_req 25 + * @size_ack: known size of struct coredump_ack on this kernel 26 + * @mask: supported features 27 + * 28 + * When a coredump happens the kernel will connect to the coredump 29 + * socket and send a coredump request to the coredump server. The @size 30 + * member is set to the size of struct coredump_req and provides a hint 31 + * to userspace how much data can be read. Userspace may use MSG_PEEK to 32 + * peek the size of struct coredump_req and then choose to consume it in 33 + * one go. Userspace may also simply read a COREDUMP_ACK_SIZE_VER0 34 + * request. If the size the kernel sends is larger userspace simply 35 + * discards any remaining data. 36 + * 37 + * The coredump_req->mask member is set to the currently know features. 38 + * Userspace may only set coredump_ack->mask to the bits raised by the 39 + * kernel in coredump_req->mask. 40 + * 41 + * The coredump_req->size_ack member is set by the kernel to the size of 42 + * struct coredump_ack the kernel knows. Userspace may only send up to 43 + * coredump_req->size_ack bytes to the kernel and must set 44 + * coredump_ack->size accordingly. 45 + */ 46 + struct coredump_req { 47 + __u32 size; 48 + __u32 size_ack; 49 + __u64 mask; 50 + }; 51 + 52 + enum { 53 + COREDUMP_REQ_SIZE_VER0 = 16U, /* size of first published struct */ 54 + }; 55 + 56 + /** 57 + * struct coredump_ack - message userspace sends to kernel 58 + * @size: size of the struct 59 + * @spare: unused 60 + * @mask: features kernel is supposed to use 61 + * 62 + * The @size member must be set to the size of struct coredump_ack. It 63 + * may never exceed what the kernel returned in coredump_req->size_ack 64 + * but it may of course be smaller (>= COREDUMP_ACK_SIZE_VER0 and <= 65 + * coredump_req->size_ack). 66 + * 67 + * The @mask member must be set to the features the coredump server 68 + * wants the kernel to use. Only bits the kernel returned in 69 + * coredump_req->mask may be set. 70 + */ 71 + struct coredump_ack { 72 + __u32 size; 73 + __u32 spare; 74 + __u64 mask; 75 + }; 76 + 77 + enum { 78 + COREDUMP_ACK_SIZE_VER0 = 16U, /* size of first published struct */ 79 + }; 80 + 81 + /** 82 + * enum coredump_mark - Markers for the coredump socket 83 + * 84 + * The kernel will place a single byte on the coredump socket. The 85 + * markers notify userspace whether the coredump ack succeeded or 86 + * failed. 87 + * 88 + * @COREDUMP_MARK_MINSIZE: the provided coredump_ack size was too small 89 + * @COREDUMP_MARK_MAXSIZE: the provided coredump_ack size was too big 90 + * @COREDUMP_MARK_UNSUPPORTED: the provided coredump_ack mask was invalid 91 + * @COREDUMP_MARK_CONFLICTING: the provided coredump_ack mask has conflicting options 92 + * @COREDUMP_MARK_REQACK: the coredump request and ack was successful 93 + * @__COREDUMP_MARK_MAX: the maximum coredump mark value 94 + */ 95 + enum coredump_mark { 96 + COREDUMP_MARK_REQACK = 0U, 97 + COREDUMP_MARK_MINSIZE = 1U, 98 + COREDUMP_MARK_MAXSIZE = 2U, 99 + COREDUMP_MARK_UNSUPPORTED = 3U, 100 + COREDUMP_MARK_CONFLICTING = 4U, 101 + __COREDUMP_MARK_MAX = (1U << 31), 102 + }; 103 + 104 + #endif /* _UAPI_LINUX_COREDUMP_H */
+1 -1
tools/testing/selftests/coredump/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 - CFLAGS = $(KHDR_INCLUDES) 2 + CFLAGS += -Wall -O0 -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) 3 3 4 4 TEST_GEN_PROGS := stackdump_test 5 5 TEST_FILES := stackdump
+3
tools/testing/selftests/coredump/config
··· 1 + CONFIG_COREDUMP=y 2 + CONFIG_NET=y 3 + CONFIG_UNIX=y
+1455 -252
tools/testing/selftests/coredump/stackdump_test.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 + #include <assert.h> 3 4 #include <fcntl.h> 4 5 #include <inttypes.h> 5 6 #include <libgen.h> 7 + #include <limits.h> 8 + #include <linux/coredump.h> 9 + #include <linux/fs.h> 6 10 #include <linux/limits.h> 7 11 #include <pthread.h> 8 12 #include <string.h> 9 13 #include <sys/mount.h> 14 + #include <poll.h> 15 + #include <sys/epoll.h> 10 16 #include <sys/resource.h> 11 17 #include <sys/stat.h> 12 18 #include <sys/socket.h> ··· 20 14 #include <unistd.h> 21 15 22 16 #include "../kselftest_harness.h" 17 + #include "../filesystems/wrappers.h" 23 18 #include "../pidfd/pidfd.h" 24 19 25 20 #define STACKDUMP_FILE "stack_values" 26 21 #define STACKDUMP_SCRIPT "stackdump" 27 22 #define NUM_THREAD_SPAWN 128 28 23 24 + #ifndef PAGE_SIZE 25 + #define PAGE_SIZE 4096 26 + #endif 27 + 29 28 static void *do_nothing(void *) 30 29 { 31 30 while (1) 32 31 pause(); 32 + 33 + return NULL; 33 34 } 34 35 35 36 static void crashing_child(void) ··· 55 42 { 56 43 char original_core_pattern[256]; 57 44 pid_t pid_coredump_server; 45 + int fd_tmpfs_detached; 58 46 }; 47 + 48 + static int create_detached_tmpfs(void) 49 + { 50 + int fd_context, fd_tmpfs; 51 + 52 + fd_context = sys_fsopen("tmpfs", 0); 53 + if (fd_context < 0) 54 + return -1; 55 + 56 + if (sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) 57 + return -1; 58 + 59 + fd_tmpfs = sys_fsmount(fd_context, 0, 0); 60 + close(fd_context); 61 + return fd_tmpfs; 62 + } 59 63 60 64 FIXTURE_SETUP(coredump) 61 65 { 62 - char buf[PATH_MAX]; 63 66 FILE *file; 64 - char *dir; 65 67 int ret; 66 68 67 69 self->pid_coredump_server = -ESRCH; 70 + self->fd_tmpfs_detached = -1; 68 71 file = fopen("/proc/sys/kernel/core_pattern", "r"); 69 72 ASSERT_NE(NULL, file); 70 73 ··· 89 60 ASSERT_LT(ret, sizeof(self->original_core_pattern)); 90 61 91 62 self->original_core_pattern[ret] = '\0'; 63 + self->fd_tmpfs_detached = create_detached_tmpfs(); 64 + ASSERT_GE(self->fd_tmpfs_detached, 0); 92 65 93 66 ret = fclose(file); 94 67 ASSERT_EQ(0, ret); ··· 129 98 goto fail; 130 99 } 131 100 101 + if (self->fd_tmpfs_detached >= 0) { 102 + ret = close(self->fd_tmpfs_detached); 103 + if (ret < 0) { 104 + reason = "Unable to close detached tmpfs"; 105 + goto fail; 106 + } 107 + self->fd_tmpfs_detached = -1; 108 + } 109 + 132 110 return; 133 111 fail: 134 112 /* This should never happen */ ··· 146 106 147 107 TEST_F_TIMEOUT(coredump, stackdump, 120) 148 108 { 149 - struct sigaction action = {}; 150 109 unsigned long long stack; 151 110 char *test_dir, *line; 152 111 size_t line_length; 153 - char buf[PATH_MAX]; 112 + char buf[PAGE_SIZE]; 154 113 int ret, i, status; 155 114 FILE *file; 156 115 pid_t pid; ··· 208 169 fclose(file); 209 170 } 210 171 172 + static int create_and_listen_unix_socket(const char *path) 173 + { 174 + struct sockaddr_un addr = { 175 + .sun_family = AF_UNIX, 176 + }; 177 + assert(strlen(path) < sizeof(addr.sun_path) - 1); 178 + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 179 + size_t addr_len = 180 + offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1; 181 + int fd, ret; 182 + 183 + fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 184 + if (fd < 0) 185 + goto out; 186 + 187 + ret = bind(fd, (const struct sockaddr *)&addr, addr_len); 188 + if (ret < 0) 189 + goto out; 190 + 191 + ret = listen(fd, 128); 192 + if (ret < 0) 193 + goto out; 194 + 195 + return fd; 196 + 197 + out: 198 + if (fd >= 0) 199 + close(fd); 200 + return -1; 201 + } 202 + 203 + static bool set_core_pattern(const char *pattern) 204 + { 205 + int fd; 206 + ssize_t ret; 207 + 208 + fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC); 209 + if (fd < 0) 210 + return false; 211 + 212 + ret = write(fd, pattern, strlen(pattern)); 213 + close(fd); 214 + if (ret < 0) 215 + return false; 216 + 217 + fprintf(stderr, "Set core_pattern to '%s' | %zu == %zu\n", pattern, ret, strlen(pattern)); 218 + return ret == strlen(pattern); 219 + } 220 + 221 + static int get_peer_pidfd(int fd) 222 + { 223 + int fd_peer_pidfd; 224 + socklen_t fd_peer_pidfd_len = sizeof(fd_peer_pidfd); 225 + int ret = getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &fd_peer_pidfd, 226 + &fd_peer_pidfd_len); 227 + if (ret < 0) { 228 + fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); 229 + return -1; 230 + } 231 + return fd_peer_pidfd; 232 + } 233 + 234 + static bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info) 235 + { 236 + memset(info, 0, sizeof(*info)); 237 + info->mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 238 + return ioctl(fd_peer_pidfd, PIDFD_GET_INFO, info) == 0; 239 + } 240 + 241 + static void 242 + wait_and_check_coredump_server(pid_t pid_coredump_server, 243 + struct __test_metadata *const _metadata, 244 + FIXTURE_DATA(coredump)* self) 245 + { 246 + int status; 247 + waitpid(pid_coredump_server, &status, 0); 248 + self->pid_coredump_server = -ESRCH; 249 + ASSERT_TRUE(WIFEXITED(status)); 250 + ASSERT_EQ(WEXITSTATUS(status), 0); 251 + } 252 + 211 253 TEST_F(coredump, socket) 212 254 { 213 - int fd, pidfd, ret, status; 214 - FILE *file; 255 + int pidfd, ret, status; 215 256 pid_t pid, pid_coredump_server; 216 257 struct stat st; 217 - char core_file[PATH_MAX]; 218 258 struct pidfd_info info = {}; 219 259 int ipc_sockets[2]; 220 260 char c; 221 - const struct sockaddr_un coredump_sk = { 222 - .sun_family = AF_UNIX, 223 - .sun_path = "/tmp/coredump.socket", 224 - }; 225 - size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 226 - sizeof("/tmp/coredump.socket"); 261 + 262 + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); 227 263 228 264 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 229 265 ASSERT_EQ(ret, 0); 230 266 231 - file = fopen("/proc/sys/kernel/core_pattern", "w"); 232 - ASSERT_NE(file, NULL); 233 - 234 - ret = fprintf(file, "@/tmp/coredump.socket"); 235 - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 236 - ASSERT_EQ(fclose(file), 0); 237 - 238 267 pid_coredump_server = fork(); 239 268 ASSERT_GE(pid_coredump_server, 0); 240 269 if (pid_coredump_server == 0) { 241 - int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; 242 - socklen_t fd_peer_pidfd_len; 270 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1; 271 + int exit_code = EXIT_FAILURE; 243 272 244 273 close(ipc_sockets[0]); 245 274 246 - fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 275 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 247 276 if (fd_server < 0) 248 - _exit(EXIT_FAILURE); 277 + goto out; 249 278 250 - ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 251 - if (ret < 0) { 252 - fprintf(stderr, "Failed to bind coredump socket\n"); 253 - close(fd_server); 254 - close(ipc_sockets[1]); 255 - _exit(EXIT_FAILURE); 256 - } 257 - 258 - ret = listen(fd_server, 1); 259 - if (ret < 0) { 260 - fprintf(stderr, "Failed to listen on coredump socket\n"); 261 - close(fd_server); 262 - close(ipc_sockets[1]); 263 - _exit(EXIT_FAILURE); 264 - } 265 - 266 - if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 267 - close(fd_server); 268 - close(ipc_sockets[1]); 269 - _exit(EXIT_FAILURE); 270 - } 279 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 280 + goto out; 271 281 272 282 close(ipc_sockets[1]); 273 283 274 284 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 275 - if (fd_coredump < 0) { 276 - fprintf(stderr, "Failed to accept coredump socket connection\n"); 277 - close(fd_server); 278 - _exit(EXIT_FAILURE); 279 - } 285 + if (fd_coredump < 0) 286 + goto out; 280 287 281 - fd_peer_pidfd_len = sizeof(fd_peer_pidfd); 282 - ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, 283 - &fd_peer_pidfd, &fd_peer_pidfd_len); 284 - if (ret < 0) { 285 - fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); 286 - close(fd_coredump); 287 - close(fd_server); 288 - _exit(EXIT_FAILURE); 289 - } 288 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 289 + if (fd_peer_pidfd < 0) 290 + goto out; 290 291 291 - memset(&info, 0, sizeof(info)); 292 - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 293 - ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); 294 - if (ret < 0) { 295 - fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); 296 - close(fd_coredump); 297 - close(fd_server); 298 - close(fd_peer_pidfd); 299 - _exit(EXIT_FAILURE); 300 - } 292 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 293 + goto out; 301 294 302 - if (!(info.mask & PIDFD_INFO_COREDUMP)) { 303 - fprintf(stderr, "Missing coredump information from coredumping task\n"); 304 - close(fd_coredump); 305 - close(fd_server); 306 - close(fd_peer_pidfd); 307 - _exit(EXIT_FAILURE); 308 - } 295 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 296 + goto out; 309 297 310 - if (!(info.coredump_mask & PIDFD_COREDUMPED)) { 311 - fprintf(stderr, "Received connection from non-coredumping task\n"); 312 - close(fd_coredump); 313 - close(fd_server); 314 - close(fd_peer_pidfd); 315 - _exit(EXIT_FAILURE); 316 - } 298 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 299 + goto out; 317 300 318 301 fd_core_file = creat("/tmp/coredump.file", 0644); 319 - if (fd_core_file < 0) { 320 - fprintf(stderr, "Failed to create coredump file\n"); 321 - close(fd_coredump); 322 - close(fd_server); 323 - close(fd_peer_pidfd); 324 - _exit(EXIT_FAILURE); 325 - } 302 + if (fd_core_file < 0) 303 + goto out; 326 304 327 305 for (;;) { 328 306 char buffer[4096]; 329 307 ssize_t bytes_read, bytes_write; 330 308 331 309 bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 332 - if (bytes_read < 0) { 333 - close(fd_coredump); 334 - close(fd_server); 335 - close(fd_peer_pidfd); 336 - close(fd_core_file); 337 - _exit(EXIT_FAILURE); 338 - } 310 + if (bytes_read < 0) 311 + goto out; 339 312 340 313 if (bytes_read == 0) 341 314 break; 342 315 343 316 bytes_write = write(fd_core_file, buffer, bytes_read); 344 - if (bytes_read != bytes_write) { 345 - close(fd_coredump); 346 - close(fd_server); 347 - close(fd_peer_pidfd); 348 - close(fd_core_file); 349 - _exit(EXIT_FAILURE); 350 - } 317 + if (bytes_read != bytes_write) 318 + goto out; 351 319 } 352 320 353 - close(fd_coredump); 354 - close(fd_server); 355 - close(fd_peer_pidfd); 356 - close(fd_core_file); 357 - _exit(EXIT_SUCCESS); 321 + exit_code = EXIT_SUCCESS; 322 + out: 323 + if (fd_core_file >= 0) 324 + close(fd_core_file); 325 + if (fd_peer_pidfd >= 0) 326 + close(fd_peer_pidfd); 327 + if (fd_coredump >= 0) 328 + close(fd_coredump); 329 + if (fd_server >= 0) 330 + close(fd_server); 331 + _exit(exit_code); 358 332 } 359 333 self->pid_coredump_server = pid_coredump_server; 360 334 ··· 387 335 ASSERT_TRUE(WIFSIGNALED(status)); 388 336 ASSERT_TRUE(WCOREDUMP(status)); 389 337 390 - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 391 - ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); 338 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 392 339 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 393 340 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 394 341 395 - waitpid(pid_coredump_server, &status, 0); 396 - self->pid_coredump_server = -ESRCH; 397 - ASSERT_TRUE(WIFEXITED(status)); 398 - ASSERT_EQ(WEXITSTATUS(status), 0); 342 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 399 343 400 344 ASSERT_EQ(stat("/tmp/coredump.file", &st), 0); 401 345 ASSERT_GT(st.st_size, 0); 402 - /* 403 - * We should somehow validate the produced core file. 404 - * For now just allow for visual inspection 405 - */ 406 346 system("file /tmp/coredump.file"); 407 347 } 408 348 409 349 TEST_F(coredump, socket_detect_userspace_client) 410 350 { 411 - int fd, pidfd, ret, status; 412 - FILE *file; 351 + int pidfd, ret, status; 413 352 pid_t pid, pid_coredump_server; 414 353 struct stat st; 415 - char core_file[PATH_MAX]; 416 354 struct pidfd_info info = {}; 417 355 int ipc_sockets[2]; 418 356 char c; 419 - const struct sockaddr_un coredump_sk = { 420 - .sun_family = AF_UNIX, 421 - .sun_path = "/tmp/coredump.socket", 422 - }; 423 - size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 424 - sizeof("/tmp/coredump.socket"); 425 357 426 - file = fopen("/proc/sys/kernel/core_pattern", "w"); 427 - ASSERT_NE(file, NULL); 428 - 429 - ret = fprintf(file, "@/tmp/coredump.socket"); 430 - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 431 - ASSERT_EQ(fclose(file), 0); 358 + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); 432 359 433 360 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 434 361 ASSERT_EQ(ret, 0); ··· 415 384 pid_coredump_server = fork(); 416 385 ASSERT_GE(pid_coredump_server, 0); 417 386 if (pid_coredump_server == 0) { 418 - int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; 419 - socklen_t fd_peer_pidfd_len; 387 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; 388 + int exit_code = EXIT_FAILURE; 420 389 421 390 close(ipc_sockets[0]); 422 391 423 - fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 392 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 424 393 if (fd_server < 0) 425 - _exit(EXIT_FAILURE); 394 + goto out; 426 395 427 - ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 428 - if (ret < 0) { 429 - fprintf(stderr, "Failed to bind coredump socket\n"); 430 - close(fd_server); 431 - close(ipc_sockets[1]); 432 - _exit(EXIT_FAILURE); 433 - } 434 - 435 - ret = listen(fd_server, 1); 436 - if (ret < 0) { 437 - fprintf(stderr, "Failed to listen on coredump socket\n"); 438 - close(fd_server); 439 - close(ipc_sockets[1]); 440 - _exit(EXIT_FAILURE); 441 - } 442 - 443 - if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 444 - close(fd_server); 445 - close(ipc_sockets[1]); 446 - _exit(EXIT_FAILURE); 447 - } 396 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 397 + goto out; 448 398 449 399 close(ipc_sockets[1]); 450 400 451 401 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 452 - if (fd_coredump < 0) { 453 - fprintf(stderr, "Failed to accept coredump socket connection\n"); 454 - close(fd_server); 455 - _exit(EXIT_FAILURE); 456 - } 402 + if (fd_coredump < 0) 403 + goto out; 457 404 458 - fd_peer_pidfd_len = sizeof(fd_peer_pidfd); 459 - ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, 460 - &fd_peer_pidfd, &fd_peer_pidfd_len); 461 - if (ret < 0) { 462 - fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); 463 - close(fd_coredump); 464 - close(fd_server); 465 - _exit(EXIT_FAILURE); 466 - } 405 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 406 + if (fd_peer_pidfd < 0) 407 + goto out; 467 408 468 - memset(&info, 0, sizeof(info)); 469 - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 470 - ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); 471 - if (ret < 0) { 472 - fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); 473 - close(fd_coredump); 474 - close(fd_server); 409 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 410 + goto out; 411 + 412 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 413 + goto out; 414 + 415 + if (info.coredump_mask & PIDFD_COREDUMPED) 416 + goto out; 417 + 418 + if (read(fd_coredump, &c, 1) < 1) 419 + goto out; 420 + 421 + exit_code = EXIT_SUCCESS; 422 + out: 423 + if (fd_peer_pidfd >= 0) 475 424 close(fd_peer_pidfd); 476 - _exit(EXIT_FAILURE); 477 - } 478 - 479 - if (!(info.mask & PIDFD_INFO_COREDUMP)) { 480 - fprintf(stderr, "Missing coredump information from coredumping task\n"); 425 + if (fd_coredump >= 0) 481 426 close(fd_coredump); 427 + if (fd_server >= 0) 482 428 close(fd_server); 483 - close(fd_peer_pidfd); 484 - _exit(EXIT_FAILURE); 485 - } 486 - 487 - if (info.coredump_mask & PIDFD_COREDUMPED) { 488 - fprintf(stderr, "Received unexpected connection from coredumping task\n"); 489 - close(fd_coredump); 490 - close(fd_server); 491 - close(fd_peer_pidfd); 492 - _exit(EXIT_FAILURE); 493 - } 494 - 495 - ret = read(fd_coredump, &c, 1); 496 - 497 - close(fd_coredump); 498 - close(fd_server); 499 - close(fd_peer_pidfd); 500 - close(fd_core_file); 501 - 502 - if (ret < 1) 503 - _exit(EXIT_FAILURE); 504 - _exit(EXIT_SUCCESS); 429 + _exit(exit_code); 505 430 } 506 431 self->pid_coredump_server = pid_coredump_server; 507 432 ··· 470 483 if (pid == 0) { 471 484 int fd_socket; 472 485 ssize_t ret; 486 + const struct sockaddr_un coredump_sk = { 487 + .sun_family = AF_UNIX, 488 + .sun_path = "/tmp/coredump.socket", 489 + }; 490 + size_t coredump_sk_len = 491 + offsetof(struct sockaddr_un, sun_path) + 492 + sizeof("/tmp/coredump.socket"); 473 493 474 494 fd_socket = socket(AF_UNIX, SOCK_STREAM, 0); 475 495 if (fd_socket < 0) 476 496 _exit(EXIT_FAILURE); 477 497 478 - 479 498 ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 480 499 if (ret < 0) 481 500 _exit(EXIT_FAILURE); 482 501 483 - (void *)write(fd_socket, &(char){ 0 }, 1); 484 502 close(fd_socket); 485 503 _exit(EXIT_SUCCESS); 486 504 } ··· 497 505 ASSERT_TRUE(WIFEXITED(status)); 498 506 ASSERT_EQ(WEXITSTATUS(status), 0); 499 507 500 - info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 501 - ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); 508 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 502 509 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 503 510 ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0); 504 511 505 - waitpid(pid_coredump_server, &status, 0); 506 - self->pid_coredump_server = -ESRCH; 507 - ASSERT_TRUE(WIFEXITED(status)); 508 - ASSERT_EQ(WEXITSTATUS(status), 0); 512 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 509 513 510 514 ASSERT_NE(stat("/tmp/coredump.file", &st), 0); 511 515 ASSERT_EQ(errno, ENOENT); ··· 509 521 510 522 TEST_F(coredump, socket_enoent) 511 523 { 512 - int pidfd, ret, status; 513 - FILE *file; 524 + int pidfd, status; 514 525 pid_t pid; 515 - char core_file[PATH_MAX]; 516 526 517 - file = fopen("/proc/sys/kernel/core_pattern", "w"); 518 - ASSERT_NE(file, NULL); 519 - 520 - ret = fprintf(file, "@/tmp/coredump.socket"); 521 - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 522 - ASSERT_EQ(fclose(file), 0); 527 + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); 523 528 524 529 pid = fork(); 525 530 ASSERT_GE(pid, 0); ··· 530 549 TEST_F(coredump, socket_no_listener) 531 550 { 532 551 int pidfd, ret, status; 533 - FILE *file; 534 552 pid_t pid, pid_coredump_server; 535 553 int ipc_sockets[2]; 536 554 char c; ··· 540 560 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 541 561 sizeof("/tmp/coredump.socket"); 542 562 563 + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); 564 + 543 565 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 544 566 ASSERT_EQ(ret, 0); 545 - 546 - file = fopen("/proc/sys/kernel/core_pattern", "w"); 547 - ASSERT_NE(file, NULL); 548 - 549 - ret = fprintf(file, "@/tmp/coredump.socket"); 550 - ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 551 - ASSERT_EQ(fclose(file), 0); 552 567 553 568 pid_coredump_server = fork(); 554 569 ASSERT_GE(pid_coredump_server, 0); 555 570 if (pid_coredump_server == 0) { 556 - int fd_server; 557 - socklen_t fd_peer_pidfd_len; 571 + int fd_server = -1; 572 + int exit_code = EXIT_FAILURE; 558 573 559 574 close(ipc_sockets[0]); 560 575 561 576 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 562 577 if (fd_server < 0) 563 - _exit(EXIT_FAILURE); 578 + goto out; 564 579 565 580 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 566 - if (ret < 0) { 567 - fprintf(stderr, "Failed to bind coredump socket\n"); 568 - close(fd_server); 569 - close(ipc_sockets[1]); 570 - _exit(EXIT_FAILURE); 571 - } 581 + if (ret < 0) 582 + goto out; 572 583 573 - if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 574 - close(fd_server); 575 - close(ipc_sockets[1]); 576 - _exit(EXIT_FAILURE); 577 - } 584 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 585 + goto out; 578 586 579 - close(fd_server); 587 + exit_code = EXIT_SUCCESS; 588 + out: 589 + if (fd_server >= 0) 590 + close(fd_server); 580 591 close(ipc_sockets[1]); 581 - _exit(EXIT_SUCCESS); 592 + _exit(exit_code); 582 593 } 583 594 self->pid_coredump_server = pid_coredump_server; 584 595 ··· 589 618 ASSERT_TRUE(WIFSIGNALED(status)); 590 619 ASSERT_FALSE(WCOREDUMP(status)); 591 620 592 - waitpid(pid_coredump_server, &status, 0); 593 - self->pid_coredump_server = -ESRCH; 594 - ASSERT_TRUE(WIFEXITED(status)); 595 - ASSERT_EQ(WEXITSTATUS(status), 0); 621 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 622 + } 623 + 624 + static ssize_t recv_marker(int fd) 625 + { 626 + enum coredump_mark mark = COREDUMP_MARK_REQACK; 627 + ssize_t ret; 628 + 629 + ret = recv(fd, &mark, sizeof(mark), MSG_WAITALL); 630 + if (ret != sizeof(mark)) 631 + return -1; 632 + 633 + switch (mark) { 634 + case COREDUMP_MARK_REQACK: 635 + fprintf(stderr, "Received marker: ReqAck\n"); 636 + return COREDUMP_MARK_REQACK; 637 + case COREDUMP_MARK_MINSIZE: 638 + fprintf(stderr, "Received marker: MinSize\n"); 639 + return COREDUMP_MARK_MINSIZE; 640 + case COREDUMP_MARK_MAXSIZE: 641 + fprintf(stderr, "Received marker: MaxSize\n"); 642 + return COREDUMP_MARK_MAXSIZE; 643 + case COREDUMP_MARK_UNSUPPORTED: 644 + fprintf(stderr, "Received marker: Unsupported\n"); 645 + return COREDUMP_MARK_UNSUPPORTED; 646 + case COREDUMP_MARK_CONFLICTING: 647 + fprintf(stderr, "Received marker: Conflicting\n"); 648 + return COREDUMP_MARK_CONFLICTING; 649 + default: 650 + fprintf(stderr, "Received unknown marker: %u\n", mark); 651 + break; 652 + } 653 + return -1; 654 + } 655 + 656 + static bool read_marker(int fd, enum coredump_mark mark) 657 + { 658 + ssize_t ret; 659 + 660 + ret = recv_marker(fd); 661 + if (ret < 0) 662 + return false; 663 + return ret == mark; 664 + } 665 + 666 + static bool read_coredump_req(int fd, struct coredump_req *req) 667 + { 668 + ssize_t ret; 669 + size_t field_size, user_size, ack_size, kernel_size, remaining_size; 670 + 671 + memset(req, 0, sizeof(*req)); 672 + field_size = sizeof(req->size); 673 + 674 + /* Peek the size of the coredump request. */ 675 + ret = recv(fd, req, field_size, MSG_PEEK | MSG_WAITALL); 676 + if (ret != field_size) 677 + return false; 678 + kernel_size = req->size; 679 + 680 + if (kernel_size < COREDUMP_ACK_SIZE_VER0) 681 + return false; 682 + if (kernel_size >= PAGE_SIZE) 683 + return false; 684 + 685 + /* Use the minimum of user and kernel size to read the full request. */ 686 + user_size = sizeof(struct coredump_req); 687 + ack_size = user_size < kernel_size ? user_size : kernel_size; 688 + ret = recv(fd, req, ack_size, MSG_WAITALL); 689 + if (ret != ack_size) 690 + return false; 691 + 692 + fprintf(stderr, "Read coredump request with size %u and mask 0x%llx\n", 693 + req->size, (unsigned long long)req->mask); 694 + 695 + if (user_size > kernel_size) 696 + remaining_size = user_size - kernel_size; 697 + else 698 + remaining_size = kernel_size - user_size; 699 + 700 + if (PAGE_SIZE <= remaining_size) 701 + return false; 702 + 703 + /* 704 + * Discard any additional data if the kernel's request was larger than 705 + * what we knew about or cared about. 706 + */ 707 + if (remaining_size) { 708 + char buffer[PAGE_SIZE]; 709 + 710 + ret = recv(fd, buffer, sizeof(buffer), MSG_WAITALL); 711 + if (ret != remaining_size) 712 + return false; 713 + fprintf(stderr, "Discarded %zu bytes of data after coredump request\n", remaining_size); 714 + } 715 + 716 + return true; 717 + } 718 + 719 + static bool send_coredump_ack(int fd, const struct coredump_req *req, 720 + __u64 mask, size_t size_ack) 721 + { 722 + ssize_t ret; 723 + /* 724 + * Wrap struct coredump_ack in a larger struct so we can 725 + * simulate sending to much data to the kernel. 726 + */ 727 + struct large_ack_for_size_testing { 728 + struct coredump_ack ack; 729 + char buffer[PAGE_SIZE]; 730 + } large_ack = {}; 731 + 732 + if (!size_ack) 733 + size_ack = sizeof(struct coredump_ack) < req->size_ack ? 734 + sizeof(struct coredump_ack) : 735 + req->size_ack; 736 + large_ack.ack.mask = mask; 737 + large_ack.ack.size = size_ack; 738 + ret = send(fd, &large_ack, size_ack, MSG_NOSIGNAL); 739 + if (ret != size_ack) 740 + return false; 741 + 742 + fprintf(stderr, "Sent coredump ack with size %zu and mask 0x%llx\n", 743 + size_ack, (unsigned long long)mask); 744 + return true; 745 + } 746 + 747 + static bool check_coredump_req(const struct coredump_req *req, size_t min_size, 748 + __u64 required_mask) 749 + { 750 + if (req->size < min_size) 751 + return false; 752 + if ((req->mask & required_mask) != required_mask) 753 + return false; 754 + if (req->mask & ~required_mask) 755 + return false; 756 + return true; 757 + } 758 + 759 + TEST_F(coredump, socket_request_kernel) 760 + { 761 + int pidfd, ret, status; 762 + pid_t pid, pid_coredump_server; 763 + struct stat st; 764 + struct pidfd_info info = {}; 765 + int ipc_sockets[2]; 766 + char c; 767 + 768 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 769 + 770 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 771 + ASSERT_EQ(ret, 0); 772 + 773 + pid_coredump_server = fork(); 774 + ASSERT_GE(pid_coredump_server, 0); 775 + if (pid_coredump_server == 0) { 776 + struct coredump_req req = {}; 777 + int fd_server = -1, fd_coredump = -1, fd_core_file = -1, fd_peer_pidfd = -1; 778 + int exit_code = EXIT_FAILURE; 779 + 780 + close(ipc_sockets[0]); 781 + 782 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 783 + if (fd_server < 0) 784 + goto out; 785 + 786 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 787 + goto out; 788 + 789 + close(ipc_sockets[1]); 790 + 791 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 792 + if (fd_coredump < 0) 793 + goto out; 794 + 795 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 796 + if (fd_peer_pidfd < 0) 797 + goto out; 798 + 799 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 800 + goto out; 801 + 802 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 803 + goto out; 804 + 805 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 806 + goto out; 807 + 808 + fd_core_file = creat("/tmp/coredump.file", 0644); 809 + if (fd_core_file < 0) 810 + goto out; 811 + 812 + if (!read_coredump_req(fd_coredump, &req)) 813 + goto out; 814 + 815 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 816 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 817 + COREDUMP_REJECT | COREDUMP_WAIT)) 818 + goto out; 819 + 820 + if (!send_coredump_ack(fd_coredump, &req, 821 + COREDUMP_KERNEL | COREDUMP_WAIT, 0)) 822 + goto out; 823 + 824 + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) 825 + goto out; 826 + 827 + for (;;) { 828 + char buffer[4096]; 829 + ssize_t bytes_read, bytes_write; 830 + 831 + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 832 + if (bytes_read < 0) 833 + goto out; 834 + 835 + if (bytes_read == 0) 836 + break; 837 + 838 + bytes_write = write(fd_core_file, buffer, bytes_read); 839 + if (bytes_read != bytes_write) 840 + goto out; 841 + } 842 + 843 + exit_code = EXIT_SUCCESS; 844 + out: 845 + if (fd_core_file >= 0) 846 + close(fd_core_file); 847 + if (fd_peer_pidfd >= 0) 848 + close(fd_peer_pidfd); 849 + if (fd_coredump >= 0) 850 + close(fd_coredump); 851 + if (fd_server >= 0) 852 + close(fd_server); 853 + _exit(exit_code); 854 + } 855 + self->pid_coredump_server = pid_coredump_server; 856 + 857 + EXPECT_EQ(close(ipc_sockets[1]), 0); 858 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 859 + EXPECT_EQ(close(ipc_sockets[0]), 0); 860 + 861 + pid = fork(); 862 + ASSERT_GE(pid, 0); 863 + if (pid == 0) 864 + crashing_child(); 865 + 866 + pidfd = sys_pidfd_open(pid, 0); 867 + ASSERT_GE(pidfd, 0); 868 + 869 + waitpid(pid, &status, 0); 870 + ASSERT_TRUE(WIFSIGNALED(status)); 871 + ASSERT_TRUE(WCOREDUMP(status)); 872 + 873 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 874 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 875 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 876 + 877 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 878 + 879 + ASSERT_EQ(stat("/tmp/coredump.file", &st), 0); 880 + ASSERT_GT(st.st_size, 0); 881 + system("file /tmp/coredump.file"); 882 + } 883 + 884 + TEST_F(coredump, socket_request_userspace) 885 + { 886 + int pidfd, ret, status; 887 + pid_t pid, pid_coredump_server; 888 + struct pidfd_info info = {}; 889 + int ipc_sockets[2]; 890 + char c; 891 + 892 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 893 + 894 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 895 + ASSERT_EQ(ret, 0); 896 + 897 + pid_coredump_server = fork(); 898 + ASSERT_GE(pid_coredump_server, 0); 899 + if (pid_coredump_server == 0) { 900 + struct coredump_req req = {}; 901 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; 902 + int exit_code = EXIT_FAILURE; 903 + 904 + close(ipc_sockets[0]); 905 + 906 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 907 + if (fd_server < 0) 908 + goto out; 909 + 910 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 911 + goto out; 912 + 913 + close(ipc_sockets[1]); 914 + 915 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 916 + if (fd_coredump < 0) 917 + goto out; 918 + 919 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 920 + if (fd_peer_pidfd < 0) 921 + goto out; 922 + 923 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 924 + goto out; 925 + 926 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 927 + goto out; 928 + 929 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 930 + goto out; 931 + 932 + if (!read_coredump_req(fd_coredump, &req)) 933 + goto out; 934 + 935 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 936 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 937 + COREDUMP_REJECT | COREDUMP_WAIT)) 938 + goto out; 939 + 940 + if (!send_coredump_ack(fd_coredump, &req, 941 + COREDUMP_USERSPACE | COREDUMP_WAIT, 0)) 942 + goto out; 943 + 944 + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) 945 + goto out; 946 + 947 + for (;;) { 948 + char buffer[4096]; 949 + ssize_t bytes_read; 950 + 951 + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 952 + if (bytes_read > 0) 953 + goto out; 954 + 955 + if (bytes_read < 0) 956 + goto out; 957 + 958 + if (bytes_read == 0) 959 + break; 960 + } 961 + 962 + exit_code = EXIT_SUCCESS; 963 + out: 964 + if (fd_peer_pidfd >= 0) 965 + close(fd_peer_pidfd); 966 + if (fd_coredump >= 0) 967 + close(fd_coredump); 968 + if (fd_server >= 0) 969 + close(fd_server); 970 + _exit(exit_code); 971 + } 972 + self->pid_coredump_server = pid_coredump_server; 973 + 974 + EXPECT_EQ(close(ipc_sockets[1]), 0); 975 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 976 + EXPECT_EQ(close(ipc_sockets[0]), 0); 977 + 978 + pid = fork(); 979 + ASSERT_GE(pid, 0); 980 + if (pid == 0) 981 + crashing_child(); 982 + 983 + pidfd = sys_pidfd_open(pid, 0); 984 + ASSERT_GE(pidfd, 0); 985 + 986 + waitpid(pid, &status, 0); 987 + ASSERT_TRUE(WIFSIGNALED(status)); 988 + ASSERT_TRUE(WCOREDUMP(status)); 989 + 990 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 991 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 992 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 993 + 994 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 995 + } 996 + 997 + TEST_F(coredump, socket_request_reject) 998 + { 999 + int pidfd, ret, status; 1000 + pid_t pid, pid_coredump_server; 1001 + struct pidfd_info info = {}; 1002 + int ipc_sockets[2]; 1003 + char c; 1004 + 1005 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 1006 + 1007 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 1008 + ASSERT_EQ(ret, 0); 1009 + 1010 + pid_coredump_server = fork(); 1011 + ASSERT_GE(pid_coredump_server, 0); 1012 + if (pid_coredump_server == 0) { 1013 + struct coredump_req req = {}; 1014 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; 1015 + int exit_code = EXIT_FAILURE; 1016 + 1017 + close(ipc_sockets[0]); 1018 + 1019 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 1020 + if (fd_server < 0) 1021 + goto out; 1022 + 1023 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 1024 + goto out; 1025 + 1026 + close(ipc_sockets[1]); 1027 + 1028 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 1029 + if (fd_coredump < 0) 1030 + goto out; 1031 + 1032 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 1033 + if (fd_peer_pidfd < 0) 1034 + goto out; 1035 + 1036 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 1037 + goto out; 1038 + 1039 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 1040 + goto out; 1041 + 1042 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 1043 + goto out; 1044 + 1045 + if (!read_coredump_req(fd_coredump, &req)) 1046 + goto out; 1047 + 1048 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 1049 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 1050 + COREDUMP_REJECT | COREDUMP_WAIT)) 1051 + goto out; 1052 + 1053 + if (!send_coredump_ack(fd_coredump, &req, 1054 + COREDUMP_REJECT | COREDUMP_WAIT, 0)) 1055 + goto out; 1056 + 1057 + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) 1058 + goto out; 1059 + 1060 + for (;;) { 1061 + char buffer[4096]; 1062 + ssize_t bytes_read; 1063 + 1064 + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 1065 + if (bytes_read > 0) 1066 + goto out; 1067 + 1068 + if (bytes_read < 0) 1069 + goto out; 1070 + 1071 + if (bytes_read == 0) 1072 + break; 1073 + } 1074 + 1075 + exit_code = EXIT_SUCCESS; 1076 + out: 1077 + if (fd_peer_pidfd >= 0) 1078 + close(fd_peer_pidfd); 1079 + if (fd_coredump >= 0) 1080 + close(fd_coredump); 1081 + if (fd_server >= 0) 1082 + close(fd_server); 1083 + _exit(exit_code); 1084 + } 1085 + self->pid_coredump_server = pid_coredump_server; 1086 + 1087 + EXPECT_EQ(close(ipc_sockets[1]), 0); 1088 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 1089 + EXPECT_EQ(close(ipc_sockets[0]), 0); 1090 + 1091 + pid = fork(); 1092 + ASSERT_GE(pid, 0); 1093 + if (pid == 0) 1094 + crashing_child(); 1095 + 1096 + pidfd = sys_pidfd_open(pid, 0); 1097 + ASSERT_GE(pidfd, 0); 1098 + 1099 + waitpid(pid, &status, 0); 1100 + ASSERT_TRUE(WIFSIGNALED(status)); 1101 + ASSERT_FALSE(WCOREDUMP(status)); 1102 + 1103 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 1104 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 1105 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 1106 + 1107 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 1108 + } 1109 + 1110 + TEST_F(coredump, socket_request_invalid_flag_combination) 1111 + { 1112 + int pidfd, ret, status; 1113 + pid_t pid, pid_coredump_server; 1114 + struct pidfd_info info = {}; 1115 + int ipc_sockets[2]; 1116 + char c; 1117 + 1118 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 1119 + 1120 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 1121 + ASSERT_EQ(ret, 0); 1122 + 1123 + pid_coredump_server = fork(); 1124 + ASSERT_GE(pid_coredump_server, 0); 1125 + if (pid_coredump_server == 0) { 1126 + struct coredump_req req = {}; 1127 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; 1128 + int exit_code = EXIT_FAILURE; 1129 + 1130 + close(ipc_sockets[0]); 1131 + 1132 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 1133 + if (fd_server < 0) 1134 + goto out; 1135 + 1136 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 1137 + goto out; 1138 + 1139 + close(ipc_sockets[1]); 1140 + 1141 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 1142 + if (fd_coredump < 0) 1143 + goto out; 1144 + 1145 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 1146 + if (fd_peer_pidfd < 0) 1147 + goto out; 1148 + 1149 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 1150 + goto out; 1151 + 1152 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 1153 + goto out; 1154 + 1155 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 1156 + goto out; 1157 + 1158 + if (!read_coredump_req(fd_coredump, &req)) 1159 + goto out; 1160 + 1161 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 1162 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 1163 + COREDUMP_REJECT | COREDUMP_WAIT)) 1164 + goto out; 1165 + 1166 + if (!send_coredump_ack(fd_coredump, &req, 1167 + COREDUMP_KERNEL | COREDUMP_REJECT | COREDUMP_WAIT, 0)) 1168 + goto out; 1169 + 1170 + if (!read_marker(fd_coredump, COREDUMP_MARK_CONFLICTING)) 1171 + goto out; 1172 + 1173 + exit_code = EXIT_SUCCESS; 1174 + out: 1175 + if (fd_peer_pidfd >= 0) 1176 + close(fd_peer_pidfd); 1177 + if (fd_coredump >= 0) 1178 + close(fd_coredump); 1179 + if (fd_server >= 0) 1180 + close(fd_server); 1181 + _exit(exit_code); 1182 + } 1183 + self->pid_coredump_server = pid_coredump_server; 1184 + 1185 + EXPECT_EQ(close(ipc_sockets[1]), 0); 1186 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 1187 + EXPECT_EQ(close(ipc_sockets[0]), 0); 1188 + 1189 + pid = fork(); 1190 + ASSERT_GE(pid, 0); 1191 + if (pid == 0) 1192 + crashing_child(); 1193 + 1194 + pidfd = sys_pidfd_open(pid, 0); 1195 + ASSERT_GE(pidfd, 0); 1196 + 1197 + waitpid(pid, &status, 0); 1198 + ASSERT_TRUE(WIFSIGNALED(status)); 1199 + ASSERT_FALSE(WCOREDUMP(status)); 1200 + 1201 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 1202 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 1203 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 1204 + 1205 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 1206 + } 1207 + 1208 + TEST_F(coredump, socket_request_unknown_flag) 1209 + { 1210 + int pidfd, ret, status; 1211 + pid_t pid, pid_coredump_server; 1212 + struct pidfd_info info = {}; 1213 + int ipc_sockets[2]; 1214 + char c; 1215 + 1216 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 1217 + 1218 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 1219 + ASSERT_EQ(ret, 0); 1220 + 1221 + pid_coredump_server = fork(); 1222 + ASSERT_GE(pid_coredump_server, 0); 1223 + if (pid_coredump_server == 0) { 1224 + struct coredump_req req = {}; 1225 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; 1226 + int exit_code = EXIT_FAILURE; 1227 + 1228 + close(ipc_sockets[0]); 1229 + 1230 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 1231 + if (fd_server < 0) 1232 + goto out; 1233 + 1234 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 1235 + goto out; 1236 + 1237 + close(ipc_sockets[1]); 1238 + 1239 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 1240 + if (fd_coredump < 0) 1241 + goto out; 1242 + 1243 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 1244 + if (fd_peer_pidfd < 0) 1245 + goto out; 1246 + 1247 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 1248 + goto out; 1249 + 1250 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 1251 + goto out; 1252 + 1253 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 1254 + goto out; 1255 + 1256 + if (!read_coredump_req(fd_coredump, &req)) 1257 + goto out; 1258 + 1259 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 1260 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 1261 + COREDUMP_REJECT | COREDUMP_WAIT)) 1262 + goto out; 1263 + 1264 + if (!send_coredump_ack(fd_coredump, &req, (1ULL << 63), 0)) 1265 + goto out; 1266 + 1267 + if (!read_marker(fd_coredump, COREDUMP_MARK_UNSUPPORTED)) 1268 + goto out; 1269 + 1270 + exit_code = EXIT_SUCCESS; 1271 + out: 1272 + if (fd_peer_pidfd >= 0) 1273 + close(fd_peer_pidfd); 1274 + if (fd_coredump >= 0) 1275 + close(fd_coredump); 1276 + if (fd_server >= 0) 1277 + close(fd_server); 1278 + _exit(exit_code); 1279 + } 1280 + self->pid_coredump_server = pid_coredump_server; 1281 + 1282 + EXPECT_EQ(close(ipc_sockets[1]), 0); 1283 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 1284 + EXPECT_EQ(close(ipc_sockets[0]), 0); 1285 + 1286 + pid = fork(); 1287 + ASSERT_GE(pid, 0); 1288 + if (pid == 0) 1289 + crashing_child(); 1290 + 1291 + pidfd = sys_pidfd_open(pid, 0); 1292 + ASSERT_GE(pidfd, 0); 1293 + 1294 + waitpid(pid, &status, 0); 1295 + ASSERT_TRUE(WIFSIGNALED(status)); 1296 + ASSERT_FALSE(WCOREDUMP(status)); 1297 + 1298 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 1299 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 1300 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 1301 + 1302 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 1303 + } 1304 + 1305 + TEST_F(coredump, socket_request_invalid_size_small) 1306 + { 1307 + int pidfd, ret, status; 1308 + pid_t pid, pid_coredump_server; 1309 + struct pidfd_info info = {}; 1310 + int ipc_sockets[2]; 1311 + char c; 1312 + 1313 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 1314 + 1315 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 1316 + ASSERT_EQ(ret, 0); 1317 + 1318 + pid_coredump_server = fork(); 1319 + ASSERT_GE(pid_coredump_server, 0); 1320 + if (pid_coredump_server == 0) { 1321 + struct coredump_req req = {}; 1322 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; 1323 + int exit_code = EXIT_FAILURE; 1324 + 1325 + close(ipc_sockets[0]); 1326 + 1327 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 1328 + if (fd_server < 0) 1329 + goto out; 1330 + 1331 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 1332 + goto out; 1333 + 1334 + close(ipc_sockets[1]); 1335 + 1336 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 1337 + if (fd_coredump < 0) 1338 + goto out; 1339 + 1340 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 1341 + if (fd_peer_pidfd < 0) 1342 + goto out; 1343 + 1344 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 1345 + goto out; 1346 + 1347 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 1348 + goto out; 1349 + 1350 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 1351 + goto out; 1352 + 1353 + if (!read_coredump_req(fd_coredump, &req)) 1354 + goto out; 1355 + 1356 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 1357 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 1358 + COREDUMP_REJECT | COREDUMP_WAIT)) 1359 + goto out; 1360 + 1361 + if (!send_coredump_ack(fd_coredump, &req, 1362 + COREDUMP_REJECT | COREDUMP_WAIT, 1363 + COREDUMP_ACK_SIZE_VER0 / 2)) 1364 + goto out; 1365 + 1366 + if (!read_marker(fd_coredump, COREDUMP_MARK_MINSIZE)) 1367 + goto out; 1368 + 1369 + exit_code = EXIT_SUCCESS; 1370 + out: 1371 + if (fd_peer_pidfd >= 0) 1372 + close(fd_peer_pidfd); 1373 + if (fd_coredump >= 0) 1374 + close(fd_coredump); 1375 + if (fd_server >= 0) 1376 + close(fd_server); 1377 + _exit(exit_code); 1378 + } 1379 + self->pid_coredump_server = pid_coredump_server; 1380 + 1381 + EXPECT_EQ(close(ipc_sockets[1]), 0); 1382 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 1383 + EXPECT_EQ(close(ipc_sockets[0]), 0); 1384 + 1385 + pid = fork(); 1386 + ASSERT_GE(pid, 0); 1387 + if (pid == 0) 1388 + crashing_child(); 1389 + 1390 + pidfd = sys_pidfd_open(pid, 0); 1391 + ASSERT_GE(pidfd, 0); 1392 + 1393 + waitpid(pid, &status, 0); 1394 + ASSERT_TRUE(WIFSIGNALED(status)); 1395 + ASSERT_FALSE(WCOREDUMP(status)); 1396 + 1397 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 1398 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 1399 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 1400 + 1401 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 1402 + } 1403 + 1404 + TEST_F(coredump, socket_request_invalid_size_large) 1405 + { 1406 + int pidfd, ret, status; 1407 + pid_t pid, pid_coredump_server; 1408 + struct pidfd_info info = {}; 1409 + int ipc_sockets[2]; 1410 + char c; 1411 + 1412 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 1413 + 1414 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 1415 + ASSERT_EQ(ret, 0); 1416 + 1417 + pid_coredump_server = fork(); 1418 + ASSERT_GE(pid_coredump_server, 0); 1419 + if (pid_coredump_server == 0) { 1420 + struct coredump_req req = {}; 1421 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1; 1422 + int exit_code = EXIT_FAILURE; 1423 + 1424 + close(ipc_sockets[0]); 1425 + 1426 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 1427 + if (fd_server < 0) 1428 + goto out; 1429 + 1430 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 1431 + goto out; 1432 + 1433 + close(ipc_sockets[1]); 1434 + 1435 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 1436 + if (fd_coredump < 0) 1437 + goto out; 1438 + 1439 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 1440 + if (fd_peer_pidfd < 0) 1441 + goto out; 1442 + 1443 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 1444 + goto out; 1445 + 1446 + if (!(info.mask & PIDFD_INFO_COREDUMP)) 1447 + goto out; 1448 + 1449 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) 1450 + goto out; 1451 + 1452 + if (!read_coredump_req(fd_coredump, &req)) 1453 + goto out; 1454 + 1455 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 1456 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 1457 + COREDUMP_REJECT | COREDUMP_WAIT)) 1458 + goto out; 1459 + 1460 + if (!send_coredump_ack(fd_coredump, &req, 1461 + COREDUMP_REJECT | COREDUMP_WAIT, 1462 + COREDUMP_ACK_SIZE_VER0 + PAGE_SIZE)) 1463 + goto out; 1464 + 1465 + if (!read_marker(fd_coredump, COREDUMP_MARK_MAXSIZE)) 1466 + goto out; 1467 + 1468 + exit_code = EXIT_SUCCESS; 1469 + out: 1470 + if (fd_peer_pidfd >= 0) 1471 + close(fd_peer_pidfd); 1472 + if (fd_coredump >= 0) 1473 + close(fd_coredump); 1474 + if (fd_server >= 0) 1475 + close(fd_server); 1476 + _exit(exit_code); 1477 + } 1478 + self->pid_coredump_server = pid_coredump_server; 1479 + 1480 + EXPECT_EQ(close(ipc_sockets[1]), 0); 1481 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 1482 + EXPECT_EQ(close(ipc_sockets[0]), 0); 1483 + 1484 + pid = fork(); 1485 + ASSERT_GE(pid, 0); 1486 + if (pid == 0) 1487 + crashing_child(); 1488 + 1489 + pidfd = sys_pidfd_open(pid, 0); 1490 + ASSERT_GE(pidfd, 0); 1491 + 1492 + waitpid(pid, &status, 0); 1493 + ASSERT_TRUE(WIFSIGNALED(status)); 1494 + ASSERT_FALSE(WCOREDUMP(status)); 1495 + 1496 + ASSERT_TRUE(get_pidfd_info(pidfd, &info)); 1497 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 1498 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 1499 + 1500 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 1501 + } 1502 + 1503 + static int open_coredump_tmpfile(int fd_tmpfs_detached) 1504 + { 1505 + return openat(fd_tmpfs_detached, ".", O_TMPFILE | O_RDWR | O_EXCL, 0600); 1506 + } 1507 + 1508 + #define NUM_CRASHING_COREDUMPS 5 1509 + 1510 + TEST_F_TIMEOUT(coredump, socket_multiple_crashing_coredumps, 500) 1511 + { 1512 + int pidfd[NUM_CRASHING_COREDUMPS], status[NUM_CRASHING_COREDUMPS]; 1513 + pid_t pid[NUM_CRASHING_COREDUMPS], pid_coredump_server; 1514 + struct pidfd_info info = {}; 1515 + int ipc_sockets[2]; 1516 + char c; 1517 + 1518 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 1519 + 1520 + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); 1521 + 1522 + pid_coredump_server = fork(); 1523 + ASSERT_GE(pid_coredump_server, 0); 1524 + if (pid_coredump_server == 0) { 1525 + int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1; 1526 + int exit_code = EXIT_FAILURE; 1527 + struct coredump_req req = {}; 1528 + 1529 + close(ipc_sockets[0]); 1530 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 1531 + if (fd_server < 0) { 1532 + fprintf(stderr, "Failed to create and listen on unix socket\n"); 1533 + goto out; 1534 + } 1535 + 1536 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 1537 + fprintf(stderr, "Failed to notify parent via ipc socket\n"); 1538 + goto out; 1539 + } 1540 + close(ipc_sockets[1]); 1541 + 1542 + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { 1543 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 1544 + if (fd_coredump < 0) { 1545 + fprintf(stderr, "accept4 failed: %m\n"); 1546 + goto out; 1547 + } 1548 + 1549 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 1550 + if (fd_peer_pidfd < 0) { 1551 + fprintf(stderr, "get_peer_pidfd failed for fd %d: %m\n", fd_coredump); 1552 + goto out; 1553 + } 1554 + 1555 + if (!get_pidfd_info(fd_peer_pidfd, &info)) { 1556 + fprintf(stderr, "get_pidfd_info failed for fd %d\n", fd_peer_pidfd); 1557 + goto out; 1558 + } 1559 + 1560 + if (!(info.mask & PIDFD_INFO_COREDUMP)) { 1561 + fprintf(stderr, "pidfd info missing PIDFD_INFO_COREDUMP for fd %d\n", fd_peer_pidfd); 1562 + goto out; 1563 + } 1564 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) { 1565 + fprintf(stderr, "pidfd info missing PIDFD_COREDUMPED for fd %d\n", fd_peer_pidfd); 1566 + goto out; 1567 + } 1568 + 1569 + if (!read_coredump_req(fd_coredump, &req)) { 1570 + fprintf(stderr, "read_coredump_req failed for fd %d\n", fd_coredump); 1571 + goto out; 1572 + } 1573 + 1574 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 1575 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 1576 + COREDUMP_REJECT | COREDUMP_WAIT)) { 1577 + fprintf(stderr, "check_coredump_req failed for fd %d\n", fd_coredump); 1578 + goto out; 1579 + } 1580 + 1581 + if (!send_coredump_ack(fd_coredump, &req, 1582 + COREDUMP_KERNEL | COREDUMP_WAIT, 0)) { 1583 + fprintf(stderr, "send_coredump_ack failed for fd %d\n", fd_coredump); 1584 + goto out; 1585 + } 1586 + 1587 + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) { 1588 + fprintf(stderr, "read_marker failed for fd %d\n", fd_coredump); 1589 + goto out; 1590 + } 1591 + 1592 + fd_core_file = open_coredump_tmpfile(self->fd_tmpfs_detached); 1593 + if (fd_core_file < 0) { 1594 + fprintf(stderr, "%m - open_coredump_tmpfile failed for fd %d\n", fd_coredump); 1595 + goto out; 1596 + } 1597 + 1598 + for (;;) { 1599 + char buffer[4096]; 1600 + ssize_t bytes_read, bytes_write; 1601 + 1602 + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 1603 + if (bytes_read < 0) { 1604 + fprintf(stderr, "read failed for fd %d: %m\n", fd_coredump); 1605 + goto out; 1606 + } 1607 + 1608 + if (bytes_read == 0) 1609 + break; 1610 + 1611 + bytes_write = write(fd_core_file, buffer, bytes_read); 1612 + if (bytes_read != bytes_write) { 1613 + fprintf(stderr, "write failed for fd %d: %m\n", fd_core_file); 1614 + goto out; 1615 + } 1616 + } 1617 + 1618 + close(fd_core_file); 1619 + close(fd_peer_pidfd); 1620 + close(fd_coredump); 1621 + fd_peer_pidfd = -1; 1622 + fd_coredump = -1; 1623 + } 1624 + 1625 + exit_code = EXIT_SUCCESS; 1626 + out: 1627 + if (fd_core_file >= 0) 1628 + close(fd_core_file); 1629 + if (fd_peer_pidfd >= 0) 1630 + close(fd_peer_pidfd); 1631 + if (fd_coredump >= 0) 1632 + close(fd_coredump); 1633 + if (fd_server >= 0) 1634 + close(fd_server); 1635 + _exit(exit_code); 1636 + } 1637 + self->pid_coredump_server = pid_coredump_server; 1638 + 1639 + EXPECT_EQ(close(ipc_sockets[1]), 0); 1640 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 1641 + EXPECT_EQ(close(ipc_sockets[0]), 0); 1642 + 1643 + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { 1644 + pid[i] = fork(); 1645 + ASSERT_GE(pid[i], 0); 1646 + if (pid[i] == 0) 1647 + crashing_child(); 1648 + pidfd[i] = sys_pidfd_open(pid[i], 0); 1649 + ASSERT_GE(pidfd[i], 0); 1650 + } 1651 + 1652 + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { 1653 + waitpid(pid[i], &status[i], 0); 1654 + ASSERT_TRUE(WIFSIGNALED(status[i])); 1655 + ASSERT_TRUE(WCOREDUMP(status[i])); 1656 + } 1657 + 1658 + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { 1659 + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 1660 + ASSERT_EQ(ioctl(pidfd[i], PIDFD_GET_INFO, &info), 0); 1661 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 1662 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 1663 + } 1664 + 1665 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 1666 + } 1667 + 1668 + #define MAX_EVENTS 128 1669 + 1670 + static void process_coredump_worker(int fd_coredump, int fd_peer_pidfd, int fd_core_file) 1671 + { 1672 + int epfd = -1; 1673 + int exit_code = EXIT_FAILURE; 1674 + 1675 + epfd = epoll_create1(0); 1676 + if (epfd < 0) 1677 + goto out; 1678 + 1679 + struct epoll_event ev; 1680 + ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET; 1681 + ev.data.fd = fd_coredump; 1682 + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd_coredump, &ev) < 0) 1683 + goto out; 1684 + 1685 + for (;;) { 1686 + struct epoll_event events[1]; 1687 + int n = epoll_wait(epfd, events, 1, -1); 1688 + if (n < 0) 1689 + break; 1690 + 1691 + if (events[0].events & (EPOLLIN | EPOLLRDHUP)) { 1692 + for (;;) { 1693 + char buffer[4096]; 1694 + ssize_t bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 1695 + if (bytes_read < 0) { 1696 + if (errno == EAGAIN || errno == EWOULDBLOCK) 1697 + break; 1698 + goto out; 1699 + } 1700 + if (bytes_read == 0) 1701 + goto done; 1702 + ssize_t bytes_write = write(fd_core_file, buffer, bytes_read); 1703 + if (bytes_write != bytes_read) 1704 + goto out; 1705 + } 1706 + } 1707 + } 1708 + 1709 + done: 1710 + exit_code = EXIT_SUCCESS; 1711 + out: 1712 + if (epfd >= 0) 1713 + close(epfd); 1714 + if (fd_core_file >= 0) 1715 + close(fd_core_file); 1716 + if (fd_peer_pidfd >= 0) 1717 + close(fd_peer_pidfd); 1718 + if (fd_coredump >= 0) 1719 + close(fd_coredump); 1720 + _exit(exit_code); 1721 + } 1722 + 1723 + TEST_F_TIMEOUT(coredump, socket_multiple_crashing_coredumps_epoll_workers, 500) 1724 + { 1725 + int pidfd[NUM_CRASHING_COREDUMPS], status[NUM_CRASHING_COREDUMPS]; 1726 + pid_t pid[NUM_CRASHING_COREDUMPS], pid_coredump_server, worker_pids[NUM_CRASHING_COREDUMPS]; 1727 + struct pidfd_info info = {}; 1728 + int ipc_sockets[2]; 1729 + char c; 1730 + 1731 + ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket")); 1732 + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); 1733 + 1734 + pid_coredump_server = fork(); 1735 + ASSERT_GE(pid_coredump_server, 0); 1736 + if (pid_coredump_server == 0) { 1737 + int fd_server = -1, exit_code = EXIT_FAILURE, n_conns = 0; 1738 + fd_server = -1; 1739 + exit_code = EXIT_FAILURE; 1740 + n_conns = 0; 1741 + close(ipc_sockets[0]); 1742 + fd_server = create_and_listen_unix_socket("/tmp/coredump.socket"); 1743 + if (fd_server < 0) 1744 + goto out; 1745 + 1746 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) 1747 + goto out; 1748 + close(ipc_sockets[1]); 1749 + 1750 + while (n_conns < NUM_CRASHING_COREDUMPS) { 1751 + int fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1; 1752 + struct coredump_req req = {}; 1753 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 1754 + if (fd_coredump < 0) { 1755 + if (errno == EAGAIN || errno == EWOULDBLOCK) 1756 + continue; 1757 + goto out; 1758 + } 1759 + fd_peer_pidfd = get_peer_pidfd(fd_coredump); 1760 + if (fd_peer_pidfd < 0) 1761 + goto out; 1762 + if (!get_pidfd_info(fd_peer_pidfd, &info)) 1763 + goto out; 1764 + if (!(info.mask & PIDFD_INFO_COREDUMP) || !(info.coredump_mask & PIDFD_COREDUMPED)) 1765 + goto out; 1766 + if (!read_coredump_req(fd_coredump, &req)) 1767 + goto out; 1768 + if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0, 1769 + COREDUMP_KERNEL | COREDUMP_USERSPACE | 1770 + COREDUMP_REJECT | COREDUMP_WAIT)) 1771 + goto out; 1772 + if (!send_coredump_ack(fd_coredump, &req, COREDUMP_KERNEL | COREDUMP_WAIT, 0)) 1773 + goto out; 1774 + if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) 1775 + goto out; 1776 + fd_core_file = open_coredump_tmpfile(self->fd_tmpfs_detached); 1777 + if (fd_core_file < 0) 1778 + goto out; 1779 + pid_t worker = fork(); 1780 + if (worker == 0) { 1781 + close(fd_server); 1782 + process_coredump_worker(fd_coredump, fd_peer_pidfd, fd_core_file); 1783 + } 1784 + worker_pids[n_conns] = worker; 1785 + if (fd_coredump >= 0) 1786 + close(fd_coredump); 1787 + if (fd_peer_pidfd >= 0) 1788 + close(fd_peer_pidfd); 1789 + if (fd_core_file >= 0) 1790 + close(fd_core_file); 1791 + n_conns++; 1792 + } 1793 + exit_code = EXIT_SUCCESS; 1794 + out: 1795 + if (fd_server >= 0) 1796 + close(fd_server); 1797 + 1798 + // Reap all worker processes 1799 + for (int i = 0; i < n_conns; i++) { 1800 + int wstatus; 1801 + if (waitpid(worker_pids[i], &wstatus, 0) < 0) { 1802 + fprintf(stderr, "Failed to wait for worker %d: %m\n", worker_pids[i]); 1803 + } else if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != EXIT_SUCCESS) { 1804 + fprintf(stderr, "Worker %d exited with error code %d\n", worker_pids[i], WEXITSTATUS(wstatus)); 1805 + exit_code = EXIT_FAILURE; 1806 + } 1807 + } 1808 + 1809 + _exit(exit_code); 1810 + } 1811 + self->pid_coredump_server = pid_coredump_server; 1812 + 1813 + EXPECT_EQ(close(ipc_sockets[1]), 0); 1814 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 1815 + EXPECT_EQ(close(ipc_sockets[0]), 0); 1816 + 1817 + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { 1818 + pid[i] = fork(); 1819 + ASSERT_GE(pid[i], 0); 1820 + if (pid[i] == 0) 1821 + crashing_child(); 1822 + pidfd[i] = sys_pidfd_open(pid[i], 0); 1823 + ASSERT_GE(pidfd[i], 0); 1824 + } 1825 + 1826 + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { 1827 + ASSERT_GE(waitpid(pid[i], &status[i], 0), 0); 1828 + ASSERT_TRUE(WIFSIGNALED(status[i])); 1829 + ASSERT_TRUE(WCOREDUMP(status[i])); 1830 + } 1831 + 1832 + for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) { 1833 + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 1834 + ASSERT_EQ(ioctl(pidfd[i], PIDFD_GET_INFO, &info), 0); 1835 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 1836 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 1837 + } 1838 + 1839 + wait_and_check_coredump_server(pid_coredump_server, _metadata, self); 1840 + } 1841 + 1842 + TEST_F(coredump, socket_invalid_paths) 1843 + { 1844 + ASSERT_FALSE(set_core_pattern("@ /tmp/coredump.socket")); 1845 + ASSERT_FALSE(set_core_pattern("@/tmp/../coredump.socket")); 1846 + ASSERT_FALSE(set_core_pattern("@../coredump.socket")); 1847 + ASSERT_FALSE(set_core_pattern("@/tmp/coredump.socket/..")); 1848 + ASSERT_FALSE(set_core_pattern("@..")); 1849 + 1850 + ASSERT_FALSE(set_core_pattern("@@ /tmp/coredump.socket")); 1851 + ASSERT_FALSE(set_core_pattern("@@/tmp/../coredump.socket")); 1852 + ASSERT_FALSE(set_core_pattern("@@../coredump.socket")); 1853 + ASSERT_FALSE(set_core_pattern("@@/tmp/coredump.socket/..")); 1854 + ASSERT_FALSE(set_core_pattern("@@..")); 1855 + 1856 + ASSERT_FALSE(set_core_pattern("@@@/tmp/coredump.socket")); 596 1857 } 597 1858 598 1859 TEST_HARNESS_MAIN