Index: libexec/Makefile =================================================================== --- libexec/Makefile +++ libexec/Makefile @@ -7,6 +7,7 @@ ${_atrun} \ ${_blacklistd-helper} \ ${_comsat} \ + corestop \ ${_dma} \ flua \ getty \ Index: libexec/corestop/Makefile =================================================================== --- /dev/null +++ libexec/corestop/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SCRIPTS= corestop.sh + +.include Index: libexec/corestop/corestop.sh =================================================================== --- /dev/null +++ libexec/corestop/corestop.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +die() { + echo "$*; exiting" 1>&2 + exit 1 +} + +PID="$1" +EUID="$2" + +if [ "$3" != "su-done" ]; then + # Reexecute ourselves as user $EUID. + LOGIN=$(getent passwd "${EUID}" | cut -f 1 -d ':') + + if [ -z "${LOGIN}" ]; then + die "cannot retrieve login name for EUID ${EUID}" + exit 1 + fi + + exec su -l ${LOGIN} -c "${0} ${PID} ${EUID} su-done" + + # This cannot happen, but hey, what if there's a bug in sh(1)? + exit 1 +fi + +# From this point on we're running as user $EUID. + +if [ "${EUID}" -ne $(id -u) ]; then + # Make extra sure it doesn't happen. + die "not running as EUID ${EUID}" +fi + +# Get the process working directory. +CWD=$(procstat -f "${PID}" | awk '$3 == "cwd" { print $10 }') +[ -n "${CWD}" ] || die "unable to query working directory" + +# Get process name. +COMM=$(procstat -b "${PID}" | awk 'NR == 2 { print $2 }') +[ -n "${CWD}" ] || die "unable to query process name" + +CORETMP=$(mktemp -d -t corestop.XXXXXX) +if [ $? -ne 0 -o -z "${CORETMP}" ]; then + die "mktemp failed" +fi + +cd "${CORETMP}" + +show() { + echo "\$ $@" + $@ + echo +} + +{ + echo bt | show /usr/bin/lldb -p "${PID}" + #echo bt | show /usr/local/bin/gdb -q -p "${PID}" + + show procstat arguments "${PID}" + show procstat environment "${PID}" + show procstat vm "${PID}" + show procstat files "${PID}" + show procstat threads "${PID}" + show procstat basic "${PID}" + show procstat binary "${PID}" + show procstat credentials "${PID}" + show procstat auxv "${PID}" + show procstat signal "${PID}" + show procstat tsignal "${PID}" + show procstat kstack "${PID}" + show procstat rlimit "${PID}" + # XXX: The one below doesn't seem to work. + #show procstat ptlwpinfo "${PID}" + show procstat rusage "${PID}" + show procstat cpuset "${PID}" + +} > "${CORETMP}/core.txt" 2>&1 + +# Get list of files mapped as executable into the process virtual memory, +# ie the main executable and its shared libraries. +FILES=$(procstat -v "${PID}" | awk '$4 ~ /x/ && $10 == "vn" {print $11 }' | sort -u | xargs) + +gcore -c "${COMM}.core" "${PID}" + +COREDIR="${COMM}.${PID}.coredir" +CORETAR="${CWD}/${COREDIR}.tar.gz" +tar -c -z -L -s ",^,${COREDIR}/," -f "${CORETAR}" * ${FILES} +rc=$? +rm -rf "${CORETMP}" + +if [ $rc -eq 0 ]; then + echo "${CORETAR} created for pid ${PID} (${COMM}), euid ${EUID}" +fi Index: sbin/devd/devd.conf =================================================================== --- sbin/devd/devd.conf +++ sbin/devd/devd.conf @@ -263,6 +263,13 @@ action "/etc/rc.resume acpi $notify"; }; +notify 10 { + match "system" "kernel"; + match "subsystem" "signal"; + match "type" "corestop"; + action "/usr/libexec/corestop $pid $euid 2>&1 | /usr/bin/logger -st corestop\(pid=$pid\); kill -KILL $pid"; +}; + /* EXAMPLES TO END OF FILE # An example of something that a vendor might install if you were to Index: sbin/devd/devd.conf.5 =================================================================== --- sbin/devd/devd.conf.5 +++ sbin/devd/devd.conf.5 @@ -272,6 +272,8 @@ Name of attached/detached device. .It Li endpoints Endpoint count (USB). +.It Li euid +Effective UID (corestop). .It Li function Card functions. .It Li interface @@ -295,7 +297,8 @@ .It Li parent Parent device. .It Li pid -PID of the process triggering the rule (RCTL). +PID of the process triggering the rule (RCTL), +or the process that crashed (corestop). .It Li port Hub port number (USB). .It Li product @@ -521,6 +524,9 @@ .It Sy Type .It Li coredump Notification that a process has crashed and dumped core. +.It Li corestop +Notification that a process would crash, but was stopped +just before executing the offending instruction. .El .El .Pp Index: sys/compat/linux/linux_file.c =================================================================== --- sys/compat/linux/linux_file.c +++ sys/compat/linux/linux_file.c @@ -1371,9 +1371,10 @@ case LINUX_F_DUPFD_CLOEXEC: return (kern_fcntl(td, args->fd, F_DUPFD_CLOEXEC, args->arg)); + default: + linux_msg(td, "unsupported fcntl cmd %d\n", args->cmd); + return (EINVAL); } - - return (EINVAL); } int Index: sys/compat/linux/linux_futex.c =================================================================== --- sys/compat/linux/linux_futex.c +++ sys/compat/linux/linux_futex.c @@ -760,7 +760,8 @@ clockrt = args->op & LINUX_FUTEX_CLOCK_REALTIME; args->op = args->op & ~LINUX_FUTEX_CLOCK_REALTIME; if (clockrt && args->op != LINUX_FUTEX_WAIT_BITSET && - args->op != LINUX_FUTEX_WAIT_REQUEUE_PI) { + args->op != LINUX_FUTEX_WAIT_REQUEUE_PI && + args->op != LINUX_FUTEX_WAIT) { LIN_SDT_PROBE0(futex, linux_sys_futex, unimplemented_clockswitch); LIN_SDT_PROBE1(futex, linux_sys_futex, return, ENOSYS); Index: sys/compat/linux/linux_mib.h =================================================================== --- sys/compat/linux/linux_mib.h +++ sys/compat/linux/linux_mib.h @@ -62,6 +62,7 @@ #define linux_use26(t) (linux_kernver(t) >= LINUX_KERNVER_2006000) +extern int linux_debug; extern int linux_default_openfiles; extern int linux_ignore_ip_recverr; extern int linux_preserve_vstatus; Index: sys/compat/linux/linux_mib.c =================================================================== --- sys/compat/linux/linux_mib.c +++ sys/compat/linux/linux_mib.c @@ -63,6 +63,11 @@ SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Linux mode"); +int linux_debug = 1; +SYSCTL_INT(_compat_linux, OID_AUTO, debug, CTLFLAG_RWTUN, + &linux_debug, 0, + "Log warnings from linux(4); or 0 to disable"); + int linux_default_openfiles = 1024; SYSCTL_INT(_compat_linux, OID_AUTO, default_openfiles, CTLFLAG_RWTUN, &linux_default_openfiles, 0, Index: sys/compat/linux/linux_socket.h =================================================================== --- sys/compat/linux/linux_socket.h +++ sys/compat/linux/linux_socket.h @@ -170,12 +170,6 @@ /* Socket defines */ #define LINUX_SOL_SOCKET 1 -#define LINUX_SOL_IP 0 -#define LINUX_SOL_TCP 6 -#define LINUX_SOL_UDP 17 -#define LINUX_SOL_IPV6 41 -#define LINUX_SOL_IPX 256 -#define LINUX_SOL_AX25 257 #define LINUX_SO_DEBUG 1 #define LINUX_SO_REUSEADDR 2 Index: sys/compat/linux/linux_socket.c =================================================================== --- sys/compat/linux/linux_socket.c +++ sys/compat/linux/linux_socket.c @@ -92,10 +92,9 @@ linux_to_bsd_sockopt_level(int level) { - switch (level) { - case LINUX_SOL_SOCKET: + if (level == LINUX_SOL_SOCKET) return (SOL_SOCKET); - } + /* Remaining values are RFC-defined protocol numbers. */ return (level); } @@ -103,10 +102,8 @@ bsd_to_linux_sockopt_level(int level) { - switch (level) { - case SOL_SOCKET: + if (level == SOL_SOCKET) return (LINUX_SOL_SOCKET); - } return (level); } @@ -946,7 +943,7 @@ struct msghdr msg; struct l_cmsghdr linux_cmsg; struct l_cmsghdr *ptr_cmsg; - struct l_msghdr linux_msg; + struct l_msghdr linux_msghdr; struct iovec *iov; socklen_t datalen; struct sockaddr *sa; @@ -958,7 +955,7 @@ l_size_t clen; int error, fflag; - error = copyin(msghdr, &linux_msg, sizeof(linux_msg)); + error = copyin(msghdr, &linux_msghdr, sizeof(linux_msghdr)); if (error != 0) return (error); @@ -969,10 +966,11 @@ * order to handle this case. This should be checked, but allows the * Linux ping to work. */ - if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0) - linux_msg.msg_control = PTROUT(NULL); + if (PTRIN(linux_msghdr.msg_control) != NULL && + linux_msghdr.msg_controllen == 0) + linux_msghdr.msg_control = PTROUT(NULL); - error = linux_to_bsd_msghdr(&msg, &linux_msg); + error = linux_to_bsd_msghdr(&msg, &linux_msghdr); if (error != 0) return (error); @@ -1010,7 +1008,7 @@ goto bad; } - if (linux_msg.msg_controllen >= sizeof(struct l_cmsghdr)) { + if (linux_msghdr.msg_controllen >= sizeof(struct l_cmsghdr)) { error = ENOBUFS; control = m_get(M_WAITOK, MT_CONTROL); @@ -1018,8 +1016,8 @@ data = mtod(control, void *); datalen = 0; - ptr_cmsg = PTRIN(linux_msg.msg_control); - clen = linux_msg.msg_controllen; + ptr_cmsg = PTRIN(linux_msghdr.msg_control); + clen = linux_msghdr.msg_controllen; do { error = copyin(ptr_cmsg, &linux_cmsg, sizeof(struct l_cmsghdr)); @@ -1154,7 +1152,7 @@ struct l_cmsghdr *linux_cmsg = NULL; struct l_ucred linux_ucred; socklen_t datalen, maxlen, outlen; - struct l_msghdr linux_msg; + struct l_msghdr linux_msghdr; struct iovec *iov, *uiov; struct mbuf *control = NULL; struct mbuf **controlp; @@ -1166,11 +1164,11 @@ void *data; int error, i, fd, fds, *fdp; - error = copyin(msghdr, &linux_msg, sizeof(linux_msg)); + error = copyin(msghdr, &linux_msghdr, sizeof(linux_msghdr)); if (error != 0) return (error); - error = linux_to_bsd_msghdr(msg, &linux_msg); + error = linux_to_bsd_msghdr(msg, &linux_msghdr); if (error != 0) return (error); @@ -1198,7 +1196,7 @@ goto bad; if (msg->msg_name) { - msg->msg_name = PTRIN(linux_msg.msg_name); + msg->msg_name = PTRIN(linux_msghdr.msg_name); error = bsd_to_linux_sockaddr(sa, &lsa, msg->msg_namelen); if (error == 0) error = copyout(lsa, PTRIN(msg->msg_name), @@ -1208,12 +1206,12 @@ goto bad; } - error = bsd_to_linux_msghdr(msg, &linux_msg); + error = bsd_to_linux_msghdr(msg, &linux_msghdr); if (error != 0) goto bad; - maxlen = linux_msg.msg_controllen; - linux_msg.msg_controllen = 0; + maxlen = linux_msghdr.msg_controllen; + linux_msghdr.msg_controllen = 0; if (control) { linux_cmsg = malloc(L_CMSG_HDRSZ, M_LINUX, M_WAITOK | M_ZERO); @@ -1221,7 +1219,7 @@ msg->msg_controllen = control->m_len; cm = CMSG_FIRSTHDR(msg); - outbuf = PTRIN(linux_msg.msg_control); + outbuf = PTRIN(linux_msghdr.msg_control); outlen = 0; while (cm != NULL) { linux_cmsg->cmsg_type = @@ -1230,6 +1228,7 @@ bsd_to_linux_sockopt_level(cm->cmsg_level); if (linux_cmsg->cmsg_type == -1 || cm->cmsg_level != SOL_SOCKET) { + linux_msg(curthread, "recvmsg: tukej; type %d level %d", linux_cmsg->cmsg_type, cm->cmsg_level); error = EINVAL; goto bad; } @@ -1287,7 +1286,7 @@ error = EMSGSIZE; goto bad; } else { - linux_msg.msg_flags |= LINUX_MSG_CTRUNC; + linux_msghdr.msg_flags |= LINUX_MSG_CTRUNC; m_dispose_extcontrolm(control); goto out; } @@ -1309,11 +1308,11 @@ cm = CMSG_NXTHDR(msg, cm); } - linux_msg.msg_controllen = outlen; + linux_msghdr.msg_controllen = outlen; } out: - error = copyout(&linux_msg, msghdr, sizeof(linux_msg)); + error = copyout(&linux_msghdr, msghdr, sizeof(linux_msghdr)); bad: if (control != NULL) { @@ -1810,7 +1809,7 @@ return (linux_sendfile(td, arg)); } - uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what); + linux_msg(td, "socket type %d not implemented", args->what); return (ENOSYS); } #endif /* __i386__ || __arm__ || (__amd64__ && COMPAT_LINUX32) */ Index: sys/compat/linux/linux_util.c =================================================================== --- sys/compat/linux/linux_util.c +++ sys/compat/linux/linux_util.c @@ -91,6 +91,9 @@ va_list ap; struct proc *p; + if (linux_debug == 0) + return; + p = td->td_proc; printf("linux: pid %d (%s): ", (int)p->p_pid, p->p_comm); va_start(ap, fmt); Index: sys/kern/kern_sig.c =================================================================== --- sys/kern/kern_sig.c +++ sys/kern/kern_sig.c @@ -104,6 +104,7 @@ "struct thread *", "struct proc *", "int"); static int coredump(struct thread *); +static void corestop(struct thread *, int sig); static int killpg1(struct thread *td, int sig, int pgid, int all, ksiginfo_t *ksi); static int issignal(struct thread *td); @@ -189,6 +190,10 @@ SYSCTL_INT(_kern, OID_AUTO, coredump, CTLFLAG_RW, &do_coredump, 0, "Enable/Disable coredumps"); +static int do_corestop = 0; +SYSCTL_INT(_kern, OID_AUTO, corestop, CTLFLAG_RW, + &do_corestop, 0, "Stop the process instead of dumping core"); + static int set_core_nodump_flag = 0; SYSCTL_INT(_kern, OID_AUTO, nodump_coredump, CTLFLAG_RW, &set_core_nodump_flag, 0, "Enable setting the NODUMP flag on coredump files"); @@ -3104,6 +3109,9 @@ * Default action, where the default is to kill * the process. (Other cases were ignored above.) */ + if (do_corestop && (sigprop(sig) & SIGPROP_CORE)) + corestop(td, sig); + mtx_unlock(&ps->ps_mtx); proc_td_siginfo_capture(td, &ksi.ksi_info); sigexit(td, sig); @@ -3769,6 +3777,81 @@ #endif free(name, M_TEMP); return (error); +} + +/* + * Stop the crashed process, so it can be analyzed and then killed. + */ +static void +corestop(struct thread *td, int sig) +{ + char strbuf[32]; + struct proc *p; + struct sigacts *ps; + int must_exit, rv; + + p = td->td_proc; + PROC_LOCK_ASSERT(p, MA_OWNED); + ps = p->p_sigacts; + mtx_assert(&ps->ps_mtx, MA_OWNED); + + /* + * The policy here mimics the one from coredump(). + */ + if ((!sugid_coredump && (p->p_flag & P_SUGID) != 0) || + (p->p_flag2 & P2_NOTRACE) != 0) { + return; + } + + must_exit = thread_single(p, SINGLE_NO_EXIT); + if (must_exit) + return; + + KASSERT((p->p_flag2 & P2_CORESTOPPED) == 0, + ("%s: P2_CORESTOPPED already set", __func__)); + + p->p_flag2 |= P2_CORESTOPPED; + + log(LOG_INFO, + "pid %d (%s), jid %d, uid %d: would exit on " + "signal %d (core stopped)\n", p->p_pid, p->p_comm, + p->p_ucred->cr_prison->pr_id, + td->td_ucred->cr_uid, + sig &~ WCOREFLAG); + rv = snprintf(strbuf, sizeof(strbuf), + "pid=%d euid=%d", p->p_pid, p->p_ucred->cr_uid); + KASSERT(rv > 0 && rv < sizeof(strbuf), + ("%s: snprintf failed\n", __func__)); + devctl_notify("kernel", "signal", "corestop", strbuf); + +again: + /* + * Actually stop executing now. The only caveat is that we need + * to provide a way for the debugger to attach and detach, more + * than once. This is somewhat complicated due to PT_ATTACH + * and PT_DETACH insisting on resuming the process. + */ + mtx_unlock(&ps->ps_mtx); + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, + &p->p_mtx.lock_object, "corestopping"); + sigqueue_delete(&td->td_sigqueue, sig); + sigqueue_delete(&p->p_sigqueue, sig); + p->p_flag |= P_STOPPED_SIG; + p->p_xsig = sig; + PROC_SLOCK(p); + sig_suspend_threads(td, p, 0); + thread_suspend_switch(td, p); + PROC_SUNLOCK(p); + mtx_lock(&ps->ps_mtx); + + /* + * Handle signals as long as we are being ptraced. + */ + if (p->p_flag2 & P2_PTRACE_FSTP) { + while ((sig = cursig(td)) != 0) + postsig(sig); + goto again; + } } /* Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -1089,11 +1089,18 @@ * Unsuspend all threads. To leave a thread * suspended, use PT_SUSPEND to suspend it before * continuing the process. + * + * Except for processes that are corestopped - those + * need to remain stopped, otherwise it wouldn't be + * possible to reattach to them. */ - PROC_SLOCK(p); - p->p_flag &= ~(P_STOPPED_TRACE | P_STOPPED_SIG | P_WAITED); - thread_unsuspend(p); - PROC_SUNLOCK(p); + p->p_flag &= ~(P_STOPPED_TRACE | P_WAITED); + if ((p->p_flag2 & P2_CORESTOPPED) == 0 || data != 0) { + p->p_flag &= ~P_STOPPED_SIG; + PROC_SLOCK(p); + thread_unsuspend(p); + PROC_SUNLOCK(p); + } break; case PT_WRITE_I: Index: sys/kern/tty_tty.c =================================================================== --- sys/kern/tty_tty.c +++ sys/kern/tty_tty.c @@ -63,10 +63,14 @@ { struct proc *p; - if (*dev != NULL) + if (*dev != NULL) { + printf("%s: *dev != null, %d (%s)\n", __func__, curproc->p_pid, curproc->p_comm); return; - if (strcmp(name, "tty")) + } + if (strcmp(name, "tty")) { + printf("%s: bingo, name != tty, %d (%s)\n", __func__, curproc->p_pid, curproc->p_comm); return; + } p = curproc; sx_sunlock(&clone_drain_lock); sx_slock(&proctree_lock); Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -784,6 +784,7 @@ #define P2_PROTMAX_DISABLE 0x00000400 /* Force disable implied PROT_MAX. */ #define P2_STKGAP_DISABLE 0x00000800 /* Disable stack gap for MAP_STACK */ #define P2_STKGAP_DISABLE_EXEC 0x00001000 /* Stack gap disabled after exec */ +#define P2_CORESTOPPED 0x00002000 /* Stopped instead of dumping core */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ Index: tests/sys/audit/Makefile =================================================================== --- tests/sys/audit/Makefile +++ tests/sys/audit/Makefile @@ -49,7 +49,7 @@ TEST_METADATA+= timeout="30" TEST_METADATA+= required_user="root" TEST_METADATA+= is_exclusive="true" -TEST_METADATA+= required_files="/etc/rc.d/auditd" +TEST_METADATA+= required_files="/etc/rc.d/auditd /dev/auditpipe" WARNS?= 6