diff options
Diffstat (limited to 'sys/kern/sys_process.c')
-rw-r--r-- | sys/kern/sys_process.c | 85 |
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); |