[DTrace-devel] [PATCH 2/6] libdtrace: do not taint scalar alloca member loads

Alan Maguire alan.maguire at oracle.com
Fri Jun 5 22:12:13 UTC 2026


Member access on an alloca-backed struct propagated DT_NF_ALLOCA from the
base expression unconditionally.  This is correct when the result still
denotes  storage inside the alloca buffer, but wrong for scalar member
loads: after the load, the result is an ordinary scalar value.

This caused values such as copyin(...)->myint to be treated later as
scratch offsets, producing bogus pointer-like values and bad reads.

Only propagate alloca taint for member expressions that remain
by reference.  Preserve address-taking of alloca-backed members,
and update store codegen so writes to scalar memebers inside alloca
storage still bounds-check and convert the alloca offset correctly.

Add a regression test for assigning a scalar member loaded from
alloca-backed storage to a normal scalar variable.

Signed-off-by: Alan Maguire <alan.maguire at oracle.com>
Assisted-by: OpenAI Codex (GPT-5) <codex at openai.com>
---
 libdtrace/dt_cg.c                             |  9 ++---
 libdtrace/dt_parser.c                         | 15 ++++++--
 .../tst.alloca-struct-scalar-load-taint.d     | 35 +++++++++++++++++++
 3 files changed, 52 insertions(+), 7 deletions(-)
 create mode 100644 test/unittest/funcs/alloca/tst.alloca-struct-scalar-load-taint.d

diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 0859e9cb..e97e9abc 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -3788,13 +3788,15 @@ dt_cg_store(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst)
 
 	/*
 	 * If we're storing into a writable lvalue that is a dereference of an
-	 * alloca pointer or if we are storing into a writable alloca lvalue,
-	 * we need to do bounds-checking and turn the offset value into a real
-	 * pointer.
+	 * alloca pointer, a member of an alloca pointer, or if we are storing
+	 * into a writable alloca lvalue, we need to do bounds-checking and
+	 * turn the offset value into a real pointer.
 	 */
 	if (dst->dn_flags & DT_NF_WRITABLE && dst->dn_flags & DT_NF_LVALUE &&
 	    ((dst->dn_op == DT_TOK_DEREF &&
 	      dst->dn_child->dn_flags & DT_NF_ALLOCA) ||
+	     ((dst->dn_op == DT_TOK_DOT || dst->dn_op == DT_TOK_PTR) &&
+	      dst->dn_left->dn_flags & DT_NF_ALLOCA) ||
 	     dst->dn_flags & DT_NF_ALLOCA)) {
 		assert(!(dst->dn_flags & DT_NF_BITFIELD));
 
@@ -7496,7 +7498,6 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 			 * pointer, bounds-check the pointer reference.
 			 */
 			if (dnp->dn_left->dn_flags & DT_NF_ALLOCA) {
-				assert(dnp->dn_flags & DT_NF_ALLOCA);
 				dt_cg_alloca_access_check(dlp, drp, dnp->dn_reg,
 							  DT_ISIMM, size);
 				dt_cg_alloca_ptr(dlp, drp, dnp->dn_reg,
diff --git a/libdtrace/dt_parser.c b/libdtrace/dt_parser.c
index e71c0985..3e1351d2 100644
--- a/libdtrace/dt_parser.c
+++ b/libdtrace/dt_parser.c
@@ -3179,7 +3179,6 @@ dt_cook_op1(dt_node_t *dnp, uint_t idflags)
 		if ((cp->dn_flags & DT_NF_USERLAND) &&
 		    (kind == CTF_K_POINTER || (dnp->dn_flags & DT_NF_REF)))
 			dnp->dn_flags |= DT_NF_USERLAND;
-		break;
 
 		/* Dereferenced pointers cannot be alloca pointers any more.  */
 		dnp->dn_flags &= ~(DT_NF_ALLOCA | DT_NF_NONASSIGN);
@@ -3251,6 +3250,11 @@ dt_cook_op1(dt_node_t *dnp, uint_t idflags)
 			 cp->dn_op == DT_TOK_DEREF &&
 			 (cp->dn_child->dn_flags & DT_NF_ALLOCA))
 			dt_cook_taint_alloca(dnp, NULL, cp->dn_child);
+		else if (cp->dn_kind == DT_NODE_OP2 &&
+			 (cp->dn_op == DT_TOK_DOT ||
+			  cp->dn_op == DT_TOK_PTR) &&
+			 (cp->dn_left->dn_flags & DT_NF_ALLOCA))
+			dt_cook_taint_alloca(dnp, NULL, cp->dn_left);
 		break;
 
 	case DT_TOK_SIZEOF:
@@ -4049,8 +4053,13 @@ asgn_common:
 		if (lp->dn_flags & DT_NF_WRITABLE)
 			dnp->dn_flags |= DT_NF_WRITABLE;
 
-		/* Transfer alloca taint. */
-		if (lp->dn_flags & DT_NF_ALLOCA)
+		/*
+		 * Only propagate alloca taint when the member access still
+		 * denotes storage inside the alloca buffer.  Scalar members
+		 * are loaded from the alloca buffer and yield ordinary values.
+		 */
+		if ((lp->dn_flags & DT_NF_ALLOCA) &&
+		    (dnp->dn_flags & DT_NF_REF))
 			dt_cook_taint_alloca(dnp, NULL, lp);
 
 		if (xflags && (kind == CTF_K_POINTER ||
diff --git a/test/unittest/funcs/alloca/tst.alloca-struct-scalar-load-taint.d b/test/unittest/funcs/alloca/tst.alloca-struct-scalar-load-taint.d
new file mode 100644
index 00000000..8aa071be
--- /dev/null
+++ b/test/unittest/funcs/alloca/tst.alloca-struct-scalar-load-taint.d
@@ -0,0 +1,35 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2026, 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: Loading a scalar struct member from alloca()ed storage does not
+ *	      taint the scalar value as an alloca() pointer.
+ *
+ * SECTION: Actions and Subroutines/alloca()
+ */
+
+#pragma D option quiet
+
+struct foo {
+	int	c;
+} *ptr;
+
+BEGIN
+{
+	ptr = alloca(sizeof(struct foo));
+	ptr->c = 12;
+
+	depth = ptr->c;
+	depth = 13;
+
+	exit(depth == 13 ? 0 : 1);
+}
+
+ERROR
+{
+	exit(1);
+}
-- 
2.43.5




More information about the DTrace-devel mailing list