aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-04-23 13:26:01 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-05-10 01:03:06 +0000
commit51af25060861869b9ba59601eb67eae2445e61e0 (patch)
treeb6cc1a4ad228370decc213c137e021aa15667a0f /sys
parent2b348ecde121f57020addd77d59a1b79b8046c79 (diff)
Add ptrace(PT_COREDUMP)
(cherry picked from commit 87a64872cd3166a09b58aac28cdb95380d6a38eb)
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/freebsd32/freebsd32.h7
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c12
-rw-r--r--sys/kern/kern_sig.c42
-rw-r--r--sys/kern/sys_process.c85
-rw-r--r--sys/sys/proc.h2
-rw-r--r--sys/sys/ptrace.h21
6 files changed, 168 insertions, 1 deletions
diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h
index 4227d9037afb..2e4f5155cbf4 100644
--- a/sys/compat/freebsd32/freebsd32.h
+++ b/sys/compat/freebsd32/freebsd32.h
@@ -429,4 +429,11 @@ struct timex32 {
int32_t stbcnt;
};
+struct ptrace_coredump32 {
+ int pc_fd;
+ uint32_t pc_flags;
+ uint32_t pc_limit1, pc_limit2;
+};
+
+
#endif /* !_COMPAT_FREEBSD32_FREEBSD32_H_ */
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index b7db1c4468d7..d258afa2352d 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -920,6 +920,7 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
struct ptrace_io_desc piod;
struct ptrace_lwpinfo pl;
struct ptrace_vm_entry pve;
+ struct ptrace_coredump pc;
struct dbreg32 dbreg;
struct fpreg32 fpreg;
struct reg32 reg;
@@ -931,6 +932,7 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
struct ptrace_io_desc32 piod;
struct ptrace_lwpinfo32 pl;
struct ptrace_vm_entry32 pve;
+ struct ptrace_coredump32 pc;
uint32_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret32 psr;
} r32;
@@ -1009,6 +1011,16 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
CP(r32.pve, r.pve, pve_fsid);
PTRIN_CP(r32.pve, r.pve, pve_path);
break;
+ case PT_COREDUMP:
+ if (uap->data != sizeof(r32.pc))
+ error = EINVAL;
+ else
+ error = copyin(uap->addr, &r32.pc, uap->data);
+ CP(r32.pc, r.pc, pc_fd);
+ CP(r32.pc, r.pc, pc_flags);
+ r.pc.pc_limit = PAIR32TO64(off_t, r32.pc.pc_limit);
+ data = sizeof(r.pc);
+ break;
default:
addr = uap->addr;
break;
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 445582a176c8..0453d3b2702c 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2521,6 +2521,42 @@ out:
thread_unlock(td);
}
+static void
+ptrace_coredump(struct thread *td)
+{
+ struct proc *p;
+ struct thr_coredump_req *tcq;
+ void *rl_cookie;
+
+ MPASS(td == curthread);
+ p = td->td_proc;
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ if ((td->td_dbgflags & TDB_COREDUMPRQ) == 0)
+ return;
+ KASSERT((p->p_flag & P_STOPPED_TRACE) != 0, ("not stopped"));
+
+ tcq = td->td_coredump;
+ KASSERT(tcq != NULL, ("td_coredump is NULL"));
+
+ if (p->p_sysent->sv_coredump == NULL) {
+ tcq->tc_error = ENOSYS;
+ goto wake;
+ }
+
+ PROC_UNLOCK(p);
+ rl_cookie = vn_rangelock_wlock(tcq->tc_vp, 0, OFF_MAX);
+
+ tcq->tc_error = p->p_sysent->sv_coredump(td, tcq->tc_vp,
+ tcq->tc_limit, tcq->tc_flags);
+
+ vn_rangelock_unlock(tcq->tc_vp, rl_cookie);
+ PROC_LOCK(p);
+wake:
+ td->td_dbgflags &= ~TDB_COREDUMPRQ;
+ td->td_coredump = NULL;
+ wakeup(p);
+}
+
static int
sig_suspend_threads(struct thread *td, struct proc *p, int sending)
{
@@ -2651,6 +2687,12 @@ stopme:
td->td_dbgflags |= TDB_SSWITCH;
thread_suspend_switch(td, p);
td->td_dbgflags &= ~TDB_SSWITCH;
+ if ((td->td_dbgflags & TDB_COREDUMPRQ) != 0) {
+ PROC_SUNLOCK(p);
+ ptrace_coredump(td);
+ PROC_SLOCK(p);
+ goto stopme;
+ }
if (p->p_xthread == td)
p->p_xthread = NULL;
if (!(p->p_flag & P_TRACED))
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index e89fc6dff7e0..bc38a8ee585d 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);
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index e4e01f9bec16..8e2a081eb027 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -376,6 +376,7 @@ struct thread {
int td_oncpu; /* (t) Which cpu we are on. */
void *td_lkpi_task; /* LinuxKPI task struct pointer */
int td_pmcpend;
+ void *td_coredump; /* (c) coredump request. */
#ifdef EPOCH_TRACE
SLIST_HEAD(, epoch_tracker) td_epochs;
#endif
@@ -485,6 +486,7 @@ do { \
#define TDB_FSTP 0x00001000 /* The thread is PT_ATTACH leader */
#define TDB_STEP 0x00002000 /* (x86) PSL_T set for PT_STEP */
#define TDB_SSWITCH 0x00004000 /* Suspended in ptracestop */
+#define TDB_COREDUMPRQ 0x00008000 /* Coredump request */
/*
* "Private" flags kept in td_pflags:
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
index 1ee42318e57e..06f01a04fd9d 100644
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -74,6 +74,8 @@
#define PT_GET_SC_ARGS 27 /* fetch syscall args */
#define PT_GET_SC_RET 28 /* fetch syscall results */
+#define PT_COREDUMP 29 /* create a coredump */
+
#define PT_GETREGS 33 /* get general-purpose registers */
#define PT_SETREGS 34 /* set general-purpose registers */
#define PT_GETFPREGS 35 /* get floating-point registers */
@@ -176,8 +178,27 @@ struct ptrace_vm_entry {
char *pve_path; /* Path name of object. */
};
+/* Argument structure for PT_COREDUMP */
+struct ptrace_coredump {
+ int pc_fd; /* File descriptor to write dump to. */
+ uint32_t pc_flags; /* Flags PC_* */
+ off_t pc_limit; /* Maximum size of the coredump,
+ 0 for no limit. */
+};
+
+/* Flags for PT_COREDUMP pc_flags */
+#define PC_COMPRESS 0x00000001 /* Allow compression */
+#define PC_ALL 0x00000002 /* Include non-dumpable entries */
+
#ifdef _KERNEL
+struct thr_coredump_req {
+ struct vnode *tc_vp; /* vnode to write coredump to. */
+ off_t tc_limit; /* max coredump file size. */
+ int tc_flags; /* user flags */
+ int tc_error; /* request result */
+};
+
int ptrace_set_pc(struct thread *_td, unsigned long _addr);
int ptrace_single_step(struct thread *_td);
int ptrace_clear_single_step(struct thread *_td);