[DTrace-devel] [PATCH v3 09/21] proc: don't ptrace shortlived-grabbed processes if someone else is ptracing them

Kris Van Hees kris.van.hees at oracle.com
Tue Feb 20 17:33:16 UTC 2024


On Tue, Jan 16, 2024 at 09:13:05PM +0000, Nick Alcock wrote:
> Shortlived grabs, by definition, are grabs that won't last long and where we
> don't expect things like dlopen monitoring to be useful.  We ptrace for
> this, and to extract dynamic loader scope info and take things like symbol
> interposition into accout when doing symbol lookup; but if someone else is
> already tracing the process we should probably just try not ptracing it,
> since the worst that will happen if we do is that some name lookups of
> interposed symbols or symbols in alternate dlmopen namespaces might yield
> the wrong address (probably leading to a probe not firing when it
> should).
> 
> Interposed symbols are rare enough that this failure mode is probably not
> justification for refusing to trace entirely (perhaps a warning message
> might be desirable, but honestly I think it's rare enough that not even that
> is justifiable: most ptrace failures will have no visible negative
> consequences at all, since not only must an interposed symbol exist but we
> must also be tracing it *and* tracing the process with multiple debuggers at
> once, which seems likely to be very rare: nearly all warnings will be
> warning when nothing will go wrong at all).
> 
> It is even rarer in the modern toolchain world in which many ELF objects
> cannot be interposed at all, whether due to -z now or
> -fno-semantic-interposition or whatever.
> 
> Doing this makes it practical to have multiple dtraces USDT-probing the same
> process at the same time: one of them will ptrace it, the other one won't,
> and both will get the mapping info they need for a
> reliable-enough-to-be-useful trace (and if we're lucky and can ptrace,
> symbol resolution quality goes up a bit).
> 
> test/unittest/usdt/tst.multitrace.sh tests this (though there are other
> things stopping it working well, it cannot work at all without something
> like this).
> 
> Signed-off-by: Nick Alcock <nick.alcock at oracle.com>

Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>

> ---
>  libdtrace/dt_proc.c | 16 ++++++++++++----
>  libproc/Pcontrol.c  | 18 ++++++++++++++++++
>  libproc/libproc.h   |  1 +
>  3 files changed, 31 insertions(+), 4 deletions(-)
> 
> diff --git a/libdtrace/dt_proc.c b/libdtrace/dt_proc.c
> index dd5f69a60e17..73f35135a7b6 100644
> --- a/libdtrace/dt_proc.c
> +++ b/libdtrace/dt_proc.c
> @@ -931,16 +931,24 @@ dt_proc_control(void *arg)
>  		 * be worth ptracing it so that we get better symbol resolution,
>  		 * but if the process is a crucial system daemon, avoid ptracing
>  		 * it entirely, to avoid rtld_db dropping breakpoints in crucial
> -		 * system daemons unless specifically demanded.  No death
> -		 * notification is ever sent.
> +		 * system daemons unless specifically demanded.  Also avoid
> +		 * ptracing if the process is already being traced by someone
> +		 * else (like another DTrace instance).  No death notification
> +		 * is ever sent.
>  		 *
>  		 * Also, obviously enough, never drop breakpoints in ourself!
>  		 */
>  		if (datap->dpcd_flags & DTRACE_PROC_SHORTLIVED) {
> -			noninvasive = 1;
> +			pid_t tracer_pid;
> +
> +                        noninvasive = 1;
>  			dpr->dpr_notifiable = 0;
> -			if ((Psystem_daemon(dpr->dpr_pid, dtp->dt_useruid,
> +			tracer_pid = Ptracer_pid(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()))
>  				noninvasive = 2;
>  		}
> diff --git a/libproc/Pcontrol.c b/libproc/Pcontrol.c
> index eb450bdc2981..68b372da62eb 100644
> --- a/libproc/Pcontrol.c
> +++ b/libproc/Pcontrol.c
> @@ -2811,6 +2811,24 @@ Puid(pid_t pid)
>  	return uid;
>  }
>  
> +/*
> + * Determine if this process is already being ptraced, and if so, the pid of the
> + * ptracer.  The caller should eschew invasive tracing in this case.
> + */
> +pid_t
> +Ptracer_pid(pid_t pid)
> +{
> +	char *traced;
> +	pid_t tracer_pid;
> +
> +	if ((traced = Pget_proc_status(pid, "TracerPid")) == NULL)
> +		return 0;
> +
> +	tracer_pid = strtol(traced, NULL, 10);
> +	free(traced);
> +	return tracer_pid;
> +}
> +
>  /*
>   * 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 22ff54d58d3f..9e5c25990b4f 100644
> --- a/libproc/libproc.h
> +++ b/libproc/libproc.h
> @@ -101,6 +101,7 @@ extern	void	Pset_procfs_path(const char *);
>  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);
>  
>  /*
>   * Calls that do not take a process structure.  These are used to determine
> -- 
> 2.43.0.272.gce700b77fd
> 
> 



More information about the DTrace-devel mailing list