[DTrace-devel] [PATCH 4/4] rawfbt: selectively allow return() in clauses

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


The return() action is only allowed in clauses associated with a rawfbt
probe on a function listed in /sys/kernel/debug/error_injexction/list.
Use a reject_clause() callback in the rawfbt provider to enforce this.

The list of allowed function is only initialized the first time it is
needed, and its content will be stored in a hashtable for faster
lookup beyond the first.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 libdtrace/dt_prov_fbt.c                       | 134 ++++++++++++++++++
 .../actions/return/err.not_allowed-1.d        |  27 ++++
 .../actions/return/err.not_allowed-1.r        |   2 +
 .../actions/return/err.not_allowed-2.d        |  27 ++++
 .../actions/return/err.not_allowed-2.r        |   2 +
 .../actions/return/err.not_allowed-3.d        |  27 ++++
 .../actions/return/err.not_allowed-3.r        |   2 +
 7 files changed, 221 insertions(+)
 create mode 100644 test/unittest/actions/return/err.not_allowed-1.d
 create mode 100644 test/unittest/actions/return/err.not_allowed-1.r
 create mode 100644 test/unittest/actions/return/err.not_allowed-2.d
 create mode 100644 test/unittest/actions/return/err.not_allowed-2.r
 create mode 100644 test/unittest/actions/return/err.not_allowed-3.d
 create mode 100644 test/unittest/actions/return/err.not_allowed-3.r

diff --git a/libdtrace/dt_prov_fbt.c b/libdtrace/dt_prov_fbt.c
index 50fa0d9d..b98d8d87 100644
--- a/libdtrace/dt_prov_fbt.c
+++ b/libdtrace/dt_prov_fbt.c
@@ -591,6 +591,139 @@ static void kprobe_detach(dtrace_hdl_t *dtp, const dt_probe_t *prp)
 		free(tpn);
 }
 
+/*
+ * raafbt only:
+ *
+ * Accept all clauses, except those that use the return() action in a function
+ * that does not allow error rejection.
+ */
+#define FUNCS_ALLOW_RETURN	"/sys/kernel/debug/error_injection/list"
+
+typedef struct allowed_fn {
+	const char	*mod;
+	const char	*fun;
+	dt_hentry_t	he;
+} allowed_fun_t;
+
+static uint32_t
+fun_hval(const allowed_fun_t *p)
+{
+	return str2hval(p->fun, p->mod ? str2hval(p->mod, 0) : 0);
+}
+
+static int
+fun_cmp(const allowed_fun_t *p, const allowed_fun_t *q) {
+	int	rc;
+
+	if (p->mod != NULL) {
+		if (q->mod == NULL)
+			return 1;
+		else {
+			rc = strcmp(p->mod, q->mod);
+			if (rc != 0)
+				return rc;
+		}
+	} else if (q->mod != NULL)
+		return -1;
+
+	return strcmp(p->fun, q->fun);
+}
+
+DEFINE_HE_STD_LINK_FUNCS(fun, allowed_fun_t, he)
+
+static void *
+fun_del_entry(allowed_fun_t *head, allowed_fun_t *p)
+{
+	head = fun_del(head, p);
+
+	free((char *)p->mod);
+	free((char *)p->fun);
+	free(p);
+
+	return head;
+}
+
+static dt_htab_ops_t fun_htab_ops = {
+	.hval = (htab_hval_fn)fun_hval,
+	.cmp = (htab_cmp_fn)fun_cmp,
+	.add = (htab_add_fn)fun_add,
+	.del = (htab_del_fn)fun_del_entry,
+	.next = (htab_next_fn)fun_next
+};
+
+static void reject_clause(const dt_probe_t *prp, int clsflags)
+{
+	dt_htab_t	*atab = prp->prov->prv_data;
+	allowed_fun_t	tmpl;
+
+	/* If the clause does not have a return() action, allow it. */
+	if (!(clsflags & DT_CLSFLAG_RETURN))
+		return;
+
+	/*
+	 * If the htab of allowed functions for return() does not exist yet,
+	 * create it.
+	 */
+	if (atab == NULL) {
+		FILE		*f;
+		char		*buf = NULL;
+		size_t		len = 0;
+		allowed_fun_t	*entry;
+
+		atab = dt_htab_create(&fun_htab_ops);
+		if (atab == NULL)
+			return;
+
+		prp->prov->prv_data = atab;
+
+		f = fopen(FUNCS_ALLOW_RETURN, "r");
+		if (f == NULL)
+			return;
+
+		while (getline(&buf, &len, f) >= 0) {
+			char	*p;
+
+			entry = (allowed_fun_t *)malloc(sizeof(allowed_fun_t));
+			if (entry == NULL)
+				break;
+
+			p = strchr(buf, ' ');
+			if (p) {
+				*p++ = '\0';
+				if (*p == '[') {
+					char	*q;
+
+					p++;
+					q = strchr(p, ']');
+					if (q)
+						*q = '\0';
+				} else
+					p = NULL;
+			}
+
+			entry->fun = strdup(buf);
+			entry->mod = p != NULL ? strdup(p) : NULL;
+
+			if (dt_htab_insert(atab, entry) < 0)
+				return;
+		}
+
+		fclose(f);
+	}
+
+	tmpl.mod = prp->desc->mod;
+	tmpl.fun = prp->desc->fun;
+	if (dt_htab_lookup(atab, &tmpl) != NULL)
+		return;
+
+	tmpl.mod = NULL;
+	if (dt_htab_lookup(atab, &tmpl) != NULL)
+		return;
+
+	xyerror(D_ACT_RETURN, "return() not allowed for %s:%s:%s:%s\n",
+		prp->desc->prv, prp->desc->mod, prp->desc->fun, prp->desc->prb);
+}
+
 dt_provimpl_t	dt_fbt_fprobe = {
 	.name		= prvname,
 	.prog_type	= BPF_PROG_TYPE_TRACING,
@@ -628,6 +761,7 @@ dt_provimpl_t	dt_rawfbt = {
 	.populate	= &populate,
 	.provide	= &provide,
 	.load_prog	= &dt_bpf_prog_load,
+	.reject_clause	= &reject_clause,
 	.trampoline	= &kprobe_trampoline,
 	.attach		= &kprobe_attach,
 	.detach		= &kprobe_detach,
diff --git a/test/unittest/actions/return/err.not_allowed-1.d b/test/unittest/actions/return/err.not_allowed-1.d
new file mode 100644
index 00000000..246a68b1
--- /dev/null
+++ b/test/unittest/actions/return/err.not_allowed-1.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 not allowed for fbt probes
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+#pragma D option destructive
+
+BEGIN
+{
+	ok = 0;
+	exit(0);
+}
+
+fbt:btrfs:open_ctree:entry
+/ok/
+{
+	return(0);
+}
diff --git a/test/unittest/actions/return/err.not_allowed-1.r b/test/unittest/actions/return/err.not_allowed-1.r
new file mode 100644
index 00000000..e3443c30
--- /dev/null
+++ b/test/unittest/actions/return/err.not_allowed-1.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: could not enable tracing: return() not allowed for fbt:btrfs:open_ctree:entry
diff --git a/test/unittest/actions/return/err.not_allowed-2.d b/test/unittest/actions/return/err.not_allowed-2.d
new file mode 100644
index 00000000..e0d303c7
--- /dev/null
+++ b/test/unittest/actions/return/err.not_allowed-2.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 only allowed for select functions
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+#pragma D option destructive
+
+BEGIN
+{
+	ok = 0;
+	exit(0);
+}
+
+rawfbt:btrfs:close_ctree:entry
+/ok/
+{
+	return(0);
+}
diff --git a/test/unittest/actions/return/err.not_allowed-2.r b/test/unittest/actions/return/err.not_allowed-2.r
new file mode 100644
index 00000000..758d3477
--- /dev/null
+++ b/test/unittest/actions/return/err.not_allowed-2.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: could not enable tracing: return() not allowed for rawfbt:btrfs:close_ctree:entry
diff --git a/test/unittest/actions/return/err.not_allowed-3.d b/test/unittest/actions/return/err.not_allowed-3.d
new file mode 100644
index 00000000..770565c9
--- /dev/null
+++ b/test/unittest/actions/return/err.not_allowed-3.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 only allowed for select functions
+ *
+ * SECTION: Actions and Subroutines/return()
+ */
+
+#pragma D option quiet
+#pragma D option destructive
+
+BEGIN
+{
+	ok = 0;
+	exit(0);
+}
+
+rawfbt:btrfs:*_ctree:entry
+/ok/
+{
+	return(0);
+}
diff --git a/test/unittest/actions/return/err.not_allowed-3.r b/test/unittest/actions/return/err.not_allowed-3.r
new file mode 100644
index 00000000..758d3477
--- /dev/null
+++ b/test/unittest/actions/return/err.not_allowed-3.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: could not enable tracing: return() not allowed for rawfbt:btrfs:close_ctree:entry
-- 
2.43.5




More information about the DTrace-devel mailing list