The branch main has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=87a64872cd3166a09b58aac28cdb95380d6a38eb

commit 87a64872cd3166a09b58aac28cdb95380d6a38eb
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2021-04-23 13:26:01 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2021-05-03 16:18:26 +0000

    Add ptrace(PT_COREDUMP)
    
    It writes the core of live stopped process to the file descriptor
    provided as an argument.
    
    Based on the initial version from https://reviews.freebsd.org/D29691,
    submitted by Michał Górny <[email protected]>.
    
    Reviewed by:    markj
    Tested by:      pho
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D29955
---
 lib/libc/sys/ptrace.2                 | 66 ++++++++++++++++++++++++++-
 sys/compat/freebsd32/freebsd32.h      |  7 +++
 sys/compat/freebsd32/freebsd32_misc.c | 12 +++++
 sys/kern/kern_sig.c                   | 42 +++++++++++++++++
 sys/kern/sys_process.c                | 85 ++++++++++++++++++++++++++++++++++-
 sys/sys/proc.h                        |  2 +
 sys/sys/ptrace.h                      | 21 +++++++++
 7 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2
index 8e9c5d8ab87a..6148e6d333d5 100644
--- a/lib/libc/sys/ptrace.2
+++ b/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,70 @@ and extends up to
 The
 .Fa data
 argument is ignored.
+.It Dv PT_COREDUMP
+This request creates a coredump for the stopped program.
+The
+.Fa addr
+argument specifies a pointer to a
+.Vt "struct ptrace_coredump" ,
+which is defined as follows:
+.Bd -literal
+struct ptrace_coredump {
+       int             pc_fd;
+       uint32_t        pc_flags;
+       off_t           pc_limit;
+};
+.Ed
+The fields of the structure are:
+.Bl -tag -width pc_flags
+.It Dv pc_fd
+File descriptor to write the dump to.
+It must refer to a regular file, opened for writing.
+.It Dv pc_flags
+Flags.
+The following flags are defined:
+.Bl -tag -width PC_COMPRESS
+.It Dv PC_COMPRESS
+Request compression of the dump.
+.It Dv PC_ALL
+Include non-dumpable entries into the dump.
+The dumper ignores
+.Dv MAP_NOCORE
+flag of the process map entry, but device mappings are not dumped even with
+.Dv PC_ALL
+set.
+.El
+.It Dv pc_limit
+Maximum size of the coredump.
+Specify zero for no limit.
+.El
+.Pp
+The size of
+.Vt "struct ptrace_coredump"
+must be passed in
+.Fa data .
+.Pp
+The process must be stopped before dumping core.
+A single thread in the target process is temporarily unsuspended
+in kernel to write the dump.
+If the
+.Nm
+call fails before a thread is unsuspended, there is no event to
+.Xr waitpid 2
+for.
+If a thread was unsuspended, it will stop again before the
+.Nm
+call returns, and the process must be waited upon using
+.Xr waitpid 2
+to consume the new stop event.
+Since it is hard to deduce whether a thread was unsuspended before
+an error occurred, it is recommended to unconditionally perform
+.Xr waitpid 2
+with
+.Dv WNOHANG
+flag after
+.Dv PT_COREDUMP ,
+and silently accept zero result from it.
 .El
 .Sh ARM MACHINE-SPECIFIC REQUESTS
 .Bl -tag -width "Dv PT_SETVFPREGS"
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 c2caa7c10544..f221397e91dc 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -932,6 +932,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;
@@ -943,6 +944,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;
@@ -1021,6 +1023,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 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);
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index b959681e992b..ebd396b4aebe 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -377,6 +377,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
@@ -486,6 +487,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);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-main
To unsubscribe, send any mail to "[email protected]"

Reply via email to