Index: lib/libc/sys/ptrace.2 =================================================================== --- lib/libc/sys/ptrace.2 +++ lib/libc/sys/ptrace.2 @@ -2,7 +2,7 @@ .\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $ .\" .\" This file is in the public domain. -.Dd July 15, 2019 +.Dd April 10, 2021 .Dt PTRACE 2 .Os .Sh NAME @@ -807,6 +807,20 @@ The .Fa data argument is ignored. +.It Dv PT_COREDUMP +This request creates a coredump for the stopped program. +.Fa addr +argument specifies a pointer to a +.Vt "struct ptrace_coredump" , +which is defined as follows: +.Bd -literal +struct ptrace_coredump { + TODO; +}; +.Ed +.Pp +The size of the structmust be passed in +.Fa data . .El .Sh ARM MACHINE-SPECIFIC REQUESTS .Bl -tag -width "Dv PT_SETVFPREGS" Index: sys/kern/subr_trap.c =================================================================== --- sys/kern/subr_trap.c +++ sys/kern/subr_trap.c @@ -193,6 +193,39 @@ #endif } +static void +ptrace_coredump(struct thread *td) +{ + struct proc *p = td->td_proc; + struct vnode *vp; + + if ((td->td_flags & TDF_COREDUMP) == 0) + return; + if (p->p_sysent->sv_coredump == NULL) { + td->td_coredump_error = ENOSYS; + return; + } + + if ((p->p_flag & P_HADTHREADS) != 0) { + if (thread_single(p, SINGLE_BOUNDARY) != 0) { + td->td_coredump_error = EBUSY; + return; + } + } + + vp = td->td_coredump_vn; + PROC_UNLOCK(p); + td->td_coredump_error = + p->p_sysent->sv_coredump(td, vp, td->td_coredump_limit, 0); + PROC_LOCK(p); + + if ((p->p_flag & P_HADTHREADS) != 0) + thread_single_end(p, SINGLE_BOUNDARY); + + td->td_flags &= ~TDF_COREDUMP; + wakeup(p); +} + /* * Process an asynchronous software trap. * This is relatively easy. @@ -355,6 +388,12 @@ kern_sigprocmask(td, SIG_SETMASK, &td->td_oldsigmask, NULL, 0); } + if (td->td_flags & TDF_COREDUMP) { + PROC_LOCK(p); + ptrace_coredump(td); + PROC_UNLOCK(p); + } + #ifdef RACCT if (__predict_false(racct_enable && p->p_throttled != 0)) racct_proc_throttled(p); Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -51,6 +51,8 @@ #include #include #include +#include +#include #include @@ -469,6 +471,7 @@ 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,14 @@ 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); + if (r.pc.pc_flags != 0) + error = EINVAL; + break; default: addr = uap->addr; break; @@ -611,6 +622,7 @@ struct ptrace_io_desc *piod = NULL; struct ptrace_lwpinfo *pl; struct ptrace_sc_ret *psr; + struct file *fp; int error, num, tmp; int proctree_locked = 0; lwpid_t tid = 0, *buf; @@ -635,6 +647,7 @@ case PT_SET_EVENT_MASK: case PT_DETACH: case PT_GET_SC_ARGS: + case PT_COREDUMP: sx_xlock(&proctree_lock); proctree_locked = 1; break; @@ -950,6 +963,11 @@ case PT_TO_SCX: case PT_SYSCALL: case PT_DETACH: + case PT_COREDUMP: + /* PT_COREDUMP does not pass signal in data. */ + if (req == PT_COREDUMP) + data = 0; + /* Zero means do not send any signal */ if (data < 0 || data > _SIG_MAXSIG) { error = EINVAL; @@ -1003,6 +1021,30 @@ break; } break; + case PT_COREDUMP: + { + struct ptrace_coredump *pc = addr; + CTR2(KTR_PTRACE, + "PT_COREDUMP: pid %d, fd = %d", + p->p_pid, pc->pc_fd); + + error = getvnode(td, pc->pc_fd, &cap_write_rights, &fp); + if (error != 0) + break; + + /* Do not try writing to non-regular files. */ + if (fp->f_vnode->v_type != VREG) { + error = EFAULT; + break; + } + + td2->td_coredump_vn = fp->f_vnode; + if (pc->pc_limit <= 0) + td2->td_coredump_limit = OFF_MAX; + else + td2->td_coredump_limit = pc->pc_limit; + break; + } case PT_DETACH: /* * Clear P_TRACED before reparenting @@ -1052,12 +1094,19 @@ break; } + if (error != 0) + break; + sx_xunlock(&proctree_lock); proctree_locked = 0; sendsig: MPASS(proctree_locked == 0); + KASSERT((td2->td_flags & TDF_COREDUMP) == 0, + ("Coredump already requested")); + td2->td_flags |= TDF_COREDUMP; + /* * Clear the pending event for the thread that just * reported its event (p_xthread). This may not be @@ -1313,6 +1362,13 @@ } out: + if (req == PT_COREDUMP) { + while ((td2->td_flags & TDF_COREDUMP) != 0) + msleep(p, &p->p_mtx, PPAUSE, "crdmp", 0); + error = td2->td_coredump_error; + fdrop(fp, td); + } + /* Drop our hold on this process now that the request has completed. */ _PRELE(p); fail: Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -377,6 +377,9 @@ int td_oncpu; /* (t) Which cpu we are on. */ void *td_lkpi_task; /* LinuxKPI task struct pointer */ int td_pmcpend; + struct vnode *td_coredump_vn; /* (c) vnode to write coredump to. */ + off_t td_coredump_limit; /* (c) max coredump file size. */ + int td_coredump_error; /* (c) coredump return value. */ #ifdef EPOCH_TRACE SLIST_HEAD(, epoch_tracker) td_epochs; #endif @@ -443,7 +446,7 @@ #define TDF_TIMEOUT 0x00000010 /* Timing out during sleep. */ #define TDF_IDLETD 0x00000020 /* This is a per-CPU idle thread. */ #define TDF_CANSWAP 0x00000040 /* Thread can be swapped. */ -#define TDF_UNUSED80 0x00000080 /* unused. */ +#define TDF_COREDUMP 0x00000080 /* Coredump request. */ #define TDF_KTH_SUSP 0x00000100 /* kthread is suspended */ #define TDF_ALLPROCSUSP 0x00000200 /* suspended by SINGLE_ALLPROC */ #define TDF_BOUNDARY 0x00000400 /* Thread suspended at user boundary */ Index: sys/sys/ptrace.h =================================================================== --- sys/sys/ptrace.h +++ 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,6 +178,14 @@ char *pve_path; /* Path name of object. */ }; +/* Argument structure for PT_COREDUMP. */ +struct ptrace_coredump { + int pc_fd; /* File descriptor to write dump to. */ + off_t pc_limit; /* Maximum size of the coredump, + 0 for no limit. */ + uint32_t pc_flags; /* Flags (currently reserved). */ +}; + #ifdef _KERNEL int ptrace_set_pc(struct thread *_td, unsigned long _addr);