[PATCH 2/6] libdtrace: do not taint scalar alloca member loads
Alan Maguire <[email protected]> Fri, 5 Jun 2026 23:12:13 +0100
| Newsgroups | dev.linux.lists.dtrace |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> Assisted-by: OpenAI Codex (GPT-5) <[email protected]> --- 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