[DTrace-devel] [PATCH 5/7] proc: improve armouring against self-grabs
Nick Alcock
nick.alcock at oracle.com
Wed Mar 20 14:15:35 UTC 2024
The DTrace victim process-monitoring loop (used by -c in particular, but also
by u*() actions) has some protection to keep DTrace from trying to ptrace()
itself: processes whose PID is the same as DTrace's, or which is already
being traced by DTrace, or which is a system daemon, do not get traced.
But this is not quite good enough. DTrace is a multithreaded process, but we
check the tracer pid (which is a kernel task ID derived from
/proc/$pid/status, i.e. a *thread* ID) against getpid() (which is a POSIX
pid, i.e. a *task group* ID). We should dig out the task group ID of the
process being traced and compare *that* against our own PID. (This still
does not stop two distinct dtraces trying to trace each other, but in that
case they shouldn't deadlock: each should sometimes let the other run and
they'll both make some sort of stuttering forward progress.)
To armour things a bit further, we shouldn't try to maintain a Pwait()ing
process-monitoring loop for anything libproc has already said it cannot
ptrace(). It doesn't matter whether we *asked* for it to be noninvasively
traced in the first place: what matters is purely whether we got a ptrace()
grab on it, because that's what controls whether we need to do the whole
waitpid() signal-redirection dance to it.
Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
libdtrace/dt_proc.c | 10 +++++-----
libproc/Pcontrol.c | 17 +++++++++++++++++
libproc/libproc.h | 1 +
3 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/libdtrace/dt_proc.c b/libdtrace/dt_proc.c
index 99ea94d273edc..a052abbacf204 100644
--- a/libdtrace/dt_proc.c
+++ b/libdtrace/dt_proc.c
@@ -945,17 +945,17 @@ dt_proc_control(void *arg)
* Also, obviously enough, never drop breakpoints in ourself!
*/
if (datap->dpcd_flags & DTRACE_PROC_SHORTLIVED) {
- pid_t tracer_pid;
+ pid_t tracer_pid, tgid;
noninvasive = 1;
dpr->dpr_notifiable = 0;
tracer_pid = Ptracer_pid(dpr->dpr_pid);
+ tgid = Ptgid(dpr->dpr_pid);
if ((Psystem_daemon(dpr->dpr_pid, dtp->dt_useruid,
dtp->dt_sysslice) > 0) ||
- ((tracer_pid != 0) &&
- (tracer_pid != getpid())) ||
- (dpr->dpr_pid == getpid()))
+ (tracer_pid == getpid()) ||
+ (tgid == getpid()))
noninvasive = 2;
}
@@ -971,7 +971,7 @@ dt_proc_control(void *arg)
* the cleanup handlers: the process is running, but does not
* need a monitoring thread.
*/
- if (noninvasive && !Ptraceable(dpr->dpr_proc)) {
+ if (!Ptraceable(dpr->dpr_proc)) {
dt_dprintf("%i: noninvasive grab, control thread "
"suiciding\n", dpr->dpr_pid);
pthread_exit(NULL);
diff --git a/libproc/Pcontrol.c b/libproc/Pcontrol.c
index 1a19601411c8e..c96a410b70b3f 100644
--- a/libproc/Pcontrol.c
+++ b/libproc/Pcontrol.c
@@ -2837,6 +2837,23 @@ Ptracer_pid(pid_t pid)
return tracer_pid;
}
+/*
+ * Get the thread-group ID of the given task (comparable to getpid().
+ */
+pid_t
+Ptgid(pid_t pid)
+{
+ char *txt;
+ pid_t tgid;
+
+ if ((txt = Pget_proc_status(pid, "Tgid")) == NULL)
+ return 0;
+
+ tgid = strtol(txt, NULL, 10);
+ free(txt);
+ return tgid;
+}
+
/*
* Return 1 if this process is a system daemon, 0 if it is not, and -1 on error.
*
diff --git a/libproc/libproc.h b/libproc/libproc.h
index a3163964331cd..59c73051bd85c 100644
--- a/libproc/libproc.h
+++ b/libproc/libproc.h
@@ -102,6 +102,7 @@ extern int Pdynamically_linked(struct ps_prochandle *);
extern int Ptraceable(struct ps_prochandle *);
extern int Pelf64(struct ps_prochandle *);
extern pid_t Ptracer_pid(pid_t);
+extern pid_t Ptgid(pid_t);
/*
* Calls that do not take a process structure. These are used to determine
--
2.44.0.273.ge0bd14271f
More information about the DTrace-devel
mailing list