aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/sys_process.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/sys_process.c')
-rw-r--r--sys/kern/sys_process.c85
1 files changed, 84 insertions, 1 deletions
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index 27849de47fde..50157106a35e 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -51,6 +51,8 @@ __FBSDID("$FreeBSD$");
#include <sys/sx.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
+#include <sys/caprights.h>
+#include <sys/filedesc.h>
#include <machine/reg.h>
@@ -469,6 +471,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
struct ptrace_io_desc piod;
struct ptrace_lwpinfo pl;
struct ptrace_vm_entry pve;
+ struct ptrace_coredump pc;
struct dbreg dbreg;
struct fpreg fpreg;
struct reg reg;
@@ -519,6 +522,12 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
case PT_VM_ENTRY:
error = copyin(uap->addr, &r.pve, sizeof(r.pve));
break;
+ case PT_COREDUMP:
+ if (uap->data != sizeof(r.pc))
+ error = EINVAL;
+ else
+ error = copyin(uap->addr, &r.pc, uap->data);
+ break;
default:
addr = uap->addr;
break;
@@ -632,6 +641,22 @@ proc_can_ptrace(struct thread *td, struct proc *p)
return (0);
}
+
+static struct thread *
+ptrace_sel_coredump_thread(struct proc *p)
+{
+ struct thread *td2;
+
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ MPASS((p->p_flag & P_STOPPED_TRACE) != 0);
+
+ FOREACH_THREAD_IN_PROC(p, td2) {
+ if ((td2->td_dbgflags & TDB_SSWITCH) != 0)
+ return (td2);
+ }
+ return (NULL);
+}
+
int
kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
{
@@ -642,6 +667,9 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
struct ptrace_io_desc *piod = NULL;
struct ptrace_lwpinfo *pl;
struct ptrace_sc_ret *psr;
+ struct file *fp;
+ struct ptrace_coredump *pc;
+ struct thr_coredump_req *tcq;
int error, num, tmp;
lwpid_t tid = 0, *buf;
#ifdef COMPAT_FREEBSD32
@@ -1348,6 +1376,62 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
PROC_LOCK(p);
break;
+ case PT_COREDUMP:
+ pc = addr;
+ CTR2(KTR_PTRACE, "PT_COREDUMP: pid %d, fd %d",
+ p->p_pid, pc->pc_fd);
+
+ if ((pc->pc_flags & ~(PC_COMPRESS | PC_ALL)) != 0) {
+ error = EINVAL;
+ break;
+ }
+ PROC_UNLOCK(p);
+
+ tcq = malloc(sizeof(*tcq), M_TEMP, M_WAITOK | M_ZERO);
+ fp = NULL;
+ error = fget_write(td, pc->pc_fd, &cap_write_rights, &fp);
+ if (error != 0)
+ goto coredump_cleanup_nofp;
+ if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VREG) {
+ error = EPIPE;
+ goto coredump_cleanup;
+ }
+
+ PROC_LOCK(p);
+ error = proc_can_ptrace(td, p);
+ if (error != 0)
+ goto coredump_cleanup_locked;
+
+ td2 = ptrace_sel_coredump_thread(p);
+ if (td2 == NULL) {
+ error = EBUSY;
+ goto coredump_cleanup_locked;
+ }
+ KASSERT((td2->td_dbgflags & TDB_COREDUMPRQ) == 0,
+ ("proc %d tid %d req coredump", p->p_pid, td2->td_tid));
+
+ tcq->tc_vp = fp->f_vnode;
+ tcq->tc_limit = pc->pc_limit == 0 ? OFF_MAX : pc->pc_limit;
+ tcq->tc_flags = SVC_PT_COREDUMP;
+ if ((pc->pc_flags & PC_COMPRESS) == 0)
+ tcq->tc_flags |= SVC_NOCOMPRESS;
+ if ((pc->pc_flags & PC_ALL) != 0)
+ tcq->tc_flags |= SVC_ALL;
+ td2->td_coredump = tcq;
+ td2->td_dbgflags |= TDB_COREDUMPRQ;
+ thread_run_flash(td2);
+ while ((td2->td_dbgflags & TDB_COREDUMPRQ) != 0)
+ msleep(p, &p->p_mtx, PPAUSE, "crdmp", 0);
+ error = tcq->tc_error;
+coredump_cleanup_locked:
+ PROC_UNLOCK(p);
+coredump_cleanup:
+ fdrop(fp, td);
+coredump_cleanup_nofp:
+ free(tcq, M_TEMP);
+ PROC_LOCK(p);
+ break;
+
default:
#ifdef __HAVE_PTRACE_MACHDEP
if (req >= PT_FIRSTMACH) {
@@ -1360,7 +1444,6 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
error = EINVAL;
break;
}
-
out:
/* Drop our hold on this process now that the request has completed. */
_PRELE(p);