[DTrace-devel] [PATCH] Allow probes not to attach if they were not "needed"
eugene.loh at oracle.com
eugene.loh at oracle.com
Thu Sep 4 19:02:26 UTC 2025
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
description, 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.
Introduce a "NEEDED" probe flag. Set the flag when a probe is explicitly
needed (say, it was specifically named). We use two such flags:
* In dt_probe.h, DT_PROBE_FLAG_NEEDED 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_NEEDED is used for
the pps_flags member of pid_probespec_t.
Set the "NEEDED" flag for a probe in these situations:
* In dt_tp_probe_insert(), set the flag for each probe we insert.
This covers the dtrace, fbt, rawtp, sdt, and syscall providers.
* In dt_sdt_populate(), set the flag for each probe we insert.
This covers, e.g., the io, ip, lockstat, proc, sched, and tcp
providers.
* For the cpc and profile providers, set the flag for probes created
with the "provide" functions (explicitly requested by the user)
but not with the "populate" functions (chosen by default by dtrace).
* For providers that will sit on underlying probes, set the NEEDED
flag for pid_probespec_t. Specifically,
* In dt_pid_per_sym(), set the NEEDED flag to cover entry,
return, and explicit offsets. Unset the flag for glob offsets.
In dt_pid_create_usdt_probes_proc() and dt_stapsdt_parse(),
set the flag.
* In dt_prov_uprobe(), pass the pid_probespec_t NEEDED flag
to dt_probe_t, for both the overlying and underlying probe.
In dt_bpf.c, if the return code from the attach function indicates
failure, return a failure only if the probe was NEEDED.
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
A new test is added to check that a probe set explicitly on a
problematic instruction will still lead to an error.
Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
include/dtrace/pid.h | 3 +++
libdtrace/dt_bpf.c | 2 +-
libdtrace/dt_pid.c | 10 ++++------
libdtrace/dt_probe.h | 3 +++
libdtrace/dt_prov_cpc.c | 10 ++++++++--
libdtrace/dt_prov_profile.c | 10 ++++++++--
libdtrace/dt_prov_uprobe.c | 18 ++++++++++++++++++
libdtrace/dt_provider_sdt.c | 8 ++++++--
libdtrace/dt_provider_tp.c | 7 ++++++-
test/unittest/pid/err.needed.aarch64.x | 3 +++
test/unittest/pid/err.needed.sh | 23 +++++++++++++++++++++++
test/unittest/pid/err.needed.x86_64.x | 6 ++++++
test/unittest/pid/tst.entry_off0.sh | 4 ++--
13 files changed, 91 insertions(+), 16 deletions(-)
create mode 100755 test/unittest/pid/err.needed.aarch64.x
create mode 100755 test/unittest/pid/err.needed.sh
create mode 100755 test/unittest/pid/err.needed.x86_64.x
diff --git a/include/dtrace/pid.h b/include/dtrace/pid.h
index 8ddb1167e..7829e47c0 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_NEEDED 1 /* probe is needed */
+
#endif /* _DTRACE_PID_H */
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index 31781ac9f..dc3d47f56 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_NEEDED)) {
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 4af9141aa..6e7fba287 100644
--- a/libdtrace/dt_pid.c
+++ b/libdtrace/dt_pid.c
@@ -184,6 +184,7 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
psp->pps_fun = (char *) func;
psp->pps_nameoff = 0;
psp->pps_off = symp->st_value - pp->dpp_vaddr;
+ psp->pps_flags |= DT_PID_PSP_FLAG_NEEDED;
/*
* The special function "-" means the probe name is an absolute
@@ -386,15 +387,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_NEEDED;
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;
@@ -1064,6 +1060,7 @@ dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, pid_t pid, dt_proc_t *dpr,
psp.pps_pid = dpr->dpr_pid;
psp.pps_off = tp->tracepoint.addr - pmp->pr_file->first_segment->pr_vaddr;
psp.pps_nameoff = 0;
+ psp.pps_flags |= DT_PID_PSP_FLAG_NEEDED;
if (nargv) {
psp.pps_nargc = probe->probe.nargc;
@@ -1421,6 +1418,7 @@ dt_stapsdt_parse(dtrace_hdl_t *dtp, dt_proc_t *dpr, dtrace_probedesc_t *pdp,
psp.pps_inum = pmp->pr_inum;
psp.pps_pid = dpr->dpr_pid;
psp.pps_nameoff = 0;
+ psp.pps_flags |= DT_PID_PSP_FLAG_NEEDED;
if (pvp->impl->provide_probe(dtp, &psp) < 0) {
dt_pid_error(dtp, pcb, dpr, D_PROC_USDT,
diff --git a/libdtrace/dt_probe.h b/libdtrace/dt_probe.h
index 2a78cb9ca..ffd994844 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_NEEDED 1 /* probe is needed */
+
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_cpc.c b/libdtrace/dt_prov_cpc.c
index 57b11b135..9fb165cb6 100644
--- a/libdtrace/dt_prov_cpc.c
+++ b/libdtrace/dt_prov_cpc.c
@@ -347,6 +347,7 @@ static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
{
dt_provider_t *prv;
struct perf_event_attr attr;
+ dt_probe_t *prp;
prv = dt_provider_lookup(dtp, prvname);
if (!prv)
@@ -358,17 +359,22 @@ static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
return 0;
/* return if we already have this probe */
- if (dt_probe_lookup(dtp, pdp))
+ prp = dt_probe_lookup(dtp, pdp);
+ if (prp) {
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
return 0;
+ }
/* check if the probe name can be decoded */
if (decode_probename(&attr, prv, pdp->prb) == -1)
return 0;
/* try to add this probe */
- if (cpc_probe_insert(dtp, prv, pdp->prb) == NULL)
+ prp = cpc_probe_insert(dtp, prv, pdp->prb);
+ if (prp == NULL)
return 0;
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
return 1;
}
diff --git a/libdtrace/dt_prov_profile.c b/libdtrace/dt_prov_profile.c
index e1369ca9b..ec32ce83b 100644
--- a/libdtrace/dt_prov_profile.c
+++ b/libdtrace/dt_prov_profile.c
@@ -160,6 +160,7 @@ static uint64_t get_period(const char *name)
static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
{
dt_provider_t *prv;
+ dt_probe_t *prp;
int kind;
uint64_t period;
@@ -176,8 +177,11 @@ static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
return 0;
/* return if we already have this probe */
- if (dt_probe_lookup(dtp, pdp))
+ prp = dt_probe_lookup(dtp, pdp);
+ if (prp) {
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
return 0;
+ }
/* get the provider - should have been created in populate() */
prv = dt_provider_lookup(dtp, prvname);
@@ -189,9 +193,11 @@ static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
return 0;
/* try to add this probe */
- if (profile_probe_insert(dtp, prv, pdp->prb, kind, period) == NULL)
+ prp = profile_probe_insert(dtp, prv, pdp->prb, kind, period);
+ if (prp == NULL)
return 0;
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
return 1;
}
diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c
index 07b26f20f..2396522ce 100644
--- a/libdtrace/dt_prov_uprobe.c
+++ b/libdtrace/dt_prov_uprobe.c
@@ -960,6 +960,12 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp,
} else
upp = uprp->prv_data;
+ /*
+ * Pass flag info from psp to uprp
+ */
+ if (psp->pps_flags & DT_PID_PSP_FLAG_NEEDED)
+ uprp->flags |= DT_PROBE_FLAG_NEEDED;
+
/*
* Only one USDT probe can correspond to each underlying probe.
*/
@@ -1049,6 +1055,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) {
+ /*
+ * Pass flag info from psp to uprp
+ */
+ if (psp->pps_flags & DT_PID_PSP_FLAG_NEEDED)
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
+
/*
* Probe already exists. If it's already in the underlying
* probe's probe list, there is nothing left to do.
@@ -1091,6 +1103,12 @@ static int provide_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp,
return -1;
}
+ /*
+ * Pass flag info from psp to uprp
+ */
+ if (psp->pps_flags & DT_PID_PSP_FLAG_NEEDED)
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
+
/*
* Add the overlying probe to the list of probes for the underlying probe.
*/
diff --git a/libdtrace/dt_provider_sdt.c b/libdtrace/dt_provider_sdt.c
index 962848485..f9a51fa25 100644
--- a/libdtrace/dt_provider_sdt.c
+++ b/libdtrace/dt_provider_sdt.c
@@ -48,10 +48,14 @@ dt_sdt_populate(dtrace_hdl_t *dtp, const char *prvname, const char *modname,
* entries to identify the probe names.
*/
for (arg = &probe_args[0]; arg->name != NULL; arg++) {
+ dt_probe_t *prp;
+
if (arg->argno == 0 &&
- dt_probe_insert(dtp, prv, prvname, modname, "", arg->name,
- NULL))
+ (prp = dt_probe_insert(dtp, prv, prvname, modname,
+ "", arg->name, NULL))) {
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
n++;
+ }
}
return n;
diff --git a/libdtrace/dt_provider_tp.c b/libdtrace/dt_provider_tp.c
index 3d4ec1e03..4531a88a8 100644
--- a/libdtrace/dt_provider_tp.c
+++ b/libdtrace/dt_provider_tp.c
@@ -368,12 +368,17 @@ dt_tp_probe_insert(dtrace_hdl_t *dtp, dt_provider_t *prov, const char *prv,
const char *mod, const char *fun, const char *prb)
{
tp_probe_t *tpp;
+ dt_probe_t *prp;
tpp = dt_tp_alloc(dtp);
if (tpp == NULL)
return NULL;
- return dt_probe_insert(dtp, prov, prv, mod, fun, prb, tpp);
+ prp = dt_probe_insert(dtp, prov, prv, mod, fun, prb, tpp);
+ if (prp)
+ prp->flags |= DT_PROBE_FLAG_NEEDED;
+
+ return prp;
}
/*
diff --git a/test/unittest/pid/err.needed.aarch64.x b/test/unittest/pid/err.needed.aarch64.x
new file mode 100755
index 000000000..bd5b41a36
--- /dev/null
+++ b/test/unittest/pid/err.needed.aarch64.x
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "skip on aarch64"
+exit 2
diff --git a/test/unittest/pid/err.needed.sh b/test/unittest/pid/err.needed.sh
new file mode 100755
index 000000000..d6b041b60
--- /dev/null
+++ b/test/unittest/pid/err.needed.sh
@@ -0,0 +1,23 @@
+#!/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.
+#
+# (When the probe is "needed" -- that is, explicitly requested by the user
+# -- we should incur an error. Test tst.entry_off0.sh should be okay,
+# since dtrace will silently skip over such problematic instructions.)
+
+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.needed.x86_64.x b/test/unittest/pid/err.needed.x86_64.x
new file mode 100755
index 000000000..158336d98
--- /dev/null
+++ b/test/unittest/pid/err.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/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