[DTrace-devel] [PATCH] ERROR probe implementation

Kris Van Hees kris.van.hees at oracle.com
Tue Jan 19 22:58:41 PST 2021


The ERROR probe is special because it is not associated with a system
event.  Instead, it is a 'fake' probe that is reported whenever there
a fault occurs during probe execution.  This can be implemented in
BPF by including the ERROR probe clauses in every probe program that
needs them.

Whenever a fault occurs in one of our BPF programs, a call is made to
dt_probe_error() to record data associated with the fault, and the
clause is terminated with a non-zero return value.  The data items
that can be set are:

	- offset in the clause at which the fault occured
	- fault type
	- value

When a clause ends with a non-zero return value, execution is directed
to the ERROR probe clauses that are included in each probe program that
needs them.  These clauses are compiled as regular probe clauses, but
they are processed separately to resolve the EPID and PRID identifiers
according to the ERROR probe rather than the probe that the clauses are
included for.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 bpf/Build                                     |   3 +-
 bpf/get_bvar.c                                |  23 ++-
 bpf/probe_error.c                             |  36 ++++
 bpf/probe_error.h                             |  14 ++
 include/dtrace/difo_defines.h                 |   1 +
 libdtrace/dt_as.c                             |  91 ++++++++++
 libdtrace/dt_bpf.c                            |   5 +-
 libdtrace/dt_cc.c                             |   6 +
 libdtrace/dt_cg.c                             | 155 ++++++++++++++++--
 libdtrace/dt_consume.c                        |   9 +
 libdtrace/dt_dof.c                            |   4 +-
 libdtrace/dt_handle.c                         |  28 ++--
 libdtrace/dt_impl.h                           |   6 +
 libdtrace/dt_map.c                            |  12 +-
 libdtrace/dt_pcb.h                            |   3 +-
 libdtrace/dt_probe.c                          | 125 +++++++++++++-
 libdtrace/dt_prov_dtrace.c                    |   8 +-
 libdtrace/dt_work.c                           |   4 +-
 libdtrace/dtrace.h                            |   4 +-
 .../tst.DTRACEFLT_BADADDR.null_ptr_field.d    |  32 ++++
 .../tst.DTRACEFLT_BADADDR.null_ptr_field.r    |   3 +
 test/unittest/error/tst.error.d               |  11 +-
 test/unittest/error/tst.error.r               |   2 +-
 test/unittest/error/tst.errorend.d            |  11 +-
 test/unittest/error/tst.errorend.r            |   2 +-
 25 files changed, 523 insertions(+), 75 deletions(-)
 create mode 100644 bpf/probe_error.c
 create mode 100644 bpf/probe_error.h
 create mode 100644 test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d
 create mode 100644 test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r

diff --git a/bpf/Build b/bpf/Build
index e9f2b1e2..36cf1fa0 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -1,5 +1,5 @@
 # Oracle Linux DTrace.
-# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 2021, 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.
 
@@ -26,6 +26,7 @@ bpf_dlib_SOURCES = \
 	get_bvar.c \
 	get_gvar.c set_gvar.c \
 	get_tvar.c set_tvar.c \
+	probe_error.c \
 	memcpy.c strnlen.c
 
 bpf-check: $(objdir)/include/.dir.stamp
diff --git a/bpf/get_bvar.c b/bpf/get_bvar.c
index 174626f9..8d941756 100644
--- a/bpf/get_bvar.c
+++ b/bpf/get_bvar.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
  */
 #include <linux/bpf.h>
 #include <stddef.h>
@@ -8,9 +8,12 @@
 #include <bpf-helpers.h>
 #include <dtrace/conf.h>
 #include <dtrace/dif_defines.h>
+#include <dtrace/faults_defines.h>
 #include <dt_dctx.h>
 #include <dt_state.h>
 
+#include "probe_error.h"
+
 #ifndef noinline
 # define noinline	__attribute__((noinline))
 #endif
@@ -18,6 +21,12 @@
 extern struct bpf_map_def cpuinfo;
 extern struct bpf_map_def state;
 
+#define error(mst, fault, illval) \
+	({ \
+		dt_probe_error((mst), -1, (fault), (illval)); \
+		-1; \
+	})
+
 noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
 {
 	switch (id) {
@@ -75,13 +84,13 @@ noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
 		/* Chase pointers val = current->real_parent->tgid. */
 		ptr = bpf_get_current_task();
 		if (ptr == 0)
-			return -1;
+			return error(mst, DTRACEFLT_BADADDR, ptr);
 		if (bpf_probe_read((void *)&ptr, 8,
 		    (const void *)(ptr + *parent_off)))
-			return -1;
+			return error(mst, DTRACEFLT_BADADDR, ptr + *parent_off);
 		if (bpf_probe_read((void *)&val, 4,
 		    (const void *)(ptr + *tgid_off)))
-			return -1;
+			return error(mst, DTRACEFLT_BADADDR, ptr + *tgid_off);
 
 		return (uint64_t)val;
 	}
@@ -106,10 +115,6 @@ noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
 	}
 	default:
 		/* Not implemented yet. */
-#if 1
-		return (uint64_t)-1;
-#else
-		return (uint64_t)id;
-#endif
+		return error(mst, DTRACEFLT_ILLOP, 0);
 	}
 }
diff --git a/bpf/probe_error.c b/bpf/probe_error.c
new file mode 100644
index 00000000..f697b38f
--- /dev/null
+++ b/bpf/probe_error.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <linux/bpf.h>
+#include <stdint.h>
+#include <bpf-helpers.h>
+#include <dt_dctx.h>
+
+#ifndef noinline
+# define noinline	__attribute__((noinline))
+#endif
+
+/*
+ * DTrace ERROR probes provide 6 arguments:
+ *	arg0 = always NULL (used to be kernel consumer state pointer)
+ *      arg1 = EPID of probe that triggered the fault
+ *      arg2 = clause index of code that triggered the fault
+ *      arg3 = BPF offset in the clause that triggered the fault (or -1)
+ *      arg4 = fault type
+ *      arg5 = fault-specific value (usually address being accessed or 0)
+ */
+noinline int64_t dt_probe_error(dt_mstate_t *mst, uint64_t pc, uint64_t fault,
+				uint64_t illval)
+{
+	mst->argv[0] = 0;
+	mst->argv[1] = mst->epid;
+	mst->argv[2] = mst->clid;
+	mst->argv[3] = pc;
+	mst->argv[4] = fault;
+	mst->argv[5] = illval;
+
+	mst->fault |= fault;
+
+	return fault;
+}
diff --git a/bpf/probe_error.h b/bpf/probe_error.h
new file mode 100644
index 00000000..8bd5ae29
--- /dev/null
+++ b/bpf/probe_error.h
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ */
+#ifndef BPF_PROBE_ERRROR_H
+#define BPF_PROBE_ERRROR_H
+
+#include <stdint.h>
+#include <dt_dctx.h>
+
+extern int64_t dt_probe_error(dt_mstate_t *mst, uint64_t pc, uint64_t fault,
+			      uint64_t illval);
+
+#endif /* BPF_PROBE_ERRROR_H */
diff --git a/include/dtrace/difo_defines.h b/include/dtrace/difo_defines.h
index 1175a001..75e0b449 100644
--- a/include/dtrace/difo_defines.h
+++ b/include/dtrace/difo_defines.h
@@ -21,5 +21,6 @@ struct dtrace_difo;
  * DIFO flags.
  */
 #define DIFOFLG_DESTRUCTIVE		1	/* Uses destructive ops */
+#define DIFOFLG_ERROR			2	/* May trigger ERROR probe */
 
 #endif /* _DTRACE_DIFO_DEFINES_H */
diff --git a/libdtrace/dt_as.c b/libdtrace/dt_as.c
index 4ca2f420..db65bdc7 100644
--- a/libdtrace/dt_as.c
+++ b/libdtrace/dt_as.c
@@ -258,6 +258,13 @@ dt_as(dt_pcb_t *pcb)
 	if (labels == NULL)
 		longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
 
+	/*
+	 * Indicate whether this function contains code that may trigger the
+	 * ERROR probe.
+	 */
+	if (pcb->pcb_faultlbl > 0)
+		dp->dtdo_flags |= DIFOFLG_ERROR;
+
 	/*
 	 * Make an initial pass through the instruction list, filling in the
 	 * instruction buffer with valid instructions and skipping labeled nops.
@@ -605,3 +612,87 @@ fail:
 
 	return dp;
 }
+
+dtrace_difo_t *
+dt_difo_copy(dtrace_hdl_t *dtp, const dtrace_difo_t *odp)
+{
+	dtrace_difo_t	*dp;
+
+	dp = dt_zalloc(dtp, sizeof(dtrace_difo_t));
+	if (dp == NULL)
+		goto no_mem;
+
+	/* Copy the executable code. */
+	dp->dtdo_len = odp->dtdo_len;
+	dp->dtdo_buf = dt_calloc(dtp, dp->dtdo_len, sizeof(struct bpf_insn));
+	if (dp->dtdo_buf == NULL)
+		goto no_mem;
+
+	memcpy(dp->dtdo_buf, odp->dtdo_buf,
+	       dp->dtdo_len * sizeof(struct bpf_insn));
+
+	/* Copy the string table (if any). */
+	if (odp->dtdo_strlen > 0) {
+		dp->dtdo_strlen = odp->dtdo_strlen;
+		dp->dtdo_strtab = dt_zalloc(dtp, dp->dtdo_strlen);
+		if (dp->dtdo_strtab == NULL)
+			goto no_mem;
+
+		memcpy(dp->dtdo_strtab, odp->dtdo_strtab, dp->dtdo_strlen);
+	}
+
+	/* Copy the variable table (if any). */
+	if (odp->dtdo_varlen > 0) {
+		dp->dtdo_varlen = odp->dtdo_varlen;
+		dp->dtdo_vartab = dt_calloc(dtp, dp->dtdo_varlen,
+					    sizeof(dtrace_difv_t));
+		if (dp->dtdo_vartab == NULL)
+			goto no_mem;
+
+		memcpy(dp->dtdo_vartab, odp->dtdo_vartab,
+		       dp->dtdo_varlen * sizeof(dtrace_difv_t));
+	}
+
+	/* Copy the relocation tables (if any). */
+	if (odp->dtdo_brelen > 0) {
+		dp->dtdo_brelen = odp->dtdo_brelen;
+		dp->dtdo_breltab = dt_calloc(dtp, dp->dtdo_brelen,
+					    sizeof(dof_relodesc_t));
+		if (dp->dtdo_breltab == NULL)
+			goto no_mem;
+
+		memcpy(dp->dtdo_breltab, odp->dtdo_breltab,
+		       dp->dtdo_brelen * sizeof(dof_relodesc_t));
+	}
+
+	if (odp->dtdo_krelen > 0) {
+		dp->dtdo_krelen = odp->dtdo_krelen;
+		dp->dtdo_kreltab = dt_calloc(dtp, dp->dtdo_krelen,
+					    sizeof(dof_relodesc_t));
+		if (dp->dtdo_kreltab == NULL)
+			goto no_mem;
+
+		memcpy(dp->dtdo_kreltab, odp->dtdo_kreltab,
+		       dp->dtdo_krelen * sizeof(dof_relodesc_t));
+	}
+
+	if (odp->dtdo_urelen > 0) {
+		dp->dtdo_urelen = odp->dtdo_urelen;
+		dp->dtdo_ureltab = dt_calloc(dtp, dp->dtdo_urelen,
+					    sizeof(dof_relodesc_t));
+		if (dp->dtdo_ureltab == NULL)
+			goto no_mem;
+
+		memcpy(dp->dtdo_ureltab, odp->dtdo_ureltab,
+		       dp->dtdo_urelen * sizeof(dof_relodesc_t));
+	}
+
+	dp->dtdo_ddesc = dt_datadesc_hold(odp->dtdo_ddesc);
+	dp->dtdo_flags = odp->dtdo_flags;
+
+	return dp;
+
+no_mem:
+	dt_set_errno(dtp, EDT_NOMEM);
+	return NULL;
+}
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index 1705c990..06148789 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2021, 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.
  */
@@ -386,6 +386,9 @@ dt_bpf_load_progs(dtrace_hdl_t *dtp, uint_t cflags)
 		dtrace_difo_t	*dp;
 		int		fd, rc;
 
+		if (prp == dtp->dt_error)
+			continue;
+
 		dp = dt_program_construct(dtp, prp, cflags);
 		if (dp == NULL)
 			return -1;
diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
index 691c31f2..12f60601 100644
--- a/libdtrace/dt_cc.c
+++ b/libdtrace/dt_cc.c
@@ -1585,6 +1585,12 @@ dt_clause_create(dtrace_hdl_t *dtp, dtrace_difo_t *dp)
 	dp->dtdo_ddesc = yypcb->pcb_ddesc;
 	yypcb->pcb_ddesc = NULL;
 
+	/*
+	 * Special case: ERROR probe default clause.
+	 */
+	if (yypcb->pcb_cflags & DTRACE_C_EPROBE)
+		dp->dtdo_ddesc->dtdd_uarg = DT_ECB_ERROR;
+
 	/*
 	 * Generate a symbol name.
 	 */
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 858eb5a5..cd911430 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -164,38 +164,75 @@ typedef struct {
 	dt_irlist_t	*dlp;
 	dt_activity_t	act;
 	uint_t		lbl_exit;
+	int		is_reg;
 } dt_clause_arg_t;
 
 static int
 dt_cg_call_clause(dtrace_hdl_t *dtp, dt_ident_t *idp, dt_clause_arg_t *arg)
 {
 	dt_irlist_t	*dlp = arg->dlp;
+	dtrace_difo_t	*dp = dt_dlib_get_func_difo(dtp, idp);
 
 	/*
-	 *	if (*dctx.act != act)	// ldw %r0, [%fp + DCTX_FP(DCTX_ACT)]
-	 *		goto exit;	// ldw %r0, [%r0 + 0]
-	 *				// jne %r0, act, lbl_exit
-	 *	dt_clause(dctx);	// mov %r1, %fp
+	 * Regular clause (not ERROR probe clause), so activity state checking
+	 * must be done in case we should skip any furthe processing.
+	 */
+	if (arg->is_reg) {
+		/*
+		 *	if (*dctx.act != act)	// ldw %r0, [%fp +
+		 *				//	     DCTX_FP(DCTX_ACT)]
+		 *		goto exit;	// ldw %r0, [%r0 + 0]
+		 *				// jne %r0, act, lbl_exit
+		 */
+		emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP,
+				    DCTX_FP(DCTX_ACT)));
+		emit(dlp,  BPF_LOAD(BPF_W, BPF_REG_0, BPF_REG_0, 0));
+		emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, arg->act,
+					  arg->lbl_exit));
+
+		/*
+		 * If this identifier is the first one to possibly trigger the
+		 * ERROR probe, we need to allocate a fault label.
+		 */
+		if ((dp->dtdo_flags & DIFOFLG_ERROR) &&
+		    yypcb->pcb_faultlbl == 0)
+			yypcb->pcb_faultlbl = dt_irlist_label(dlp);
+	}
+
+	/*
+	 *	rc = dt_clause(dctx);	// mov %r1, %fp
 	 *				// add %r1, DCTX_FP(0)
 	 *				// call dt_program
 	 */
-	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP, DCTX_FP(DCTX_ACT)));
-	emit(dlp,  BPF_LOAD(BPF_W, BPF_REG_0, BPF_REG_0, 0));
-	emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, arg->act, arg->lbl_exit));
 	emit(dlp,  BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
 	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DCTX_FP(0)));
 	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
 
+	/*
+	 * Regular clause (not ERROR probe clause) so if the clause may trigger
+	 * the ERROR probe, we must jump to the ERROR probe handling if a
+	 * non-zero value is returned.
+	 */
+	if (arg->is_reg && (dp->dtdo_flags & DIFOFLG_ERROR)) {
+		/*
+		 *	if (rc != 0)		// jne %r0, 0, lbl_fault
+		 *		goto fault;
+		 */
+		emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0,
+					  yypcb->pcb_faultlbl));
+	}
+
 	return 0;
 }
 
 static void
-dt_cg_tramp_call_clauses(dt_pcb_t *pcb, dt_activity_t act)
+dt_cg_tramp_call_clauses(dt_pcb_t *pcb, dt_probe_t *prp, dt_activity_t act,
+			 int is_reg)
 {
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
-	dt_clause_arg_t	arg = { dlp, act, pcb->pcb_exitlbl };
+	dt_clause_arg_t	arg = { dlp, act, pcb->pcb_exitlbl, is_reg };
 
-	dt_probe_clause_iter(pcb->pcb_hdl, pcb->pcb_probe,
+	dt_probe_clause_iter(pcb->pcb_hdl, prp,
 			     (dt_clause_f *)dt_cg_call_clause, &arg);
 }
 
@@ -218,7 +255,21 @@ dt_cg_tramp_return(dt_pcb_t *pcb)
 void
 dt_cg_tramp_epilogue(dt_pcb_t *pcb)
 {
-	dt_cg_tramp_call_clauses(pcb, DT_ACTIVITY_ACTIVE);
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+
+	dt_cg_tramp_call_clauses(pcb, pcb->pcb_probe, DT_ACTIVITY_ACTIVE, 1);
+
+	/*
+	 * If there are clauses that may trigger the ERROR probe, set the
+	 * fault label and add calls to the ERROR probe clauses.
+	 */
+	if (pcb->pcb_faultlbl > 0) {
+		emit(dlp,  BPF_JUMP(yypcb->pcb_exitlbl));
+		emitl(dlp, pcb->pcb_faultlbl,
+			   BPF_NOP());
+		dt_cg_tramp_call_clauses(pcb, pcb->pcb_hdl->dt_error, 0, 0);
+	}
+
 	dt_cg_tramp_return(pcb);
 }
 
@@ -227,7 +278,18 @@ dt_cg_tramp_epilogue_advance(dt_pcb_t *pcb, dt_activity_t act)
 {
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
 
-	dt_cg_tramp_call_clauses(pcb, act);
+	dt_cg_tramp_call_clauses(pcb, pcb->pcb_probe, act, 1);
+
+	/*
+	 * If there are clauses that may trigger the ERROR probe, set the
+	 * fault label and add calls to the ERROR probe clauses.
+	 */
+	if (pcb->pcb_faultlbl > 0) {
+		emit(dlp,  BPF_JUMP(yypcb->pcb_exitlbl));
+		emitl(dlp, pcb->pcb_faultlbl,
+			   BPF_NOP());
+		dt_cg_tramp_call_clauses(pcb, pcb->pcb_hdl->dt_error, 0, 0);
+	}
 
 	/*
 	 *	(*dctx.act)++;		// lddw %r0, [%fp + DCTX_FP(DCTX_ACT)]
@@ -369,13 +431,16 @@ dt_cg_epilogue(dt_pcb_t *pcb)
 		 *	if (rc != 0)
 		 *	    goto exit;		// jne %r0, 0, pcb->pcb_exitlbl
 		 */
+#if 0
 		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP, DT_STK_DCTX));
 		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_0, DCTX_MST));
 		emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_0, DMST_FAULT));
 		emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, 0, pcb->pcb_exitlbl));
+#endif
 
 		/*
-		 *	bpf_perf_event_output(dctx->ctx, &buffers, BPF_F_CURRENT_CPU,
+		 *	bpf_perf_event_output(dctx->ctx, &buffers,
+		 *			      BPF_F_CURRENT_CPU,
 		 *			      buf - 4, bufoff + 4);
 		 *				// lddw %r1, [%fp + DT_STK_DCTX]
 		 *				// lddw %r1, [%r1 + DCTX_CTX]
@@ -384,7 +449,7 @@ dt_cg_epilogue(dt_pcb_t *pcb)
 		 *				// mov %r4, %r9
 		 *				// add %r4, -4
 		 *				// mov %r5, pcb->pcb_bufoff
-		 *				// add %r4, 4
+		 *				// add %r5, 4
 		 *				// call bpf_perf_event_output
 		 *
 		 */
@@ -411,6 +476,47 @@ dt_cg_epilogue(dt_pcb_t *pcb)
 	TRACE_REGSET("Epilogue: End  ");
 }
 
+/*
+ * Generate code for a fault condition.  A call is made to dt_probe_error() to
+ * set the fault information, and then a non-zero return from the function is
+ * performed.
+ */
+static void
+dt_cg_probe_error(dt_pcb_t *pcb, uint32_t off, uint32_t fault, uint64_t illval)
+{
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+	dt_regset_t	*drp = pcb->pcb_regs;
+	dt_ident_t	*idp = dt_dlib_get_func(yypcb->pcb_hdl,
+						"dt_probe_error");
+
+	assert(idp != NULL);
+
+	if (pcb->pcb_faultlbl == 0)
+		pcb->pcb_faultlbl = dt_irlist_label(dlp);
+
+	/*
+	 *	return dt_probe_error(	// lddw %r1, %fp, DT_STK_DCTX
+	 *		dctx->mst,	// lddw %r1, %r1, DCTX_MST
+	 *		off,		// mov %r2, off
+	 *		fault,		// mov %r3, fault
+	 *		illval);	// mov %r4, illval
+	 *				// call dt_probe_error
+	 *				// exit
+	 */
+	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_MST));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, off));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_3, fault));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_4, illval));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+	emit(dlp,  BPF_RETURN());
+}
+
 /*
  * Generate an instruction sequence to fill a gap in the output buffer with 0
  * values.  This is used to ensure that there are no uninitialized bytes in the
@@ -2811,14 +2917,27 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 		break;
 
 	case DT_TOK_PTR:
-	case DT_TOK_DOT:
+	case DT_TOK_DOT: {
+		uint_t	lbl_valid = dt_irlist_label(dlp);
+
 		assert(dnp->dn_right->dn_kind == DT_NODE_IDENT);
 		dt_cg_node(dnp->dn_left, dlp, drp);
 
 		/*
-		 * Ensure that the lvalue is not the NULL pointer.
+		 * If the lvalue is the NULL pointer, we must report a BADARR
+		 * fault.
+		 *
+		 *	if (left != 0)		// jne %lreg, 0, valid
+		 *		goto valid;
+		 *				//     (report BADADDR fault)
+		 * valid:
 		 */
-		emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_left->dn_reg, 0, yypcb->pcb_exitlbl));
+		lbl_valid = dt_irlist_label(dlp);
+		emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, dnp->dn_left->dn_reg, 0,
+					  lbl_valid));
+		dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
+		emitl(dlp, lbl_valid,
+			   BPF_NOP());
 
 		/*
 		 * If the left-hand side of PTR or DOT is a dynamic variable,
@@ -2900,6 +3019,7 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 
 		dnp->dn_reg = dnp->dn_left->dn_reg;
 		break;
+	}
 
 	case DT_TOK_STRING:
 		if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
@@ -4253,6 +4373,7 @@ dt_cg(dt_pcb_t *pcb, dt_node_t *dnp)
 	dt_irlist_destroy(&pcb->pcb_ir);
 	dt_irlist_create(&pcb->pcb_ir);
 	pcb->pcb_exitlbl = dt_irlist_label(&pcb->pcb_ir);
+	pcb->pcb_faultlbl = 0;
 
 	pcb->pcb_bufoff = 0;
 
diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
index 1a562983..a034ee8e 100644
--- a/libdtrace/dt_consume.c
+++ b/libdtrace/dt_consume.c
@@ -1979,6 +1979,15 @@ dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
 		if (rval != 0)
 			return dt_set_errno(dtp, EDT_BADEPID);
 
+		if (pdat->dtpda_ddesc->dtdd_uarg != DT_ECB_DEFAULT) {
+			rval = dt_handle(dtp, pdat);
+
+			if (rval == DTRACE_CONSUME_NEXT)
+				return DTRACE_WORKSTATUS_OKAY;
+			if (rval == DTRACE_CONSUME_ERROR)
+				return DTRACE_WORKSTATUS_ERROR;
+		}
+
 		if (flow)
 			dt_flowindent(dtp, pdat, *last, DTRACE_EPIDNONE);
 
diff --git a/libdtrace/dt_dof.c b/libdtrace/dt_dof.c
index c78e38a3..6827464a 100644
--- a/libdtrace/dt_dof.c
+++ b/libdtrace/dt_dof.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021, 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.
  */
@@ -891,8 +891,10 @@ dtrace_getopt_dof(dtrace_hdl_t *dtp)
 void *
 dtrace_geterr_dof(dtrace_hdl_t *dtp)
 {
+#if 0
 	if (dtp->dt_errprog != NULL)
 		return dtrace_dof_create(dtp, dtp->dt_errprog, 0);
+#endif
 
 	dt_set_errno(dtp, EDT_BADERROR);
 	return NULL;
diff --git a/libdtrace/dt_handle.c b/libdtrace/dt_handle.c
index 52ff27a8..8e9da52d 100644
--- a/libdtrace/dt_handle.c
+++ b/libdtrace/dt_handle.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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.
  */
@@ -16,7 +16,6 @@
 #include <dt_impl.h>
 #include <dt_program.h>
 
-#ifdef FIXME
 static const char _dt_errprog[] =
 "dtrace:::ERROR"
 "{"
@@ -26,7 +25,6 @@ static const char _dt_errprog[] =
 "	trace(arg4);"
 "	trace(arg5);"
 "}";
-#endif
 
 int
 dtrace_handle_err(dtrace_hdl_t *dtp, dtrace_handle_err_f *hdlr, void *arg)
@@ -51,23 +49,21 @@ dtrace_handle_err(dtrace_hdl_t *dtp, dtrace_handle_err_f *hdlr, void *arg)
 	if (dtp->dt_options[DTRACEOPT_GRABANON] != DTRACEOPT_UNSET)
 		goto out;
 
-#if 0
-	if ((pgp = dtrace_program_strcompile(dtp, _dt_errprog,
-	    DTRACE_PROBESPEC_NAME, DTRACE_C_ZDEFS, 0, NULL)) == NULL)
+	pgp = dtrace_program_strcompile(dtp, _dt_errprog, DTRACE_PROBESPEC_NAME,
+					DTRACE_C_ZDEFS | DTRACE_C_EPROBE, 0,
+					NULL);
+	if (pgp == NULL)
 		return dt_set_errno(dtp, dtrace_errno(dtp));
 
-	stp = dt_list_next(&pgp->dp_stmts);
-	assert(stp != NULL);
-
-	edp = stp->ds_desc->dtsd_ecbdesc;
-	assert(edp != NULL);
-	edp->dted_uarg = DT_ECB_ERROR;
-#endif
+	if (dtrace_program_exec(dtp, pgp, NULL) == -1)
+		return -1;		/* errno already set */
 
 out:
 	dtp->dt_errhdlr = hdlr;
 	dtp->dt_errarg = arg;
+#if 0
 	dtp->dt_errprog = pgp;
+#endif
 
 	return 0;
 }
@@ -148,7 +144,10 @@ dt_handle_err(dtrace_hdl_t *dtp, dtrace_probedata_t *data)
 
 	if (dd->dtdd_nrecs != 5 || strcmp(pd->prv, "dtrace") != 0 ||
 	    strcmp(pd->prb, "ERROR") != 0)
+{
+fprintf(stderr, "%s() - %s:%s:%s:%s\n", __func__, pd->prv, pd->mod, pd->fun, pd->prb);
 		return dt_set_errno(dtp, EDT_BADERROR);
+}
 
 	/*
 	 * This is an error.  We have the following items here:  EPID,
@@ -157,7 +156,10 @@ dt_handle_err(dtrace_hdl_t *dtp, dtrace_probedata_t *data)
 	epid = (uint32_t)DT_REC(uint64_t, 0);
 
 	if (dt_epid_lookup(dtp, epid, &errdd, &errpd) != 0)
+{
+fprintf(stderr, "%s() - EPID not found\n", __func__);
 		return dt_set_errno(dtp, EDT_BADERROR);
+}
 
 	err.dteda_ddesc = errdd;
 	err.dteda_pdesc = errpd;
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index bc996929..98250d70 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -287,6 +287,8 @@ struct dtrace_hdl {
 	uint32_t dt_probes_sz;	/* size of array of probes */
 	uint32_t dt_probe_id;	/* next available probe id */
 
+	struct dt_probe *dt_error; /* ERROR probe */
+
 	dt_list_t dt_provlist;	/* linked list of dt_provider_t's */
 	struct dt_provider **dt_provs; /* hash table of dt_provider_t's */
 	uint_t dt_provbuckets;	/* number of provider hash buckets */
@@ -350,7 +352,9 @@ struct dtrace_hdl {
 	int dt_aggmap_fd;	/* file descriptor for the 'aggs' BPF map */
 	dtrace_handle_err_f *dt_errhdlr; /* error handler, if any */
 	void *dt_errarg;	/* error handler argument */
+#if 0
 	dtrace_prog_t *dt_errprog; /* error handler program, if any */
+#endif
 	dtrace_handle_drop_f *dt_drophdlr; /* drop handler, if any */
 	void *dt_droparg;	/* drop handler argument */
 	dtrace_handle_proc_f *dt_prochdlr; /* proc handler, if any */
@@ -728,6 +732,7 @@ extern dtrace_difo_t *dt_program_construct(dtrace_hdl_t *dtp,
 extern void dt_pragma(dt_node_t *);
 extern int dt_reduce(dtrace_hdl_t *, dt_version_t);
 extern dtrace_difo_t *dt_as(dt_pcb_t *);
+extern dtrace_difo_t *dt_difo_copy(dtrace_hdl_t *dtp, const dtrace_difo_t *odp);
 extern void dt_dis_program(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, FILE *fp);
 extern void dt_dis_difo(const dtrace_difo_t *dp, FILE *fp);
 
@@ -735,6 +740,7 @@ extern int dt_aggregate_go(dtrace_hdl_t *);
 extern int dt_aggregate_init(dtrace_hdl_t *);
 extern void dt_aggregate_destroy(dtrace_hdl_t *);
 
+extern dtrace_datadesc_t *dt_datadesc_hold(dtrace_datadesc_t *ddp);
 extern void dt_datadesc_release(dtrace_hdl_t *, dtrace_datadesc_t *);
 extern dtrace_datadesc_t *dt_datadesc_create(dtrace_hdl_t *);
 extern int dt_datadesc_finalize(dtrace_hdl_t *, dtrace_datadesc_t *);
diff --git a/libdtrace/dt_map.c b/libdtrace/dt_map.c
index 6197d1d6..b7f067fb 100644
--- a/libdtrace/dt_map.c
+++ b/libdtrace/dt_map.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021, 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.
  */
@@ -15,10 +15,11 @@
 #include <dt_probe.h>
 #include <dt_printf.h>
 
-static void
+dtrace_datadesc_t *
 dt_datadesc_hold(dtrace_datadesc_t *ddp)
 {
 	ddp->dtdd_refcnt++;
+	return ddp;
 }
 
 void
@@ -49,9 +50,7 @@ dt_datadesc_create(dtrace_hdl_t *dtp)
 		return NULL;
 	}
 
-	dt_datadesc_hold(ddp);
-
-	return ddp;
+	return dt_datadesc_hold(ddp);
 }
 
 int
@@ -128,8 +127,7 @@ dt_epid_add(dtrace_hdl_t *dtp, dtrace_datadesc_t *ddp, dtrace_id_t prid)
 	if (dtp->dt_ddesc[epid] != NULL)
 		return epid;
 
-	dt_datadesc_hold(ddp);
-	dtp->dt_ddesc[epid] = ddp;
+	dtp->dt_ddesc[epid] = dt_datadesc_hold(ddp);
 	dtp->dt_pdesc[epid] = (dtrace_probedesc_t *)dtp->dt_probes[prid]->desc;
 
 	return epid;
diff --git a/libdtrace/dt_pcb.h b/libdtrace/dt_pcb.h
index cc39a0f3..0813c089 100644
--- a/libdtrace/dt_pcb.h
+++ b/libdtrace/dt_pcb.h
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2021, 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.
  */
@@ -44,6 +44,7 @@ typedef struct dt_pcb {
 	uint32_t pcb_bufoff;	/* output buffer offset (for DFUNCs) */
 	dt_irlist_t pcb_ir;	/* list of unrelocated IR instructions */
 	uint_t pcb_exitlbl;	/* label for exit of program */
+	uint_t pcb_faultlbl;	/* label for fault handling */
 	uint_t pcb_asvidx;	/* assembler vartab index (see dt_as.c) */
 	ulong_t **pcb_asxrefs;	/* assembler imported xlators (see dt_as.c) */
 	uint_t pcb_asxreflen;	/* assembler xlator map length (see dt_as.c) */
diff --git a/libdtrace/dt_probe.c b/libdtrace/dt_probe.c
index 23ee49ad..ad3ffb4e 100644
--- a/libdtrace/dt_probe.c
+++ b/libdtrace/dt_probe.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021, 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.
  */
@@ -22,6 +22,7 @@
 #include <dt_string.h>
 #include <dt_htab.h>
 #include <dt_list.h>
+#include <dt_bpf.h>
 
 typedef struct dt_probeclause {
 	dt_list_t	list;
@@ -1268,18 +1269,130 @@ dtrace_probe_iter(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp,
 	return dt_probe_iter(dtp, pdp, NULL, func, arg);
 }
 
+/*
+ * Create an ERROR-probe specific copy of a given clause.
+ *
+ * A modified copy of the clause is necessary because the ERROR probe may share
+ * some clauses with other probes, and yet it needs to be handled differently.
+ *
+ * The following modifications are made in the copy of the clause:
+ *
+ *	- it is named dt_error_N where N is taken from the original clause
+ *	  dt_clause_N (which also guarantees uniqueness)
+ *	- all references to the EPID identifier in the DIFO are resolved
+ *	  in-place by assigning a new EPID for the clause in the ERROR probe
+ *	- all references to the PRID identifier in the DIFO are resolved
+ *	  in-place with the probe id of the ERROR probe
+ *	- EPID and PRID records are stripped from the relocation records
+ */
+dt_ident_t *
+dt_probe_error_clause(dtrace_hdl_t *dtp, dt_ident_t *idp)
+{
+	char		*name;
+	int		len;
+	dtrace_difo_t	*dp = dt_dlib_get_func_difo(dtp, idp);
+	dt_ident_t	*nidp = NULL;
+	dtrace_difo_t	*ndp;
+	dof_relodesc_t	*rp, *nrp;
+	dtrace_epid_t	epid = dt_epid_add(dtp, dp->dtdo_ddesc,
+					   dtp->dt_error->desc->id);;
+
+	/*
+	 * Copy the DIFO.
+	 */
+	ndp = dt_difo_copy(dtp, dp);
+	if (ndp == NULL)
+		goto no_mem;
+
+	/*
+	 * Resolve EPID and PRID references.
+	 */
+	len = ndp->dtdo_brelen;
+	rp = nrp = ndp->dtdo_breltab;
+	for (; len != 0; len--, rp++) {
+		const char	*name = &ndp->dtdo_strtab[rp->dofr_name];
+		dt_ident_t	*ridp = dt_dlib_get_sym(dtp, name);
+		uint32_t	val = 0;
+
+		if (ridp != NULL && ridp->di_kind == DT_IDENT_SCALAR) {
+			if (ridp->di_id == DT_CONST_EPID)
+				val = epid;
+			else if (ridp->di_id == DT_CONST_PRID)
+				val = dtp->dt_error->desc->id;
+
+			if (val > 0) {
+				uint_t	ioff = rp->dofr_offset /
+					       sizeof(struct bpf_insn);
+
+				if (rp->dofr_type == R_BPF_64_64) {
+					ndp->dtdo_buf[ioff].imm = val;
+					ndp->dtdo_buf[ioff + 1].imm = 0;
+				} else if (rp->dofr_type == R_BPF_64_32)
+					ndp->dtdo_buf[ioff].imm = val;
+
+				continue;
+			}
+		}
+
+		if (nrp != rp)
+			*nrp = *rp;
+
+		nrp++;
+	}
+
+	ndp->dtdo_brelen -= rp - nrp;
+
+	/*
+	 * Generate a new symbol name from the given identifier.
+	 */
+	len = strlen(idp->di_name);
+	name = dt_alloc(dtp, len);
+	if (name == NULL)
+		goto no_mem;
+
+	snprintf(name, len, "dt_error_%s", idp->di_name + 10);
+
+	/*
+	 * Add the new symbol to the BPF identifier table and associate the
+	 * modified copy of the DIFO with the symbol.
+	 */
+	nidp = dt_dlib_add_func(dtp, name);
+	dt_free(dtp, name);
+	if (nidp == NULL)
+		goto no_mem;
+
+	dt_ident_set_data(nidp, ndp);
+
+	return nidp;
+
+no_mem:
+	if (ndp != NULL)
+		dt_difo_free(dtp, ndp);
+	if (nidp != NULL)
+		dt_ident_destroy(nidp);
+
+	dt_set_errno(dtp, EDT_NOMEM);
+	return NULL;
+}
+
 int
 dt_probe_add_clause(dtrace_hdl_t *dtp, dt_probe_t *prp, dt_ident_t *idp)
 {
 	dt_probeclause_t	*pcp;
 
 	pcp = dt_zalloc(dtp, sizeof(dt_probeclause_t));;
-	if (pcp == NULL) {
-		dt_set_errno(dtp, EDT_NOMEM);
-		return -1;
-	}
+	if (pcp == NULL)
+		return dt_set_errno(dtp, EDT_NOMEM);
+
+	if (prp == dtp->dt_error) {
+		pcp->clause = dt_probe_error_clause(dtp, idp);
+		if (pcp->clause == NULL) {
+			dt_free(dtp, pcp);
+			return 0;
+		}
+	} else
+		pcp->clause = idp;
 
-	pcp->clause = idp;
 	dt_list_append(&prp->clauses, pcp);
 
 	return 0;
diff --git a/libdtrace/dt_prov_dtrace.c b/libdtrace/dt_prov_dtrace.c
index b28f212d..b9929c35 100644
--- a/libdtrace/dt_prov_dtrace.c
+++ b/libdtrace/dt_prov_dtrace.c
@@ -48,13 +48,19 @@ static int populate(dtrace_hdl_t *dtp)
 		n++;
 		dt_list_append(&dtp->dt_enablings, prp);
 	}
+
 	prp = tp_probe_insert(dtp, prv, prvname, modname, funname, "END");
 	if (prp) {
 		n++;
 		dt_list_append(&dtp->dt_enablings, prp);
 	}
-	if (tp_probe_insert(dtp, prv, prvname, modname, funname, "ERROR"))
+
+	prp = tp_probe_insert(dtp, prv, prvname, modname, funname, "ERROR");
+	if (prp) {
 		n++;
+		dt_list_append(&dtp->dt_enablings, prp);
+		dtp->dt_error = prp;
+	}
 
 	return n;
 }
diff --git a/libdtrace/dt_work.c b/libdtrace/dt_work.c
index c9142114..8fc474b8 100644
--- a/libdtrace/dt_work.c
+++ b/libdtrace/dt_work.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021, 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.
  */
@@ -149,6 +149,7 @@ dtrace_go(dtrace_hdl_t *dtp, uint_t cflags)
 	if (dtp->dt_active)
 		return dt_set_errno(dtp, EINVAL);
 
+#if 0
 	/*
 	 * If a dtrace:::ERROR program and callback are registered, enable the
 	 * program before we start tracing.  If this fails for a vector open
@@ -160,6 +161,7 @@ dtrace_go(dtrace_hdl_t *dtp, uint_t cflags)
 	    dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 && (
 	    dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL))
 		return -1;		/* dt_errno has been set for us */
+#endif
 
 	/*
 	 * Create the global BPF maps.  This is done only once regardless of
diff --git a/libdtrace/dtrace.h b/libdtrace/dtrace.h
index 4cf7c998..f9650e6e 100644
--- a/libdtrace/dtrace.h
+++ b/libdtrace/dtrace.h
@@ -101,7 +101,8 @@ typedef struct dtrace_proginfo {
 #define	DTRACE_C_DEFARG	0x0800	/* Use 0/"" as value for unspecified args */
 #define	DTRACE_C_NOLIBS	0x1000	/* Do not process D system libraries */
 #define	DTRACE_C_CTL	0x2000	/* Only process control directives */
-#define	DTRACE_C_MASK	0x3bff	/* mask of all valid flags to dtrace_*compile */
+#define	DTRACE_C_EPROBE	0x8000	/* Compiling default ERROR probe clause */
+#define	DTRACE_C_MASK	0xbbff	/* mask of all valid flags to dtrace_*compile */
 
 extern dtrace_prog_t *dtrace_program_strcompile(dtrace_hdl_t *dtp, const char *s,
     dtrace_probespec_t spec, uint_t cflags, int argc, char *const argv[]);
@@ -150,6 +151,7 @@ typedef struct dtrace_stmtdesc {
 #define DT_CLSFLAG_COMMIT	4	/* commit */
 #define DT_CLSFLAG_EXIT		8	/* exit */
 #define DT_CLSFLAG_DESTRUCT	16	/* destructive */
+#define DT_CLSFLAG_ERROR	32	/* may throw an error */
 
 typedef int dtrace_stmt_f(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
     dtrace_stmtdesc_t *sdp, void *data);
diff --git a/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d
new file mode 100644
index 00000000..3f472f19
--- /dev/null
+++ b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.d
@@ -0,0 +1,32 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2021, 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: Test DTRACEFLT_BADADDR error reporting.
+ *
+ * SECTION: dtrace Provider
+ */
+
+#pragma D option quiet
+
+ERROR
+/arg2 == 1 && arg4 == 1 && arg5 == 0/
+{
+	exit(0);
+}
+
+ERROR
+/arg2 != 1 || arg4 != 1 || arg5 != 0/
+{
+	exit(1);
+}
+
+BEGIN
+{
+	trace(((struct task_struct *)NULL)->pid);
+	exit(1);
+}
diff --git a/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r
new file mode 100644
index 00000000..7c10b5b4
--- /dev/null
+++ b/test/unittest/error/tst.DTRACEFLT_BADADDR.null_ptr_field.r
@@ -0,0 +1,3 @@
+
+-- @@stderr --
+dtrace: error on enabled probe ID 4 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.error.d b/test/unittest/error/tst.error.d
index 1d0b87c5..6117a41b 100644
--- a/test/unittest/error/tst.error.d
+++ b/test/unittest/error/tst.error.d
@@ -1,20 +1,16 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021, 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.
  */
-/* @@xfail: dtv2 */
 
 /*
- * ASSERTION:
- *	To fire ERROR probe
+ * ASSERTION: Test ERROR probe firing.
  *
  * SECTION: dtrace Provider
- *
  */
 
-
 #pragma D option quiet
 
 ERROR
@@ -25,5 +21,6 @@ ERROR
 
 BEGIN
 {
-	*(char *)NULL;
+	trace(((struct task_struct *)NULL)->pid);
+	exit(1);
 }
diff --git a/test/unittest/error/tst.error.r b/test/unittest/error/tst.error.r
index 190551ee..b2ceedd6 100644
--- a/test/unittest/error/tst.error.r
+++ b/test/unittest/error/tst.error.r
@@ -1,4 +1,4 @@
 Error fired
 
 -- @@stderr --
-dtrace: error on enabled probe ID 2 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1 at DIF offset 16
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
diff --git a/test/unittest/error/tst.errorend.d b/test/unittest/error/tst.errorend.d
index 07171752..fe6dcaf6 100644
--- a/test/unittest/error/tst.errorend.d
+++ b/test/unittest/error/tst.errorend.d
@@ -1,20 +1,16 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021, 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.
  */
-/* @@xfail: dtv2 */
 
 /*
- * ASSERTION:
- *	Flow of ERROR probe
+ * ASSERTION: Ensure the END probe still fires after an ERROR was reported.
  *
  * SECTION: dtrace Provider
- *
  */
 
-
 #pragma D option quiet
 
 ERROR
@@ -30,5 +26,6 @@ END
 
 BEGIN
 {
-	*(char *)NULL;
+	trace(((struct task_struct *)NULL)->pid);
+	exit(1);
 }
diff --git a/test/unittest/error/tst.errorend.r b/test/unittest/error/tst.errorend.r
index 7a4184e4..ea8f1c36 100644
--- a/test/unittest/error/tst.errorend.r
+++ b/test/unittest/error/tst.errorend.r
@@ -2,4 +2,4 @@ Error fired
 End fired after exit
 
 -- @@stderr --
-dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1 at DIF offset 16
+dtrace: error on enabled probe ID 3 (ID 1: dtrace:::BEGIN): invalid address ({ptr}) in action #1
-- 
2.28.0




More information about the DTrace-devel mailing list