[DTrace-devel] [PATCH v4 01/10] alloca: track alloca()ed allocations
Kris Van Hees
kris.van.hees at oracle.com
Wed Apr 13 19:37:54 UTC 2022
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
... except that there is a XXX in a comment that seems to imply there is
some work left to be done. Has this been done or found not needed, or
should this be a TODO?
See:
/* XXX do we need to do alloca tainting here? try with structs */
On Tue, Apr 12, 2022 at 11:59:00AM +0100, Nick Alcock wrote:
> This introduces a DT_IDFLG_ALLOCA flag which, when set, will (in future
> commits) cause integer variable setting to subtract the address of the
> scratch base from the assignee before assignment, variable fetching to
> add it, and dereferences to bounds-check it. There is a matching parse
> tree node flag DT_NF_ALLOCA which indicates that a parse tree node has
> been influenced by an alloca'ed value. There is also a
> DT_IDFLG_NONALLOCA flag which indicates that an assignment happened that
> did *not* involve an alloca()ed value: this will be used to prevent
> reuse of the same variable for both alloca() and non-alloca() purposes.
> The node flag propagates across most operations (but not dereferences,
> because dereferencing a pointer yields a value, which should not have
> its offset adjusted). One function's identifier has this flag set by
> default: obviously enough, alloca(). (Other functions that always
> return alloca()ed pointers, or pointers into alloca()ed space, should do
> the same.)
>
> Propagating this is fairly amusing, because not only do we have to
> handle propagation of these flags both to and from parse tree nodes and
> identifiers (because you can do things like "foo = alloca(10); bar =
> foo;") and across casts, but we have to handle cases like identifiers
> with alloca'ed vars in their arglists (which will become relevant when
> indexed associative arrays appear), ternaries in which one side is
> allocaed and the other side isn't (which flips on a similarly-propagated
> DT_NF_NONASSIGN flag to stop you using such things in assignments), and
> the same possibility of changes sweeping backwards and forwards between
> the predicate and the clause that already has to be handled for ++,
> which can require the two to be cooked in either order and to be cooked
> repeatedly (yes, you can alloca in a predicate, and you can use
> alloca'ed memory in a predicate).
>
> A few things are newly banned that were allowed before: in particular,
> you can't say bar : -alloca(10). It makes little sense to negate a
> pointer anyway, but this pointer's eventual representation as a
> bounds-checked offset from the scratchmem array makes it even less
> sensible. You also cannot add or subtract pointers where one is
> allocaed and the other is not, nor (as noted above) assign the results
> of a partly-allocaed ternary to a variable.
>
> The parser dn_flags is boosted to a ushort because we have run out of
> flags :(
>
> Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
> ---
> libdtrace/dt_errtags.h | 2 +
> libdtrace/dt_ident.c | 18 +++-
> libdtrace/dt_ident.h | 2 +
> libdtrace/dt_open.c | 4 +-
> libdtrace/dt_parser.c | 216 ++++++++++++++++++++++++++++++++++++++++-
> libdtrace/dt_parser.h | 5 +-
> libdtrace/dt_pcb.h | 1 +
> 7 files changed, 242 insertions(+), 6 deletions(-)
>
> diff --git a/libdtrace/dt_errtags.h b/libdtrace/dt_errtags.h
> index 83257a148d0f..d89dacc0d096 100644
> --- a/libdtrace/dt_errtags.h
> +++ b/libdtrace/dt_errtags.h
> @@ -174,6 +174,8 @@ typedef enum {
> D_USTACK_FRAMES, /* ustack() frames arg bad type */
> D_USTACK_STRSIZE, /* ustack() strsize arg bad type */
> D_USTACK_PROTO, /* ustack() prototype mismatch */
> + D_ALLOCA_SIZE, /* allocation too large */
> + D_ALLOCA_INCOMPAT, /* pointer reused for alloca and non-alloca */
> D_LQUANT_BASETYPE, /* lquantize() bad base type */
> D_LQUANT_BASEVAL, /* lquantize() bad base value */
> D_LQUANT_LIMTYPE, /* lquantize() bad limit type */
> diff --git a/libdtrace/dt_ident.c b/libdtrace/dt_ident.c
> index 098f1fcbaaed..e34f8e8bd8df 100644
> --- a/libdtrace/dt_ident.c
> +++ b/libdtrace/dt_ident.c
> @@ -979,18 +979,34 @@ dt_ident_cook(dt_node_t *dnp, dt_ident_t *idp, dt_node_t **pargp)
> dtrace_attribute_t attr;
> dt_node_t *args, *argp;
> int argc = 0;
> + int alloca_args = 0;
>
> attr = dt_node_list_cook(pargp, DT_IDFLG_REF);
> args = pargp ? *pargp : NULL;
>
> - for (argp = args; argp != NULL; argp = argp->dn_list)
> + for (argp = args; argp != NULL; argp = argp->dn_list) {
> argc++;
> + if (argp->dn_flags & DT_NF_ALLOCA)
> + alloca_args = 1;
> + }
>
> idp->di_ops->di_cook(dnp, idp, argc, args);
>
> if (idp->di_flags & DT_IDFLG_USER)
> dnp->dn_flags |= DT_NF_USERLAND;
>
> + /*
> + * Propagate alloca flags in both directions. No need to propagate it
> + * down into the arglist, but if the alloca flag is on in the arglist it
> + * should also be on in both the identifer and the node. (If this is
> + * a node of the sort we propagate alloca taint into at all.)
> + */
> +
> + if (idp->di_flags & DT_IDFLG_ALLOCA ||
> + dnp->dn_flags & DT_NF_ALLOCA ||
> + alloca_args)
> + dt_cook_taint_alloca(dnp, idp, NULL);
> +
> return dt_attr_min(attr, idp->di_attr);
> }
>
> diff --git a/libdtrace/dt_ident.h b/libdtrace/dt_ident.h
> index 655f1116e525..56dfec0cda4d 100644
> --- a/libdtrace/dt_ident.h
> +++ b/libdtrace/dt_ident.h
> @@ -94,6 +94,8 @@ typedef struct dt_ident {
> #define DT_IDFLG_DECL 0x0400 /* variable is associated with explicit decl */
> #define DT_IDFLG_ORPHAN 0x0800 /* variable is in a dt_node and not dt_idhash */
> #define DT_IDFLG_BPF 0x1000 /* variable is BPF */
> +#define DT_IDFLG_ALLOCA 0x2000 /* variable holds an alloca()ed pointer */
> +#define DT_IDFLG_NONALLOCA 0x4000 /* variable known not to hold an alloca()ed pointer */
>
> #define DT_IDENT_UNDEF UINT_MAX /* id for (as yet) undefined identifiers */
>
> diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
> index 5bdc6037beec..f11d54d8d55a 100644
> --- a/libdtrace/dt_open.c
> +++ b/libdtrace/dt_open.c
> @@ -85,8 +85,8 @@ static const dt_provimpl_t *dt_providers[] = {
> * argument.
> */
> static const dt_ident_t _dtrace_globals[] = {
> -{ "alloca", DT_IDENT_FUNC, 0, DIF_SUBR_ALLOCA, DT_ATTR_STABCMN, DT_VERS_1_0,
> - &dt_idops_func, "void *(size_t)" },
> +{ "alloca", DT_IDENT_FUNC, DT_IDFLG_ALLOCA, DIF_SUBR_ALLOCA, DT_ATTR_STABCMN,
> + DT_VERS_1_0, &dt_idops_func, "void *(size_t)" },
> { "arg0", DT_IDENT_SCALAR, 0, DIF_VAR_ARG0, DT_ATTR_STABCMN, DT_VERS_1_0,
> &dt_idops_type, "int64_t" },
> { "arg1", DT_IDENT_SCALAR, 0, DIF_VAR_ARG1, DT_ATTR_STABCMN, DT_VERS_1_0,
> diff --git a/libdtrace/dt_parser.c b/libdtrace/dt_parser.c
> index df2153375780..b8f37d8d6b17 100644
> --- a/libdtrace/dt_parser.c
> +++ b/libdtrace/dt_parser.c
> @@ -643,6 +643,12 @@ dt_node_type_assign(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type)
> uint_t kind = ctf_type_kind(fp, base);
> ctf_encoding_t e;
>
> + /*
> + * We do not blank out DT_NF_NONASSIGN or DT_NF_ALLOCA because
> + * there is no way for repeated reevaluation of the same node to
> + * cause a previously-alloc'ed or nonassigned node to become
> + * non-allocated or non-nonassigned.
> + */
> dnp->dn_flags &=
> ~(DT_NF_SIGNED | DT_NF_REF | DT_NF_BITFIELD | DT_NF_USERLAND);
>
> @@ -678,6 +684,23 @@ void
> dt_node_type_propagate(const dt_node_t *src, dt_node_t *dst)
> {
> assert(src->dn_flags & DT_NF_COOKED);
> +
> + if ((src->dn_flags & DT_NF_ALLOCA) && !(dst->dn_flags & DT_NF_ALLOCA))
> + yypcb->pcb_alloca_taints++;
> +
> + /*
> + * A previously-nonassignable node may become assignable if allocaness
> + * later propagates to it. Once this happens, it cannot become
> + * nonassignable again (becaue allocaness cannot be turned off once
> + * enabled). This bumps the alloca taint counter, because it is quite
> + * possible that both src and dst have allocaness at this point (in
> + * fact, dst must already have it), but we still want to force
> + * rescanning in order to propagate nonassignment further.
> + */
> + if (!(src->dn_flags & DT_NF_NONASSIGN) &&
> + (dst->dn_flags & DT_NF_NONASSIGN))
> + yypcb->pcb_alloca_taints++;
> +
> dst->dn_flags = src->dn_flags & ~DT_NF_LVALUE;
> dst->dn_ctfp = src->dn_ctfp;
> dst->dn_type = src->dn_type;
> @@ -714,6 +737,29 @@ dt_node_type_size(const dt_node_t *dnp)
> return ctf_type_size(dnp->dn_ctfp, dnp->dn_type);
> }
>
> +void
> +dt_node_prop_alloca(dt_node_t *dst, const dt_node_t *lp, const dt_node_t *rp)
> +{
> + if (dst->dn_flags & DT_NF_ALLOCA)
> + return;
> +
> + if (lp->dn_flags & DT_NF_ALLOCA) {
> + dt_cook_taint_alloca(dst, NULL, NULL);
> + if (lp->dn_flags & DT_NF_NONASSIGN) {
> + dst->dn_flags |= DT_NF_NONASSIGN;
> + yypcb->pcb_alloca_taints++;
> + }
> + }
> +
> + if (rp && rp->dn_flags & DT_NF_ALLOCA) {
> + dt_cook_taint_alloca(dst, NULL, NULL);
> + if (rp->dn_flags & DT_NF_NONASSIGN) {
> + dst->dn_flags |= DT_NF_NONASSIGN;
> + yypcb->pcb_alloca_taints++;
> + }
> + }
> +}
> +
> /*
> * Determine if the specified parse tree node references an identifier of the
> * specified kind, and if so return a pointer to it; otherwise return NULL.
> @@ -1826,6 +1872,10 @@ dt_node_op1(int op, dt_node_t *cp)
> cp->dn_value &= ~0ULL >>
> (64 - dt_node_type_size(cp) * NBBY);
> }
> + if (cp->dn_flags & DT_NF_ALLOCA) {
> + xyerror(D_UNKNOWN,
> + "cannot negate alloca()ed pointers");
> + }
> /*FALLTHRU*/
> case DT_TOK_IPOS:
> return cp;
> @@ -2593,6 +2643,42 @@ dt_node_tstring(dt_node_t *fnp, uintmax_t val)
> return dnp;
> }
>
> +/*
> + * Flip on the alloca flag for a node and/or identifier, if not already set, and
> + * bump the alloca taint counter in the pcb. The SRC, if present, is used as
> + * a propagation source for the nonassignment flag.
> + *
> + * Only a limited variety of identifiers are tainted: roughly, those that can
> + * plausibly store alloca pointers. Parse tree nodes corresponding to
> + * nontainted identifers are not tainted either.
> + */
> +void
> +dt_cook_taint_alloca(dt_node_t *dnp, dt_ident_t *idp, dt_node_t *src)
> +{
> + if (idp && !(idp->di_flags & DT_IDFLG_ALLOCA)) {
> + if (idp->di_kind == DT_IDENT_ARRAY ||
> + idp->di_kind == DT_IDENT_SCALAR ||
> + idp->di_kind == DT_IDENT_AGG ||
> + idp->di_kind == DT_IDENT_XLSOU ||
> + idp->di_kind == DT_IDENT_XLPTR) {
> + idp->di_flags |= DT_IDFLG_ALLOCA;
> + yypcb->pcb_alloca_taints++;
> + } else
> + /*
> + * Not the sort of thing we should taint.
> + */
> + return;
> + }
> +
> + if (dnp && !(dnp->dn_flags & DT_NF_ALLOCA)) {
> + dnp->dn_flags |= DT_NF_ALLOCA;
> + yypcb->pcb_alloca_taints++;
> + if (src && (src->dn_flags & DT_NF_NONASSIGN)) {
> + dnp->dn_flags |= DT_NF_NONASSIGN;
> + }
> + }
> +}
> +
> /*
> * This function provides the underlying implementation of cooking an
> * identifier given its node, a hash of dynamic identifiers, an identifier
> @@ -2698,6 +2784,10 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
> if (idp->di_flags & DT_IDFLG_WRITE)
> dnp->dn_flags |= DT_NF_WRITABLE;
>
> + if ((idp->di_flags & DT_IDFLG_ALLOCA) ||
> + (dnp->dn_flags & DT_NF_ALLOCA))
> + dt_cook_taint_alloca(dnp, idp, NULL);
> +
> dt_node_attr_assign(dnp, attr);
>
> } else if (dhp == dtp->dt_globals && scope != DTRACE_OBJ_EXEC &&
> @@ -2817,6 +2907,16 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
> dnp->dn_ident = idp;
> dnp->dn_flags |= DT_NF_LVALUE | DT_NF_WRITABLE;
>
> + /*
> + * Still a good idea, but not relevant for assignments: see
> + * dt_cook_ident:DT_TOK_ASGN for more. In particular, this is
> + * not a kind of assignment, so we should not turn on NONALLOCA
> + * here.
> + */
> + if ((idp->di_flags & DT_IDFLG_ALLOCA) ||
> + (dnp->dn_flags & DT_NF_ALLOCA))
> + dt_cook_taint_alloca(dnp, idp, NULL);
> +
> dt_node_attr_assign(dnp, attr);
>
> } else if (scope != DTRACE_OBJ_EXEC) {
> @@ -2987,6 +3087,10 @@ dt_cook_op1(dt_node_t *dnp, uint_t idflags)
> dnp->dn_flags |= DT_NF_USERLAND;
> break;
>
> + /* Dereferenced pointers cannot be alloca pointers any more. */
> + dnp->dn_flags &= ~(DT_NF_ALLOCA | DT_NF_NONASSIGN);
> + break;
> +
> case DT_TOK_IPOS:
> case DT_TOK_INEG:
> if (!dt_node_is_arith(cp)) {
> @@ -3046,6 +3150,9 @@ dt_cook_op1(dt_node_t *dnp, uint_t idflags)
>
> if (cp->dn_flags & DT_NF_USERLAND)
> dnp->dn_flags |= DT_NF_USERLAND;
> +
> + if (cp->dn_flags & DT_NF_ALLOCA)
> + dt_cook_taint_alloca(dnp, NULL, cp);
> break;
>
> case DT_TOK_SIZEOF:
> @@ -3071,6 +3178,8 @@ dt_cook_op1(dt_node_t *dnp, uint_t idflags)
> dt_node_type_name(cp, n, sizeof(n)));
> }
> dt_node_type_assign(dnp, DT_STR_CTFP(dtp), DT_STR_TYPE(dtp));
> + if (cp->dn_flags & DT_NF_ALLOCA)
> + dt_cook_taint_alloca(dnp, NULL, cp);
> break;
>
> case DT_TOK_PREINC:
> @@ -3098,6 +3207,9 @@ dt_cook_op1(dt_node_t *dnp, uint_t idflags)
> }
>
> dt_node_type_propagate(cp, dnp); /* see K&R[A7.4.1] */
> +
> + if (cp->dn_flags & DT_NF_ALLOCA)
> + dt_cook_taint_alloca(dnp, NULL, cp);
> break;
>
> default:
> @@ -3186,6 +3298,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
> }
>
> dt_node_promote(lp, rp, dnp); /* see K&R[A7.11-13] */
> + dt_node_prop_alloca(dnp, lp, rp);
> break;
>
> case DT_TOK_LSH:
> @@ -3212,6 +3325,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
> }
>
> dt_node_promote(lp, rp, dnp); /* see K&R[A7.6] */
> + dt_node_prop_alloca(dnp, lp, rp);
> break;
>
> case DT_TOK_MUL:
> @@ -3225,6 +3339,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
> }
>
> dt_node_promote(lp, rp, dnp); /* see K&R[A7.6] */
> + dt_node_prop_alloca(dnp, lp, rp);
> break;
>
> case DT_TOK_LAND:
> @@ -3240,6 +3355,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
>
> dt_node_type_assign(dnp, DT_INT_CTFP(dtp), DT_INT_TYPE(dtp));
> dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr));
> + dt_node_prop_alloca(dnp, lp, rp);
> break;
>
> case DT_TOK_LT:
> @@ -3365,6 +3481,21 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
> dt_node_type_name(lp, n1, sizeof(n1)), opstr(op),
> dt_node_type_name(rp, n2, sizeof(n2)));
>
> + /*
> + * alloca()ed pointers and non-alloca()ed pointers must be to
> + * distinct objects, and the address of the alloca()ed range is
> + * an implementation detail, so pre-emptively block attempts to
> + * add or subtract alloca()ed pointers and non-alloca()ed
> + * pointers to/from each other.
> + */
> + if (lp_is_ptr && rp_is_ptr && ((lp->dn_flags & DT_NF_ALLOCA) !=
> + (rp->dn_flags & DT_NF_ALLOCA)))
> + xyerror(D_OP_INCOMPAT, "adding or subtracting pointers "
> + "to alloca and non-alloca space is meaningless: "
> + "\"%s\" %s \"%s\"\n",
> + dt_node_type_name(lp, n1, sizeof(n1)), opstr(op),
> + dt_node_type_name(rp, n2, sizeof(n2)));
> +
> /*
> * Array bounds-checking. (Non-associative arrays only.)
> */
> @@ -3388,6 +3519,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
>
> dt_node_type_assign(dnp, ctfp, type);
> dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr));
> + dt_node_prop_alloca(dnp, lp, rp);
>
> if (uref)
> dnp->dn_flags |= DT_NF_USERLAND;
> @@ -3634,6 +3766,23 @@ asgn_common:
> "to a writable variable\n", opstr(op));
> }
>
> + if ((lp->dn_kind == DT_NODE_VAR) &&
> + (rp->dn_flags & DT_NF_NONASSIGN)) {
> + xyerror(D_ALLOCA_INCOMPAT, "ternary conditional with "
> + "alloca and non-alloca branches cannot be "
> + "assigned to a variable\n");
> + }
> +
> + dt_ident_t *lp_idp = NULL;
> +
> + if (lp->dn_kind == DT_NODE_VAR)
> + lp_idp = lp->dn_ident;
> +
> + if (rp->dn_flags & DT_NF_ALLOCA)
> + dt_cook_taint_alloca(lp, lp_idp, rp);
> + else if (lp_idp)
> + lp_idp->di_flags |= DT_IDFLG_NONALLOCA;
> +
> dt_node_type_propagate(lp, dnp); /* see K&R[A7.17] */
> dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr));
> break;
> @@ -3778,6 +3927,8 @@ asgn_common:
> if (lp->dn_flags & DT_NF_WRITABLE)
> dnp->dn_flags |= DT_NF_WRITABLE;
>
> + /* XXX do we need to do alloca tainting here? try with structs */
> +
> if (uref && (kind == CTF_K_POINTER ||
> (dnp->dn_flags & DT_NF_REF)))
> dnp->dn_flags |= DT_NF_USERLAND;
> @@ -3871,6 +4022,11 @@ asgn_common:
> dnp->dn_args = rp;
> dnp->dn_list = NULL;
>
> + if (dnp->dn_args->dn_flags & DT_NF_ALLOCA)
> + dt_cook_taint_alloca(dnp, idp, dnp->dn_args);
> + else
> + idp->di_flags |= DT_IDFLG_NONALLOCA;
> +
> dt_node_free(lp);
> return dt_node_cook(dnp, idflags);
> }
> @@ -3890,6 +4046,7 @@ asgn_common:
> }
>
> dnp->dn_ident = dt_xlator_ident(dxp, lp->dn_ctfp, lp->dn_type);
> + dt_node_prop_alloca(dnp, lp, rp);
> dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp));
> dt_node_attr_assign(dnp,
> dt_attr_min(rp->dn_attr, dnp->dn_ident->di_attr));
> @@ -3936,6 +4093,17 @@ asgn_common:
> dt_node_type_name(lp, n2, sizeof(n2)));
> }
>
> + /*
> + * You cannot cast away allocaness. (You also can't cast it
> + * into existence where it was not before, but since there is no
> + * syntactic way to specify allocaness, we don't need to cover
> + * that case. This maintains the invariant that alloca flags
> + * can only ever transition from off to on, preventing the
> + * dt_cook_clause loop from inflooping.)
> + */
> + if (rp->dn_flags & DT_NF_ALLOCA)
> + dt_cook_taint_alloca(lp, NULL, rp);
> +
> dt_node_type_propagate(lp, dnp); /* see K&R[A7.5] */
> dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr));
> break;
> @@ -3957,6 +4125,7 @@ asgn_common:
>
> dt_node_type_propagate(rp, dnp); /* see K&R[A7.18] */
> dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr));
> + dt_node_prop_alloca(dnp, rp, NULL);
> break;
>
> default:
> @@ -3972,6 +4141,7 @@ asgn_common:
> */
> if (dnp->dn_op == DT_TOK_LBRAC && op == DT_TOK_ADD) {
> dt_node_t *pnp;
> + dt_node_t *ret;
>
> if (rp->dn_list != NULL) {
> xyerror(D_ARR_BADREF,
> @@ -3994,7 +4164,12 @@ asgn_common:
> pnp->dn_link = dnp->dn_link;
> dnp->dn_link = pnp;
>
> - return dt_node_cook(pnp, DT_IDFLG_REF);
> + ret = dt_node_cook(pnp, DT_IDFLG_REF);
> +
> + /*
> + * This is a dereference: do not propagate alloca taint.
> + */
> + return ret;
> }
>
> return dnp;
> @@ -4047,10 +4222,27 @@ dt_cook_op3(dt_node_t *dnp, uint_t idflags)
> "used in a conditional context\n");
> }
>
> + /*
> + * An extra condition not expressed in the type system: if one side is
> + * an alloca-derived pointer, and the other side is not, the resulting
> + * value cannot be assigned to a global. This flag is propagated
> + * just like DT_NF_ALLOCA, except that (obviously) it cannot
> + * propagate into an identifier.
> + */
> + if ((lp->dn_flags & DT_NF_ALLOCA) != (rp->dn_flags & DT_NF_ALLOCA))
> + dnp->dn_flags |= DT_NF_NONASSIGN;
> +
> + if ((lp->dn_flags & DT_NF_NONASSIGN) ||
> + (rp->dn_flags & DT_NF_NONASSIGN))
> + dnp->dn_flags |= DT_NF_NONASSIGN;
> +
> dt_node_type_assign(dnp, ctfp, type);
> dt_node_attr_assign(dnp, dt_attr_min(dnp->dn_expr->dn_attr,
> dt_attr_min(lp->dn_attr, rp->dn_attr)));
>
> + if (lp->dn_flags & DT_NF_ALLOCA)
> + dt_cook_taint_alloca(dnp, NULL, NULL);
> +
> return dnp;
> }
>
> @@ -4129,12 +4321,21 @@ dt_cook_aggregation(dt_node_t *dnp, uint_t idflags)
> *
> * but it doesn't seem worth the complexity to handle such rare cases. The
> * user can simply use the D variable declaration syntax to work around them.
> + *
> + * In addition, we count the total number of cases where we needed to set the
> + * relevant alloca flag, or set or reset the nonassign flag, on an identifier or
> + * node, and as long as it keeps rising, we reinvoke. (This will always
> + * terminate, and soon, so there is no danger of inflooping from this
> + * either. Proof: the alloca flag can only be enabled, never disabled, and the
> + * nonassign flag is only ever caused to flip in either direction by an earlier
> + * change to at least one alloca flag).
> */
> static dt_node_t *
> dt_cook_clause(dt_node_t *dnp, uint_t idflags)
> {
> volatile int err, tries;
> jmp_buf ojb;
> + int last_alloca_taints;
>
> /*
> * Before assigning dn_ctxattr, temporarily assign the probe attribute
> @@ -4154,6 +4355,9 @@ dt_cook_clause(dt_node_t *dnp, uint_t idflags)
> longjmp(yypcb->pcb_jmpbuf, err);
> }
>
> +taint_retry:
> + last_alloca_taints = yypcb->pcb_alloca_taints;
> +
> if (tries == 0) {
> yylabel("action list");
>
> @@ -4188,6 +4392,8 @@ dt_cook_clause(dt_node_t *dnp, uint_t idflags)
> yylabel(NULL);
> }
>
> + if (yypcb->pcb_alloca_taints > last_alloca_taints)
> + goto taint_retry;
> return dnp;
> }
>
> @@ -4635,6 +4841,10 @@ dt_node_printr(dt_node_t *dnp, FILE *fp, int depth)
> strcat(n, ",BITF");
> if (dnp->dn_flags & DT_NF_USERLAND)
> strcat(n, ",USER");
> + if (dnp->dn_flags & DT_NF_ALLOCA)
> + strcat(n, ",ALLOCA");
> + if (dnp->dn_flags & DT_NF_NONASSIGN)
> + strcat(n, ",NONASSIGN");
> strcat(buf, n + 1);
> } else
> strcat(buf, "0");
> @@ -4660,7 +4870,9 @@ dt_node_printr(dt_node_t *dnp, FILE *fp, int depth)
> break;
>
> case DT_NODE_VAR:
> - fprintf(fp, "VARIABLE %s%s (%s)\n",
> + fprintf(fp, "VARIABLE %s%s%s (%s)\n",
> + (dnp->dn_ident->di_flags & DT_IDFLG_ALLOCA) ? "(alloca-assigned) " :
> + (dnp->dn_ident->di_flags & DT_IDFLG_NONALLOCA) ? "(normally-assigned) " : "",
> (dnp->dn_ident->di_flags & DT_IDFLG_LOCAL) ? "this->" :
> (dnp->dn_ident->di_flags & DT_IDFLG_TLS) ? "self->" : "",
> dnp->dn_ident->di_name, buf);
> diff --git a/libdtrace/dt_parser.h b/libdtrace/dt_parser.h
> index cd2820ebe43f..c09c25706c2d 100644
> --- a/libdtrace/dt_parser.h
> +++ b/libdtrace/dt_parser.h
> @@ -31,7 +31,7 @@ typedef struct dt_node {
> ctf_file_t *dn_ctfp; /* CTF type container for node's type */
> ctf_id_t dn_type; /* CTF type reference for node's type */
> uchar_t dn_kind; /* node kind (DT_NODE_*, defined below) */
> - uchar_t dn_flags; /* node flags (DT_NF_*, defined below) */
> + ushort_t dn_flags; /* node flags (DT_NF_*, defined below) */
> ushort_t dn_op; /* operator (DT_TOK_*, defined by lex) */
> int dn_line; /* line number for error messages */
> int dn_reg; /* register allocated by cg */
> @@ -158,6 +158,8 @@ typedef struct dt_node {
> #define DT_NF_WRITABLE 0x10 /* node is writable (can be modified) */
> #define DT_NF_BITFIELD 0x20 /* node is an integer bitfield */
> #define DT_NF_USERLAND 0x40 /* data is a userland address */
> +#define DT_NF_ALLOCA 0x80 /* pointer derived from alloca() */
> +#define DT_NF_NONASSIGN 0x100 /* node is not assignable */
>
> #define DT_TYPE_NAMELEN 128 /* reasonable size for ctf_type_name() */
>
> @@ -206,6 +208,7 @@ extern dt_node_t *dt_node_tstring(dt_node_t *, uintmax_t);
>
> extern dt_node_t *dt_node_link(dt_node_t *, dt_node_t *);
> extern dt_node_t *dt_node_cook(dt_node_t *, uint_t);
> +extern void dt_cook_taint_alloca(dt_node_t *, dt_ident_t *, dt_node_t *);
>
> extern dt_node_t *dt_node_xalloc(dtrace_hdl_t *, int);
> extern void dt_node_free(dt_node_t *);
> diff --git a/libdtrace/dt_pcb.h b/libdtrace/dt_pcb.h
> index cc39a0f3d88e..4e3a981c6a48 100644
> --- a/libdtrace/dt_pcb.h
> +++ b/libdtrace/dt_pcb.h
> @@ -73,6 +73,7 @@ typedef struct dt_pcb {
> int pcb_sou_deref; /* lexer in struct/union dereference */
> int pcb_xlator_input; /* in translator input type */
> int pcb_array_dimens; /* in array dimensions */
> + int pcb_alloca_taints; /* number of alloca taint changes */
> } dt_pcb_t;
>
> extern void dt_pcb_push(dtrace_hdl_t *, dt_pcb_t *);
> --
> 2.35.1
More information about the DTrace-devel
mailing list