[DTrace-devel] [PATCH v2 20/20] usdt: fix stack args on PT_REGS_ARGSTKBASE > 0 platforms

Nick Alcock nick.alcock at oracle.com
Wed Sep 7 13:00:07 UTC 2022


On such platforms (like x86), USDT probes have unusual properties,
because they are placed after the setup of args for the USDT function
call but before the actual call (because the function does not exist,
and the call will be replaced with a NOP).

Extend dt_cg_tramp_copy_args_from_regs to handle this case, and arrange
to call it accordingly when USDT probe trampolines are being
generated. (pid glob offset probes at the same address need a bit more
work, because the args *they* want to access are the ones for the
calling function -- but this is a rare case we can ignore for now.  Such
probes work in other ways: it's only stack args that will be wrong.)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
 libdtrace/dt_cg.c       | 12 ++++++++----
 libdtrace/dt_cg.h       |  2 +-
 libdtrace/dt_prov_fbt.c |  2 +-
 libdtrace/dt_prov_pid.c | 21 +++++++++++++++++----
 4 files changed, 27 insertions(+), 10 deletions(-)

diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 9f9d0fb03028..bf147bd3cb4a 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -267,12 +267,15 @@ dt_cg_tramp_copy_regs(dt_pcb_t *pcb, int rp)
 
 /*
  * Copy arguments from a dt_pt_regs structure referenced by the 'rp' argument.
+ * If 'called' is nonzero, the registers are laid out as when inside the
+ * function: if zero, they are laid out as at the call instruction, before the
+ * function is called (as is done for e.g. usdt).
  *
  * The caller must ensure that %r7 contains the value set by the
  * dt_cg_tramp_prologue*() functions.
  */
 void
-dt_cg_tramp_copy_args_from_regs(dt_pcb_t *pcb, int rp)
+dt_cg_tramp_copy_args_from_regs(dt_pcb_t *pcb, int rp, int called)
 {
 	dtrace_hdl_t	*dtp = pcb->pcb_hdl;
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
@@ -318,13 +321,13 @@ dt_cg_tramp_copy_args_from_regs(dt_pcb_t *pcb, int rp)
 	 *		rc = bpf_probe_read[_user](dctx->mst->argv[i],
 	 *					   sizeof(uint64_t),
 	 *					   &sp[i - PT_REGS_ARGC +
-	 *						   PT_REGS_ARGSTKBASE]);
+	 *						   (called ? PT_REGS_ARGSTKBASE : 0)]);
 	 *				// mov %r1, %r7
 	 *				// add %r1, DMST_ARG(i)
 	 *				// mov %r2, sizeof(uint64_t)
 	 *				// lddw %r3, [%rp + PT_REGS_SP]
 	 *				// add %r3, (i - PT_REGS_ARGC +
-	 *						 PT_REGS_ARGSTKBASE) *
+	 *						 (called ? PT_REGS_ARGSTKBASE : 0)) *
 	 *					    sizeof(uint64_t)
 	 *				// call bpf_probe_read[_user]
 	 *		if (rc != 0)
@@ -344,7 +347,8 @@ dt_cg_tramp_copy_args_from_regs(dt_pcb_t *pcb, int rp)
 		emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DMST_ARG(i)));
 		emit(dlp,  BPF_MOV_IMM(BPF_REG_2, sizeof(uint64_t)));
 		emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_3, rp, PT_REGS_SP));
-		emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, (i - PT_REGS_ARGC + PT_REGS_ARGSTKBASE) * sizeof(uint64_t)));
+		emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, (i - PT_REGS_ARGC +
+			    (called ? PT_REGS_ARGSTKBASE : 0)) * sizeof(uint64_t)));
 		emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_user]));
 		emit(dlp,  BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, lbl_ok));
 		emit(dlp,  BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(i), 0));
diff --git a/libdtrace/dt_cg.h b/libdtrace/dt_cg.h
index a7606bc00f84..1bfe378714b3 100644
--- a/libdtrace/dt_cg.h
+++ b/libdtrace/dt_cg.h
@@ -24,7 +24,7 @@ extern void dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act);
 extern void dt_cg_tramp_prologue(dt_pcb_t *pcb);
 extern void dt_cg_tramp_clear_regs(dt_pcb_t *pcb);
 extern void dt_cg_tramp_copy_regs(dt_pcb_t *pcb, int rp);
-extern void dt_cg_tramp_copy_args_from_regs(dt_pcb_t *pcb, int rp);
+extern void dt_cg_tramp_copy_args_from_regs(dt_pcb_t *pcb, int rp, int called);
 extern void dt_cg_tramp_copy_rval_from_regs(dt_pcb_t *pcb, int rp);
 extern void dt_cg_tramp_call_clauses(dt_pcb_t *pcb, const dt_probe_t *prp,
 				     dt_activity_t act);
diff --git a/libdtrace/dt_prov_fbt.c b/libdtrace/dt_prov_fbt.c
index 72c31469df61..db037e4e72d0 100644
--- a/libdtrace/dt_prov_fbt.c
+++ b/libdtrace/dt_prov_fbt.c
@@ -175,7 +175,7 @@ static void trampoline(dt_pcb_t *pcb)
 		dt_cg_xsetx(dlp, NULL, DT_LBL_NONE, BPF_REG_0, -1);
 		emit(dlp,  BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(0), BPF_REG_0));
 	} else
-		dt_cg_tramp_copy_args_from_regs(pcb, BPF_REG_8);
+		dt_cg_tramp_copy_args_from_regs(pcb, BPF_REG_8, 1);
 	dt_cg_tramp_epilogue(pcb);
 }
 
diff --git a/libdtrace/dt_prov_pid.c b/libdtrace/dt_prov_pid.c
index a986e91490f6..1be2ea08713b 100644
--- a/libdtrace/dt_prov_pid.c
+++ b/libdtrace/dt_prov_pid.c
@@ -35,6 +35,7 @@ typedef struct pid_probe {
 	dt_list_t	probes;
 	char		*uprobe_name;
 	int		dtrace_created;
+	int		arg_setup;
 } pid_probe_t;
 
 typedef struct pid_overlying_probe {
@@ -84,7 +85,8 @@ static void probe_destroy(dtrace_hdl_t *dtp, void *datap)
  * If not found, we create a new probe.
  */
 static dt_probe_t *provide_pid_underlying(dtrace_hdl_t *dtp,
-					  const pid_probespec_t *psp)
+					  const pid_probespec_t *psp,
+					  const pid_probespec_t *overlying)
 {
 	dtrace_probedesc_t	pd;
 	dt_probe_t		*prp;
@@ -141,6 +143,16 @@ static dt_probe_t *provide_pid_underlying(dtrace_hdl_t *dtp,
 		if (prp == NULL)
 			goto fail;
 	}
+	else
+		pp = prp->prv_data;
+
+	/*
+	 * USDT probes are positioned after argument setup for a function that
+	 * is never actually called, so the stack is slightly different from the
+	 * common case of accessing args for the current function.
+	 */
+	if (overlying && overlying->pps_type == DTPPT_USDT)
+		pp->arg_setup = 1;
 
 	return prp;
 fail:
@@ -164,7 +176,7 @@ static int provide_pid(dtrace_hdl_t *dtp, const pid_probespec_t *psp)
 	pid_overlying_probe_t	*pop;
 
 	if (psp->pps_type == DTPPT_UNDERLYING) {
-		if (provide_pid_underlying(dtp, psp))
+		if (provide_pid_underlying(dtp, psp, NULL))
 			return 0;
 		else
 			return -1;
@@ -226,7 +238,7 @@ static int provide_pid(dtrace_hdl_t *dtp, const pid_probespec_t *psp)
 	pp_psp.pps_type = DTPPT_UNDERLYING;
 	pp_psp.pps_mod = mod;
 	pp_psp.pps_prb = underlying_prb;
-	uprp = provide_pid_underlying(dtp, &pp_psp);
+	uprp = provide_pid_underlying(dtp, &pp_psp, psp);
 
 	if (uprp == NULL)
 		return -1;
@@ -321,7 +333,8 @@ static void trampoline(dt_pcb_t *pcb)
 	if (strcmp(pcb->pcb_probe->desc->prb, "return") == 0)
 		dt_cg_tramp_copy_rval_from_regs(pcb, BPF_REG_8);
 	else
-		dt_cg_tramp_copy_args_from_regs(pcb, BPF_REG_8);
+		dt_cg_tramp_copy_args_from_regs(pcb, BPF_REG_8,
+						!pp->arg_setup);
 
 	/*
 	 * Retrieve the PID of the process that caused the probe to fire.
-- 
2.37.1.265.g363c192786.dirty




More information about the DTrace-devel mailing list