[DTrace-devel] [PATCH 1/4] parser, cg: Implement the return() action

Kris Van Hees kris.van.hees at oracle.com
Tue Jul 15 05:47:36 UTC 2025


The return(n) action can be used for error injection by forcing a
given return value for a kernel function.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 include/dtrace/actions_defines.h              |  1 +
 libdtrace/dt_cg.c                             | 21 +++++++++++++++
 libdtrace/dt_grammar.y                        |  1 -
 libdtrace/dt_impl.h                           |  3 ++-
 libdtrace/dt_lex.l                            |  1 -
 libdtrace/dt_open.c                           |  2 ++
 test/modules                                  |  1 +
 .../actions/return/err.D_PROTO_ARG.str.d      | 19 +++++++++++++
 .../actions/return/err.D_PROTO_ARG.str.r      |  4 +++
 .../actions/return/err.D_PROTO_ARG.void.d     | 19 +++++++++++++
 .../actions/return/err.D_PROTO_ARG.void.r     |  4 +++
 .../return/err.D_PROTO_LEN.missing_arg.d      | 20 ++++++++++++++
 .../return/err.D_PROTO_LEN.missing_arg.r      |  2 ++
 .../return/err.D_PROTO_LEN.too_many_args.d    | 20 ++++++++++++++
 .../return/err.D_PROTO_LEN.too_many_args.r    |  2 ++
 .../unittest/actions/return/err.destructive.d | 26 ++++++++++++++++++
 .../unittest/actions/return/err.destructive.r |  2 ++
 .../unittest/actions/return/tst.destructive.d | 27 +++++++++++++++++++
 .../unittest/actions/return/tst.destructive.r |  1 +
 19 files changed, 173 insertions(+), 3 deletions(-)
 create mode 100644 test/unittest/actions/return/err.D_PROTO_ARG.str.d
 create mode 100644 test/unittest/actions/return/err.D_PROTO_ARG.str.r
 create mode 100644 test/unittest/actions/return/err.D_PROTO_ARG.void.d
 create mode 100644 test/unittest/actions/return/err.D_PROTO_ARG.void.r
 create mode 100644 test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.d
 create mode 100644 test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.r
 create mode 100644 test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.d
 create mode 100644 test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.r
 create mode 100644 test/unittest/actions/return/err.destructive.d
 create mode 100644 test/unittest/actions/return/err.destructive.r
 create mode 100644 test/unittest/actions/return/tst.destructive.d
 create mode 100644 test/unittest/actions/return/tst.destructive.r

diff --git a/include/dtrace/actions_defines.h b/include/dtrace/actions_defines.h
index 4ff4fb83..601328b8 100644
--- a/include/dtrace/actions_defines.h
+++ b/include/dtrace/actions_defines.h
@@ -51,6 +51,7 @@
 #define DTRACEACT_RAISE			(DTRACEACT_PROC_DESTRUCTIVE + 2)
 #define DTRACEACT_SYSTEM		(DTRACEACT_PROC_DESTRUCTIVE + 3)
 #define DTRACEACT_FREOPEN		(DTRACEACT_PROC_DESTRUCTIVE + 4)
+#define DTRACEACT_RETURN		(DTRACEACT_PROC_DESTRUCTIVE + 5)
 
 #define DTRACEACT_PROC_CONTROL		0x0300
 
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index bd0763d6..d80b0a55 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -2893,6 +2893,25 @@ dt_cg_act_print(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 	dt_regset_free(drp, BPF_REG_0);
 }
 
+static void
+dt_cg_act_return(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
+{
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+	dt_regset_t	*drp = pcb->pcb_regs;
+
+	dt_cg_node(dnp->dn_args, &pcb->pcb_ir, pcb->pcb_regs);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
+	emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_1, DCTX_CTX));
+	emit(dlp, BPF_MOV_REG(BPF_REG_2, dnp->dn_args->dn_reg));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp, BPF_CALL_HELPER(BPF_FUNC_override_return));
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+}
+
 typedef void dt_cg_action_f(dt_pcb_t *, dt_node_t *, dtrace_actkind_t);
 
 typedef struct dt_cg_actdesc {
@@ -2951,6 +2970,8 @@ static const dt_cg_actdesc_t _dt_cg_actions[DT_ACT_MAX] = {
 	[DT_ACT_IDX(DT_ACT_SETOPT)]		= { &dt_cg_act_setopt, },
 	[DT_ACT_IDX(DT_ACT_PCAP)]		= { &dt_cg_act_pcap, },
 	[DT_ACT_IDX(DT_ACT_PRINT)]		= { &dt_cg_act_print, },
+	[DT_ACT_IDX(DT_ACT_RETURN)]		= { &dt_cg_act_return,
+						    DTRACEACT_RETURN },
 };
 
 dt_irnode_t *
diff --git a/libdtrace/dt_grammar.y b/libdtrace/dt_grammar.y
index 048a974d..a0911097 100644
--- a/libdtrace/dt_grammar.y
+++ b/libdtrace/dt_grammar.y
@@ -73,7 +73,6 @@ int yylex (void);
 %token	DT_KEY_PROVIDER
 %token	DT_KEY_REGISTER
 %token	DT_KEY_RESTRICT
-%token	DT_KEY_RETURN
 %token	DT_KEY_SELF
 %token	DT_KEY_SHORT
 %token	DT_KEY_SIGNED
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 2adc1252..e7192290 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -550,8 +550,9 @@ struct dtrace_hdl {
 #define	DT_ACT_SETOPT		DT_ACT(28)	/* setopt() action */
 #define	DT_ACT_PCAP		DT_ACT(29)	/* pcap() action */
 #define	DT_ACT_PRINT		DT_ACT(30)	/* print() action */
+#define	DT_ACT_RETURN		DT_ACT(31)	/* return() action */
 
-#define DT_ACT_MAX		31
+#define DT_ACT_MAX		32
 
 /*
  * Sentinel to tell freopen() to restore the saved stdout.  This must not
diff --git a/libdtrace/dt_lex.l b/libdtrace/dt_lex.l
index cc165c1e..bdcca415 100644
--- a/libdtrace/dt_lex.l
+++ b/libdtrace/dt_lex.l
@@ -104,7 +104,6 @@ if (yypcb->pcb_token != 0) {
 <S0>provider	return DT_KEY_PROVIDER;
 <S0>register	return DT_KEY_REGISTER;
 <S0>restrict	return DT_KEY_RESTRICT;
-<S0>return	return DT_KEY_RETURN;
 <S0>self	return DT_KEY_SELF;
 <S0>short	return DT_KEY_SHORT;
 <S0>signed	return DT_KEY_SIGNED;
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index c67455e7..08394a11 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -269,6 +269,8 @@ static const dt_ident_t _dtrace_globals[] = {
 	&dt_idops_func, "void(int)" },
 { "rand", DT_IDENT_FUNC, 0, DIF_SUBR_RAND, DT_ATTR_STABCMN, DT_VERS_1_0,
 	&dt_idops_func, "int()" },
+{ "return", DT_IDENT_ACTFUNC, 0, DT_ACT_RETURN, DT_ATTR_STABCMN, DT_VERS_2_0,
+	&dt_idops_func, "void(int)" },
 { "rindex", DT_IDENT_FUNC, 0, DIF_SUBR_RINDEX, DT_ATTR_STABCMN, DT_VERS_1_1,
 	&dt_idops_func, "int(const char *, const char *, [int])" },
 { "rw_iswriter", DT_IDENT_FUNC, 0, DIF_SUBR_RW_ISWRITER,
diff --git a/test/modules b/test/modules
index 0f01d6e0..7782540c 100644
--- a/test/modules
+++ b/test/modules
@@ -1,3 +1,4 @@
+btrfs
 ext4
 isofs
 nfs
diff --git a/test/unittest/actions/return/err.D_PROTO_ARG.str.d b/test/unittest/actions/return/err.D_PROTO_ARG.str.d
new file mode 100644
index 00000000..d04836f3
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_ARG.str.d
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+/*
+ * ASSERTION: The return() action cannot be passed a string argument.
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	return("");
+}
diff --git a/test/unittest/actions/return/err.D_PROTO_ARG.str.r b/test/unittest/actions/return/err.D_PROTO_ARG.str.r
new file mode 100644
index 00000000..46ca1828
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_ARG.str.r
@@ -0,0 +1,4 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/actions/return/err.D_PROTO_ARG.str.d: [D_PROTO_ARG] line 18: return( ) argument #1 is incompatible with prototype:
+	prototype: int
+	 argument: string
diff --git a/test/unittest/actions/return/err.D_PROTO_ARG.void.d b/test/unittest/actions/return/err.D_PROTO_ARG.void.d
new file mode 100644
index 00000000..dc3b15fa
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_ARG.void.d
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+/*
+ * ASSERTION: The return() action cannot be passed a void argument.
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	return(return(0));
+}
diff --git a/test/unittest/actions/return/err.D_PROTO_ARG.void.r b/test/unittest/actions/return/err.D_PROTO_ARG.void.r
new file mode 100644
index 00000000..1573c557
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_ARG.void.r
@@ -0,0 +1,4 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/actions/return/err.D_PROTO_ARG.void.d: [D_PROTO_ARG] line 18: return( ) argument #1 is incompatible with prototype:
+	prototype: int
+	 argument: void
diff --git a/test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.d b/test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.d
new file mode 100644
index 00000000..97ed0762
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.d
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/*
+ * ASSERTION: The return() action takes exactly one argument.
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	return();
+	exit(0);
+}
diff --git a/test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.r b/test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.r
new file mode 100644
index 00000000..fd7c2512
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/actions/return/err.D_PROTO_LEN.missing_arg.d: [D_PROTO_LEN] line 18: return( ) prototype mismatch: 0 args passed, 1 expected
diff --git a/test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.d b/test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.d
new file mode 100644
index 00000000..0776192b
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.d
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/*
+ * ASSERTION: The return() action takes exactly one argument.
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	return(1, 2);
+	exit(0);
+}
diff --git a/test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.r b/test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.r
new file mode 100644
index 00000000..37ff4d9d
--- /dev/null
+++ b/test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/actions/return/err.D_PROTO_LEN.too_many_args.d: [D_PROTO_LEN] line 18: return( ) prototype mismatch: 2 args passed, 1 expected
diff --git a/test/unittest/actions/return/err.destructive.d b/test/unittest/actions/return/err.destructive.d
new file mode 100644
index 00000000..4c4a246a
--- /dev/null
+++ b/test/unittest/actions/return/err.destructive.d
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/*
+ * ASSERTION: return() is allowed when destructive execution is allowed
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	ok = 0;
+	exit(0);
+}
+
+rawfbt:btrfs:open_ctree:entry
+/ok/
+{
+	return(0);
+}
diff --git a/test/unittest/actions/return/err.destructive.r b/test/unittest/actions/return/err.destructive.r
new file mode 100644
index 00000000..004c30a9
--- /dev/null
+++ b/test/unittest/actions/return/err.destructive.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: could not enable tracing: Destructive actions not allowed
diff --git a/test/unittest/actions/return/tst.destructive.d b/test/unittest/actions/return/tst.destructive.d
new file mode 100644
index 00000000..f1d9e4d3
--- /dev/null
+++ b/test/unittest/actions/return/tst.destructive.d
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/*
+ * ASSERTION: return() is allowed when destructive execution is allowed
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+#pragma D option destructive
+
+BEGIN
+{
+	ok = 0;
+	exit(0);
+}
+
+rawfbt:btrfs:open_ctree:entry
+/ok/
+{
+	return(0);
+}
diff --git a/test/unittest/actions/return/tst.destructive.r b/test/unittest/actions/return/tst.destructive.r
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/test/unittest/actions/return/tst.destructive.r
@@ -0,0 +1 @@
+
-- 
2.43.5




More information about the DTrace-devel mailing list