Skip to content

Commit

Permalink
alloca: track alloca()ed allocations
Browse files Browse the repository at this point in the history
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@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
  • Loading branch information
nickalcock committed Apr 19, 2022
1 parent 9489c3b commit b89add0
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 10 deletions.
4 changes: 3 additions & 1 deletion libdtrace/dt_errtags.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2022, 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.
*/
Expand Down Expand Up @@ -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 */
Expand Down
18 changes: 17 additions & 1 deletion libdtrace/dt_ident.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
4 changes: 3 additions & 1 deletion libdtrace/dt_ident.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2022, 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.
*/
Expand Down Expand Up @@ -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 */

Expand Down
4 changes: 2 additions & 2 deletions libdtrace/dt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit b89add0

Please sign in to comment.