[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