aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-04-24 11:57:40 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-05-10 01:02:47 +0000
commit4495cd6e7e50e54515cc3d9b9faba7c6fb21042b (patch)
tree5b802e3bb6a8f13b7e2d693306a57d65b8a395b8
parent57d7b30ea855ee506b86ccba9b753bcdecda14a2 (diff)
downloadsrc-4495cd6e7e50e54515cc3d9b9faba7c6fb21042b.tar.gz
src-4495cd6e7e50e54515cc3d9b9faba7c6fb21042b.zip
ptrace: do not allow for parallel ptrace requests
(cherry picked from commit 9ebf9100bad129a92961572ac862781d6c5681c7)
-rw-r--r--sys/kern/sys_process.c49
-rw-r--r--sys/sys/proc.h1
2 files changed, 48 insertions, 2 deletions
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index c93bfe15324c..e89fc6dff7e0 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -604,11 +604,18 @@ proc_set_traced(struct proc *p, bool stop)
static int
proc_can_ptrace(struct thread *td, struct proc *p)
{
+ int error;
+
PROC_LOCK_ASSERT(p, MA_OWNED);
if ((p->p_flag & P_WEXIT) != 0)
return (ESRCH);
+ if ((error = p_cansee(td, p)) != 0)
+ return (error);
+ if ((error = p_candebug(td, p)) != 0)
+ return (error);
+
/* not being traced... */
if ((p->p_flag & P_TRACED) == 0)
return (EPERM);
@@ -640,10 +647,11 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
#ifdef COMPAT_FREEBSD32
int wrap32 = 0, safe = 0;
#endif
- bool proctree_locked;
+ bool proctree_locked, p2_req_set;
curp = td->td_proc;
proctree_locked = false;
+ p2_req_set = false;
/* Lock proctree before locking the process. */
switch (req) {
@@ -782,15 +790,47 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
/* FALLTHROUGH */
default:
+ /*
+ * Check for ptrace eligibility before waiting for
+ * holds to drain.
+ */
error = proc_can_ptrace(td, p);
if (error != 0)
goto fail;
+ /*
+ * Block parallel ptrace requests. Most important, do
+ * not allow other thread in debugger to continue the
+ * debuggee until coredump finished.
+ */
+ while ((p->p_flag2 & P2_PTRACEREQ) != 0) {
+ if (proctree_locked)
+ sx_xunlock(&proctree_lock);
+ error = msleep(&p->p_flag2, &p->p_mtx, PPAUSE | PCATCH |
+ (proctree_locked ? PDROP : 0), "pptrace", 0);
+ if (proctree_locked) {
+ sx_xlock(&proctree_lock);
+ PROC_LOCK(p);
+ }
+ if (error == 0 && td2->td_proc != p)
+ error = ESRCH;
+ if (error == 0)
+ error = proc_can_ptrace(td, p);
+ if (error != 0)
+ goto fail;
+ }
+
/* Ok */
break;
}
- /* Keep this process around until we finish this request. */
+ /*
+ * Keep this process around and request parallel ptrace()
+ * request to wait until we finish this request.
+ */
+ MPASS((p->p_flag2 & P2_PTRACEREQ) == 0);
+ p->p_flag2 |= P2_PTRACEREQ;
+ p2_req_set = true;
_PHOLD(p);
/*
@@ -1325,6 +1365,11 @@ out:
/* Drop our hold on this process now that the request has completed. */
_PRELE(p);
fail:
+ if (p2_req_set) {
+ if ((p->p_flag2 & P2_PTRACEREQ) != 0)
+ wakeup(&p->p_flag2);
+ p->p_flag2 &= ~P2_PTRACEREQ;
+ }
PROC_UNLOCK(p);
if (proctree_locked)
sx_xunlock(&proctree_lock);
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 9de7be4628dd..0a779820fddc 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -817,6 +817,7 @@ struct proc {
#define P2_STKGAP_DISABLE_EXEC 0x00001000 /* Stack gap disabled
after exec */
#define P2_ITSTOPPED 0x00002000
+#define P2_PTRACEREQ 0x00004000 /* Active ptrace req */
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */