[DTrace-devel] [PATCH v2] Allow probes not to attach if they were not "needed"
Kris Van Hees
kris.van.hees at oracle.com
Tue Oct 7 15:20:12 UTC 2025
On Thu, Sep 18, 2025 at 02:02:13AM -0400, eugene.loh at oracle.com wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
>
> Various kernels prevent the placement of uprobes on particular
> instructions -- e.g., on x86 on hlt or multi-byte nops, or on aarch64 on
> autiasp. This means, for example, that one cannot place a pid probe on
> every possible instruction. If a user explicitly places a probe on a
> forbidden instruction, an error message indicates there is a problem.
>
> On the other hand, if the user specifies a wildcard in a pid probe
> name, we should skip instructions where no uprobe can be placed.
>
> The problem is that we do not find out an instruction is problematic
> until we try to attach, which is long after probe descriptions have been
> processed. And when we walk probe descriptions, we do not have
> information about which kernels and instructions are problematic.
>
> The motivating problem pertains to pid probes, but we would like
> a provider-agnostic solution.
>
> Set an "OPTIONAL" probe flag when a probe is not needed:
>
> * In dt_probe.h, DT_PROBE_FLAG_OPTIONAL is used for the "flags"
> member of dt_probe_t.
>
> * For pid-style probes (including USDT and stapsdt), in
> include/dtrace/pid.h, DT_PID_PSP_FLAG_OPTIONAL is used for
> the pps_flags member of pid_probespec_t.
>
> Remove the old check on "hlt" instructions, since it was not sufficiently
> general for other kernels and other problematic instructions.
>
> Existing tests that expose the problem are
>
> test/unittest/pid/tst.coverage.d
> test/unittest/pid/tst.emptystack.d
> test/unittest/pid/tst.entry_off0.sh
> hlt instruction on x86
>
> test/unittest/pid/tst.float.d
> multi-byte nop on x86 on newer platforms
>
> test/unittest/pid/tst.entry_off0.sh
> autiasp instruction on aarch64 on newer platforms
> depending on how the trigger process was built
>
> New tests are added to check that a probe set explicitly on a
> problematic instruction will still lead to an error, even if a
> probe description before or after it tolerates attach failure.
>
> Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
> ---
> include/dtrace/pid.h | 3 +++
> libdtrace/dt_bpf.c | 2 +-
> libdtrace/dt_pid.c | 7 +----
> libdtrace/dt_probe.h | 3 +++
> libdtrace/dt_prov_uprobe.c | 26 ++++++++++++++++---
> .../pid/err.opt_after_needed.aarch64.x | 3 +++
> test/unittest/pid/err.opt_after_needed.sh | 25 ++++++++++++++++++
> .../pid/err.opt_after_needed.x86_64.x | 6 +++++
> .../pid/err.opt_before_needed.aarch64.x | 3 +++
> test/unittest/pid/err.opt_before_needed.sh | 25 ++++++++++++++++++
> .../pid/err.opt_before_needed.x86_64.x | 6 +++++
> test/unittest/pid/err.optional.aarch64.x | 3 +++
> test/unittest/pid/err.optional.sh | 25 ++++++++++++++++++
> test/unittest/pid/err.optional.x86_64.x | 6 +++++
> test/unittest/pid/tst.entry_off0.sh | 4 +--
> 15 files changed, 135 insertions(+), 12 deletions(-)
> create mode 100755 test/unittest/pid/err.opt_after_needed.aarch64.x
> create mode 100755 test/unittest/pid/err.opt_after_needed.sh
> create mode 100755 test/unittest/pid/err.opt_after_needed.x86_64.x
> create mode 100755 test/unittest/pid/err.opt_before_needed.aarch64.x
> create mode 100755 test/unittest/pid/err.opt_before_needed.sh
> create mode 100755 test/unittest/pid/err.opt_before_needed.x86_64.x
> create mode 100755 test/unittest/pid/err.optional.aarch64.x
> create mode 100755 test/unittest/pid/err.optional.sh
> create mode 100755 test/unittest/pid/err.optional.x86_64.x
>
> diff --git a/include/dtrace/pid.h b/include/dtrace/pid.h
> index 8ddb1167e..4a239b076 100644
> --- a/include/dtrace/pid.h
> +++ b/include/dtrace/pid.h
> @@ -47,6 +47,7 @@ typedef struct pid_probespec {
> size_t pps_xargvlen; /* (high estimate of) length of array */
> int8_t *pps_argmap; /* mapped arg indexes */
> char *pps_sargv; /* list of arg sources */
> + int pps_flags; /* flags */
>
> /*
> * Fields below this point do not apply to underlying probes.
> @@ -55,4 +56,6 @@ typedef struct pid_probespec {
> uint64_t pps_nameoff; /* offset to use for name */
> } pid_probespec_t;
>
> +#define DT_PID_PSP_FLAG_OPTIONAL 1 /* probe is optional */
> +
> #endif /* _DTRACE_PID_H */
> diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
> index 31781ac9f..0223764a6 100644
> --- a/libdtrace/dt_bpf.c
> +++ b/libdtrace/dt_bpf.c
> @@ -1385,7 +1385,7 @@ dt_bpf_load_progs(dtrace_hdl_t *dtp, uint_t cflags)
> if (prp->prov->impl->attach)
> rc = prp->prov->impl->attach(dtp, prp, fd);
>
> - if (rc < 0) {
> + if (rc < 0 && !(prp->flags & DT_PROBE_FLAG_OPTIONAL)) {
> close(fd);
> dt_attach_error(dtp, rc,
> prp->desc->prv, prp->desc->mod,
> diff --git a/libdtrace/dt_pid.c b/libdtrace/dt_pid.c
> index ffc52132f..7d6cfb4dc 100644
> --- a/libdtrace/dt_pid.c
> +++ b/libdtrace/dt_pid.c
> @@ -386,15 +386,10 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
> #define disasm(x, y) 4
> #endif
>
> + psp->pps_flags |= DT_PID_PSP_FLAG_OPTIONAL;
> for (off = 0; off < symp->st_size; off += disasm(off, &disasm_info)) {
> char offstr[32];
>
> -#if defined(__amd64)
> - /* Newer kernels do not allow uprobes on "hlt" instructions. */
> - if ((unsigned int)disasm_info.buffer[off] == 0xf4)
> - continue;
> -#endif
> -
> snprintf(offstr, sizeof(offstr), "%lx", off);
> if (!gmatch(offstr, pp->dpp_name))
> continue;
> diff --git a/libdtrace/dt_probe.h b/libdtrace/dt_probe.h
> index 2a78cb9ca..637875183 100644
> --- a/libdtrace/dt_probe.h
> +++ b/libdtrace/dt_probe.h
> @@ -53,10 +53,13 @@ typedef struct dt_probe {
> uint8_t *mapping; /* translated argument mapping */
> dtrace_typeinfo_t *argv; /* output argument types */
> int argc; /* output argument count */
> + int flags; /* flags for the probe */
> dt_probe_instance_t *pr_inst; /* list of functions and offsets */
> dtrace_difo_t *difo; /* BPF probe program */
> } dt_probe_t;
>
> +#define DT_PROBE_FLAG_OPTIONAL 1 /* probe is optional */
> +
> extern dt_probe_t *dt_probe_lookup2(dt_provider_t *, const char *);
> extern dt_probe_t *dt_probe_create(dtrace_hdl_t *, dt_ident_t *, int,
> dt_node_t *, uint_t, dt_node_t *, uint_t);
> diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c
> index 07b26f20f..3b7145a50 100644
> --- a/libdtrace/dt_prov_uprobe.c
> +++ b/libdtrace/dt_prov_uprobe.c
> @@ -957,9 +957,16 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp,
> upp);
> if (uprp == NULL)
> goto fail;
> - } else
> +
> + if (psp->pps_flags & DT_PID_PSP_FLAG_OPTIONAL)
> + uprp->flags |= DT_PROBE_FLAG_OPTIONAL;
> + } else {
> upp = uprp->prv_data;
>
> + if (!(psp->pps_flags & DT_PID_PSP_FLAG_OPTIONAL))
> + uprp->flags &= ~DT_PROBE_FLAG_OPTIONAL;
> + }
> +
> /*
> * Only one USDT probe can correspond to each underlying probe.
> */
> @@ -1049,6 +1056,12 @@ static int provide_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp,
> /* Look up the overlying probe. */
> prp = dt_probe_lookup(dtp, &pd);
> if (prp != NULL) {
> + /*
> + * If not optional, pass that info on.
> + */
> + if (!(psp->pps_flags & DT_PID_PSP_FLAG_OPTIONAL))
> + prp->flags &= ~DT_PROBE_FLAG_OPTIONAL;
> +
> /*
> * Probe already exists. If it's already in the underlying
> * probe's probe list, there is nothing left to do.
> @@ -1079,10 +1092,17 @@ static int provide_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp,
> */
>
> pup->probe = uprp;
> - if (prp == NULL)
> + if (prp == NULL) {
> prp = dt_probe_insert(dtp, pvp, pd.prv, pd.mod, pd.fun, pd.prb,
> pup);
> - else
> +
> + /*
> + * If not optional, pass that info on.
> + */
> + if (psp->pps_flags & DT_PID_PSP_FLAG_OPTIONAL)
> + prp->flags |= DT_PROBE_FLAG_OPTIONAL;
> +
> + } else
> dt_list_append((dt_list_t *)prp->prv_data, pup);
>
> if (prp == NULL) {
> diff --git a/test/unittest/pid/err.opt_after_needed.aarch64.x b/test/unittest/pid/err.opt_after_needed.aarch64.x
> new file mode 100755
> index 000000000..bd5b41a36
> --- /dev/null
> +++ b/test/unittest/pid/err.opt_after_needed.aarch64.x
> @@ -0,0 +1,3 @@
> +#!/bin/sh
> +echo "skip on aarch64"
> +exit 2
> diff --git a/test/unittest/pid/err.opt_after_needed.sh b/test/unittest/pid/err.opt_after_needed.sh
> new file mode 100755
> index 000000000..3ee65cd93
> --- /dev/null
> +++ b/test/unittest/pid/err.opt_after_needed.sh
> @@ -0,0 +1,25 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> +# Licensed under the Universal Permissive License v 1.0 as shown at
> +# http://oss.oracle.com/licenses/upl.
> +#
> +
> +# We should not be able to trace on a hlt instruction, which is forbidden
> +# by the kernel.
> +#
> +# (Test tst.entry_off0.sh should be okay, since there the probe on that
> +# instruction is optional, and dtrace will silently skip over such
> +# problematic instructions. Here, however, the probe on the instruction
> +# is explicitly requested by the user.)
> +
> +dtrace=$1
> +trig=`pwd`/test/triggers/ustack-tst-basic
> +off=`${OBJDUMP} -d $trig | awk '/hlt/ {sub(/:/, ""); print $1}'`
> +
> +$dtrace $dt_flags -c $trig -n 'pid$target:a.out:-:'$off',pid$target:a.out::
> +{
> + trace("hlt instruction");
> + exit(0);
> +}'
> diff --git a/test/unittest/pid/err.opt_after_needed.x86_64.x b/test/unittest/pid/err.opt_after_needed.x86_64.x
> new file mode 100755
> index 000000000..158336d98
> --- /dev/null
> +++ b/test/unittest/pid/err.opt_after_needed.x86_64.x
> @@ -0,0 +1,6 @@
> +#!/bin/sh
> +if ! ${OBJDUMP} -d test/triggers/ustack-tst-basic | grep hlt; then
> + echo "did not find hlt instruction in trigger"
> + exit 2
> +fi
> +exit 0
> diff --git a/test/unittest/pid/err.opt_before_needed.aarch64.x b/test/unittest/pid/err.opt_before_needed.aarch64.x
> new file mode 100755
> index 000000000..bd5b41a36
> --- /dev/null
> +++ b/test/unittest/pid/err.opt_before_needed.aarch64.x
> @@ -0,0 +1,3 @@
> +#!/bin/sh
> +echo "skip on aarch64"
> +exit 2
> diff --git a/test/unittest/pid/err.opt_before_needed.sh b/test/unittest/pid/err.opt_before_needed.sh
> new file mode 100755
> index 000000000..f1f172a18
> --- /dev/null
> +++ b/test/unittest/pid/err.opt_before_needed.sh
> @@ -0,0 +1,25 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> +# Licensed under the Universal Permissive License v 1.0 as shown at
> +# http://oss.oracle.com/licenses/upl.
> +#
> +
> +# We should not be able to trace on a hlt instruction, which is forbidden
> +# by the kernel.
> +#
> +# (Test tst.entry_off0.sh should be okay, since there the probe on that
> +# instruction is optional, and dtrace will silently skip over such
> +# problematic instructions. Here, however, the probe on the instruction
> +# is explicitly requested by the user.)
> +
> +dtrace=$1
> +trig=`pwd`/test/triggers/ustack-tst-basic
> +off=`${OBJDUMP} -d $trig | awk '/hlt/ {sub(/:/, ""); print $1}'`
> +
> +$dtrace $dt_flags -c $trig -n 'pid$target:a.out::,pid$target:a.out:-:'$off'
> +{
> + trace("hlt instruction");
> + exit(0);
> +}'
> diff --git a/test/unittest/pid/err.opt_before_needed.x86_64.x b/test/unittest/pid/err.opt_before_needed.x86_64.x
> new file mode 100755
> index 000000000..158336d98
> --- /dev/null
> +++ b/test/unittest/pid/err.opt_before_needed.x86_64.x
> @@ -0,0 +1,6 @@
> +#!/bin/sh
> +if ! ${OBJDUMP} -d test/triggers/ustack-tst-basic | grep hlt; then
> + echo "did not find hlt instruction in trigger"
> + exit 2
> +fi
> +exit 0
> diff --git a/test/unittest/pid/err.optional.aarch64.x b/test/unittest/pid/err.optional.aarch64.x
> new file mode 100755
> index 000000000..bd5b41a36
> --- /dev/null
> +++ b/test/unittest/pid/err.optional.aarch64.x
> @@ -0,0 +1,3 @@
> +#!/bin/sh
> +echo "skip on aarch64"
> +exit 2
> diff --git a/test/unittest/pid/err.optional.sh b/test/unittest/pid/err.optional.sh
> new file mode 100755
> index 000000000..1dbef4e45
> --- /dev/null
> +++ b/test/unittest/pid/err.optional.sh
> @@ -0,0 +1,25 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> +# Licensed under the Universal Permissive License v 1.0 as shown at
> +# http://oss.oracle.com/licenses/upl.
> +#
> +
> +# We should not be able to trace on a hlt instruction, which is forbidden
> +# by the kernel.
> +#
> +# (Test tst.entry_off0.sh should be okay, since there the probe on that
> +# instruction is optional, and dtrace will silently skip over such
> +# problematic instructions. Here, however, the probe on the instruction
> +# is explicitly requested by the user.)
> +
> +dtrace=$1
> +trig=`pwd`/test/triggers/ustack-tst-basic
> +off=`${OBJDUMP} -d $trig | awk '/hlt/ {sub(/:/, ""); print $1}'`
> +
> +$dtrace $dt_flags -c $trig -n 'pid$target:a.out:-:'$off'
> +{
> + trace("hlt instruction");
> + exit(0);
> +}'
> diff --git a/test/unittest/pid/err.optional.x86_64.x b/test/unittest/pid/err.optional.x86_64.x
> new file mode 100755
> index 000000000..158336d98
> --- /dev/null
> +++ b/test/unittest/pid/err.optional.x86_64.x
> @@ -0,0 +1,6 @@
> +#!/bin/sh
> +if ! ${OBJDUMP} -d test/triggers/ustack-tst-basic | grep hlt; then
> + echo "did not find hlt instruction in trigger"
> + exit 2
> +fi
> +exit 0
> diff --git a/test/unittest/pid/tst.entry_off0.sh b/test/unittest/pid/tst.entry_off0.sh
> index 1a4c3d207..304be4568 100755
> --- a/test/unittest/pid/tst.entry_off0.sh
> +++ b/test/unittest/pid/tst.entry_off0.sh
> @@ -1,14 +1,14 @@
> #!/bin/bash
> #
> # Oracle Linux DTrace.
> -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
> +# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
> # Licensed under the Universal Permissive License v 1.0 as shown at
> # http://oss.oracle.com/licenses/upl.
> #
>
> # Although the D script takes only "one second," it takes a long time to
> # shut down. Until that has been solved, increase the timeout for the test.
> -# @@timeout: 120
> +# @@timeout: 240
>
> dtrace=$1
>
> --
> 2.47.3
>
More information about the DTrace-devel
mailing list