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,22 @@ 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 +.Vt "struct ptrace_coredump" +must be passed in +.Fa data . .El .Sh ARM MACHINE-SPECIFIC REQUESTS .Bl -tag -width "Dv PT_SETVFPREGS" Index: sys/kern/imgact_elf.c =================================================================== --- sys/kern/imgact_elf.c +++ sys/kern/imgact_elf.c @@ -1705,7 +1705,7 @@ } /* Create a compression stream if necessary. */ - if (compress_user_cores != 0) { + if (compress_user_cores != 0 && !(flags & SVC_NOCOMPRESS)) { params.comp = compressor_init(core_compressed_write, compress_user_cores, CORE_BUF_SIZE, compress_user_cores_level, ¶ms); Index: sys/kern/subr_trap.c =================================================================== --- sys/kern/subr_trap.c +++ sys/kern/subr_trap.c @@ -193,6 +193,42 @@ #endif } +static void +ptrace_coredump(struct thread *td) +{ + struct proc *p = td->td_proc; + struct vnode *vp; + + PROC_LOCK_ASSERT(p, MA_OWNED); + if ((td->td_dbgflags & TDB_COREDUMP) == 0) + return; + KASSERT(td->td_coredump, ("td_coredump is NULL")); + if (p->p_sysent->sv_coredump == NULL) { + td->td_coredump->tc_error = ENOSYS; + return; + } + + if ((p->p_flag & P_HADTHREADS) != 0) { + if (thread_single(p, SINGLE_BOUNDARY) != 0) { + td->td_coredump->tc_error = EBUSY; + return; + } + } + + vp = td->td_coredump->tc_vn; + PROC_UNLOCK(p); + td->td_coredump->tc_error = + p->p_sysent->sv_coredump(td, vp, td->td_coredump->tc_limit, + SVC_NOCOMPRESS); + PROC_LOCK(p); + + if ((p->p_flag & P_HADTHREADS) != 0) + thread_single_end(p, SINGLE_BOUNDARY); + + td->td_dbgflags &= ~TDB_COREDUMP; + wakeup(p); +} + /* * Process an asynchronous software trap. * This is relatively easy. @@ -355,6 +391,11 @@ kern_sigprocmask(td, SIG_SETMASK, &td->td_oldsigmask, NULL, 0); } + PROC_LOCK(p); + if (td->td_dbgflags & TDB_COREDUMP) + 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,46 @@ break; } break; + case PT_COREDUMP: + { + struct ptrace_coredump *pc = addr; + struct thread_coredump *coredump_data; + CTR2(KTR_PTRACE, + "PT_COREDUMP: pid %d, fd = %d", + p->p_pid, pc->pc_fd); + + if (td2->td_dbgflags & TDB_COREDUMP) { + error = EBUSY; + break; + } + + 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; + fdrop(fp, td); + break; + } + + PROC_UNLOCK(p); + coredump_data = malloc(sizeof(*td2->td_coredump), + M_SUBPROC, M_WAITOK | M_ZERO); + PROC_LOCK(p); + + coredump_data->tc_vn = fp->f_vnode; + coredump_data->tc_limit = pc->pc_limit <= 0 ? + OFF_MAX : pc->pc_limit; + td2->td_coredump = coredump_data; + td2->td_dbgflags |= TDB_COREDUMP; + thread_lock(td); + td2->td_flags |= TDF_ASTPENDING; + thread_unlock(td); + + break; + } case PT_DETACH: /* * Clear P_TRACED before reparenting @@ -1052,6 +1110,9 @@ break; } + if (error != 0) + break; + sx_xunlock(&proctree_lock); proctree_locked = 0; @@ -1313,6 +1374,19 @@ } out: + if (req == PT_COREDUMP && error != 0) { + void *coredump_data; + + while ((td2->td_dbgflags & TDB_COREDUMP) != 0) + msleep(p, &p->p_mtx, PPAUSE, "crdmp", 0); + MPASS(td2->td_coredump != NULL); + error = td2->td_coredump->tc_error; + fdrop(fp, td); + coredump_data = td2->td_coredump; + td2->td_coredump = NULL; + free(coredump_data, M_SUBPROC); + } + /* 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 @@ -222,6 +222,12 @@ uint64_t rux_tu; /* (c) Previous total time in usec. */ }; +struct thread_coredump { + struct vnode *tc_vn; /* vnode to write coredump to. */ + off_t tc_limit; /* (c) max coredump file size. */ + int tc_error; /* (c) coredump return value. */ +}; + /* * Kernel runnable context (thread). * This is what is put to sleep and reactivated. @@ -377,6 +383,7 @@ int td_oncpu; /* (t) Which cpu we are on. */ void *td_lkpi_task; /* LinuxKPI task struct pointer */ int td_pmcpend; + struct thread_coredump *td_coredump; /* (c) coredump request. */ #ifdef EPOCH_TRACE SLIST_HEAD(, epoch_tracker) td_epochs; #endif @@ -485,6 +492,7 @@ #define TDB_VFORK 0x00000800 /* vfork indicator for ptrace() */ #define TDB_FSTP 0x00001000 /* The thread is PT_ATTACH leader */ #define TDB_STEP 0x00002000 /* (x86) PSL_T set for PT_STEP */ +#define TDB_COREDUMP 0x00004000 /* Coredump request */ /* * "Private" flags kept in td_pflags: 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); Index: sys/sys/sysent.h =================================================================== --- sys/sys/sysent.h +++ sys/sys/sysent.h @@ -171,6 +171,9 @@ #define SV_ABI_CLOUDABI 17 #define SV_ABI_UNDEF 255 +/* sv_coredump flags */ +#define SVC_NOCOMPRESS 0x000001 /* disable compression. */ + #ifdef _KERNEL extern struct sysentvec aout_sysvec; extern struct sysent sysent[];