[DTrace-devel] [oracle/dtrace-utils] b89add: alloca: track alloca()ed allocations

Nick Alcock noreply at github.com
Tue Apr 19 20:23:27 UTC 2022


  Branch: refs/heads/dev
  Home:   https://github.com/oracle/dtrace-utils
  Commit: b89add0ea61e545175f9bd62baf60d196112c7b0
      https://github.com/oracle/dtrace-utils/commit/b89add0ea61e545175f9bd62baf60d196112c7b0
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_errtags.h
    M libdtrace/dt_ident.c
    M libdtrace/dt_ident.h
    M libdtrace/dt_open.c
    M libdtrace/dt_parser.c
    M libdtrace/dt_parser.h
    M libdtrace/dt_pcb.h

  Log Message:
  -----------
  alloca: track alloca()ed allocations

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>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 19ce450a9f4aebd18c77f1173ffe07a1370ae626
      https://github.com/oracle/dtrace-utils/commit/19ce450a9f4aebd18c77f1173ffe07a1370ae626
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_cg.c

  Log Message:
  -----------
  cg: fault improvements

Do not use regset allocation in dt_cg_probe_error on the grounds that
spilling to the stack is pointless when the regs will all be thrown away
in any case, and will trigger BPF verifier errors if some of the regs
are not initialized, as is relatively likely in an error condition.

Also add an argument specifying whether the illegal value is actually
a register whose value should be printed.  (Cases which don't care
can just pass DT_ISIMM, 0).

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 7a4b7834658f86de3fca2ae5bdb44bff1b165118
      https://github.com/oracle/dtrace-utils/commit/7a4b7834658f86de3fca2ae5bdb44bff1b165118
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M include/dtrace/faults_defines.h
    M libdtrace/dt_error.c
    M libdtrace/dt_handle.c

  Log Message:
  -----------
  alloca: new bad-size fault

We'll use DTRACEFLT_BADSIZE to distinguish in error messages between
out-of-bounds errors in bcopy and the like, and size-too-large errors.
(Without this, it's hard to tell if a, say, 0x12 in the fault value
is a nearly-NULL pointer or a wrong size.)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 8d43f03c3ab1fb0f4afa9a6071b619ec70051df6
      https://github.com/oracle/dtrace-utils/commit/8d43f03c3ab1fb0f4afa9a6071b619ec70051df6
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M include/dtrace/options_defines.h
    M libdtrace/dt_bpf.c
    M libdtrace/dt_cg.c
    M libdtrace/dt_dctx.h
    M libdtrace/dt_dlibs.c
    M libdtrace/dt_open.c
    M libdtrace/dt_options.c

  Log Message:
  -----------
  alloca: add alloca() itself

We add a new scratchsize option (default value an arbitrary 256), and a
new scratchmem dctx pointer.  We allocate twice as much space as we
asked for (only half of which is usable), because the verifier can only
use the values of variables to adjust its bounds in very limited
conditions: so we leave enough space to write the full size of scratch
space to the last byte of scratch space without overflow.  (Runtime
checks will prohibit such things, but the verifier doesn't know about
those.)

The top of currently-allocated scratch space is indicated via a single
new 8-byte-aligned value in the machine state, DMST_SCRATCH_TOP.  It is
reset to 0 before each clause is invoked.

alloca itself is fairly pedestrian given all that.  The size check is
fairly trivial: the only fiddly bit is the alignment (thanks to Kris Van
Hees for that).

Overly large allocations are unconditionally refused via a compile-time
error, before even generating any code, preventing the verifier from
concluding that too-large allocations can't succeed and therefore
the success branch in the alloca size check is unreachable code.

alloca'ed pointers are not yet terribly usable at this stage: you can't
dereference them because they are not map_values, just arbitrary
scalars.  Thus there are no tests yet either.

(One new test fails temporarily:
test/unittest/codegen/tst.stack_layout.sh.  It needs adjusting for the
new dmst variables, but we still have more to add in later commits.)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 245d035faeb0433f12a532f711190868b9c3980d
      https://github.com/oracle/dtrace-utils/commit/245d035faeb0433f12a532f711190868b9c3980d
  Author: Kris Van Hees <kris.van.hees at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_cg.c
    M libdtrace/dt_parser.c
    M test/unittest/dif/alloca.d
    A test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.ternary.d
    A test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.ternary.r
    A test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.var-clash-non-first.d
    A test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.var-clash-non-first.r
    A test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.var-clash.d
    A test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.var-clash.r
    A test/unittest/funcs/alloca/err.alloca-crossing-clauses.d
    A test/unittest/funcs/alloca/err.alloca-crossing-clauses.r
    A test/unittest/funcs/alloca/err.alloca-load-before-bottom.d
    A test/unittest/funcs/alloca/err.alloca-load-before-bottom.r
    A test/unittest/funcs/alloca/err.alloca-load-beyond-top.d
    A test/unittest/funcs/alloca/err.alloca-load-beyond-top.r
    A test/unittest/funcs/alloca/err.alloca-load-crossing-bottom.d
    A test/unittest/funcs/alloca/err.alloca-load-crossing-bottom.r
    A test/unittest/funcs/alloca/err.alloca-null-deref-lvalue.d
    A test/unittest/funcs/alloca/err.alloca-null-deref-lvalue.r
    A test/unittest/funcs/alloca/err.alloca-null-deref.d
    A test/unittest/funcs/alloca/err.alloca-null-deref.r
    A test/unittest/funcs/alloca/err.alloca-store-before-bottom.d
    A test/unittest/funcs/alloca/err.alloca-store-before-bottom.r
    A test/unittest/funcs/alloca/err.alloca-store-beyond-top.d
    A test/unittest/funcs/alloca/err.alloca-store-beyond-top.r
    A test/unittest/funcs/alloca/err.alloca-store-crossing-bottom.d
    A test/unittest/funcs/alloca/err.alloca-store-crossing-bottom.r
    A test/unittest/funcs/alloca/tst.alloca-alignment.d
    A test/unittest/funcs/alloca/tst.alloca-crossing-clauses.d
    A test/unittest/funcs/alloca/tst.alloca-overtainting.sh
    A test/unittest/funcs/alloca/tst.alloca-store-load-aliasing-arith-bottom.d
    A test/unittest/funcs/alloca/tst.alloca-store-load-aliasing-arith-bottom.r
    A test/unittest/funcs/alloca/tst.alloca-store-load-aliasing-arith.d
    A test/unittest/funcs/alloca/tst.alloca-store-load-bottom.d
    A test/unittest/funcs/alloca/tst.alloca-store-load-idx-1.d
    A test/unittest/funcs/alloca/tst.alloca-store-load-top.d
    A test/unittest/funcs/alloca/tst.alloca0-after-alloca-load.d
    A test/unittest/funcs/alloca/tst.alloca0-after-alloca.d
    A test/unittest/funcs/alloca/tst.alloca0-load.d
    A test/unittest/funcs/alloca/tst.alloca0-values.sh
    A test/unittest/funcs/alloca/tst.alloca0.d
    A test/unittest/funcs/alloca/tst.ternary.d
    A test/unittest/funcs/err.D_ALLOCA_SIZE.big_alloca.d
    A test/unittest/funcs/err.D_ALLOCA_SIZE.big_alloca.r
    A test/unittest/funcs/err.badalloca.r
    A test/unittest/funcs/err.badalloca.r.p
    M test/unittest/funcs/err.badalloca2.d
    M test/unittest/funcs/err.badalloca2.r
    A test/unittest/pointers/err.AllocaOverrun.d
    A test/unittest/pointers/err.AllocaOverrun.r
    M test/unittest/pointers/tst.ValidPointer1.d
    R test/unittest/pointers/tst.ValidPointer2.d
    A test/unittest/predicates/err.D_ALLOCA_INCOMPAT.alloca-postinc-instantiation.d
    A test/unittest/predicates/err.D_ALLOCA_INCOMPAT.alloca-postinc-instantiation.r
    A test/unittest/predicates/tst.alloca-postinc-instantiation.d

  Log Message:
  -----------
  alloca: deref

Dereferencing an alloca() pointer (identified via its parser taint) is a
matter of validating that it is suitably bounded and not null, then
converting it into an actual map_value pointer by adding the scratchmem
base to it.

But this is made a bit more complex because bounds validation involves
not only validation of the address but the access size, and there we
have two distinct sorts of derefs to deal with: non-lvalue derefs, like
*foo, for which the load is done at the time of dereferencing and the
size is known, and lvalue loads, like foo[10]=bar, where the size stored
is not known until after the deref is complete.

For non-lvalue derefs this is quite easy: adjust dt_cg_load so it tells
us the size of load it's generating an op for, and add a bounds check to
the DT_TOK_DEREF case in dt_cg_node.  For lvalue derefs this is
trickier: at DT_TOK_DEREF codegen time we have no idea what size of
store will be carried out, and in fact dt_cg_node generates no code at
all to deref an lvalue store.  The deref is carried out in
dt_cg_store_val, which has to grow a special case for a store to a
writable lvalue node whose child is a deref with the DT_NF_ALLOCA flag
turned on.  (This combination is generated by the compiler for lvalue
stores, so is completely stereotyped and safe to rely on).  When this
case fires we can do a bounds-check just like the one done for reads.

We can also add a lot of tests and flip off XFAIL for a few pre-existing
ones, now that alloca and dereferencing of allocations both work.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 82bf9e81f4d7a2ec5d077d5c7ab2f23676bb02cc
      https://github.com/oracle/dtrace-utils/commit/82bf9e81f4d7a2ec5d077d5c7ab2f23676bb02cc
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_cg.c
    A test/unittest/funcs/alloca/err.alloca-bcopy-before-beyond.d
    A test/unittest/funcs/alloca/err.alloca-bcopy-before-beyond.r
    A test/unittest/funcs/alloca/err.alloca-bcopy-before-bottom.d
    A test/unittest/funcs/alloca/err.alloca-bcopy-before-bottom.r
    A test/unittest/funcs/alloca/err.alloca-bcopy-beyond-top.d
    A test/unittest/funcs/alloca/err.alloca-bcopy-beyond-top.r
    A test/unittest/funcs/alloca/err.alloca-bcopy-crossing-bottom.d
    A test/unittest/funcs/alloca/err.alloca-bcopy-crossing-bottom.r
    A test/unittest/funcs/alloca/err.alloca-bcopy-crossing-top.d
    A test/unittest/funcs/alloca/err.alloca-bcopy-crossing-top.r
    A test/unittest/funcs/alloca/err.alloca-scratch-exceeding-bcopy.d
    A test/unittest/funcs/alloca/err.alloca-scratch-exceeding-bcopy.r
    A test/unittest/funcs/alloca/tst.alloca-bcopy-top.d
    A test/unittest/funcs/alloca/tst.alloca-bcopy-top.r
    A test/unittest/funcs/alloca/tst.alloca-scratch-filling-bcopy.d
    A test/unittest/funcs/err.badbcopy.r
    A test/unittest/funcs/err.badbcopy1.r
    A test/unittest/funcs/err.badbcopy2.r
    A test/unittest/funcs/err.badbcopy3.r
    M test/unittest/funcs/err.badbcopy4.d
    M test/unittest/funcs/err.badbcopy4.r
    M test/unittest/funcs/err.badbcopy5.d
    M test/unittest/funcs/err.badbcopy5.r
    M test/unittest/funcs/err.badbcopy6.d
    M test/unittest/funcs/err.badbcopy6.r
    A test/unittest/funcs/err.badbcopy7.d
    A test/unittest/funcs/err.badbcopy7.r
    A test/unittest/funcs/err.badbcopy8.d
    A test/unittest/funcs/err.badbcopy8.r
    M test/unittest/funcs/tst.bcopy.d

  Log Message:
  -----------
  alloca: bcopy

This is fairly simple given the machinery already present: we just need
to make sure the arguments are suitably alloca-tainted or non-tainted,
then call dt_cg_alloca_access_check on the alloca pointer to bounds
check it, and dt_cg_alloca_ptr to turn it into a real map_value pointer.

Since we validate the destination address completely ourselves, if the
underlying probe_read call fails we can even tell that it's a problem
with the source pointer, and report the failing address appropriately.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 843ce03e5100b8809327a90db62fa9f9d2ec9e0c
      https://github.com/oracle/dtrace-utils/commit/843ce03e5100b8809327a90db62fa9f9d2ec9e0c
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_cg.c
    M libdtrace/dt_dctx.h

  Log Message:
  -----------
  cg: support casts of pointers to integers

When we cast a pointer to an integer, we want to scalarize it,
i.e. ensure that it is no longer a map_value, so that the user can
compare it freely to other integers.

Add a new DMST_SCALARIZER field to the machine state that anyone
can stuff map_values into and pull them right back out of to
scalarize them.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: cf3a3f4525cd9a6e7ea8aa12bf065d5d08b0d935
      https://github.com/oracle/dtrace-utils/commit/cf3a3f4525cd9a6e7ea8aa12bf065d5d08b0d935
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_cg.c

  Log Message:
  -----------
  cg: support null pointers in ternary conditionals

This lets you compare ternaries with NULL without risking a dereference.

(Some of the alloca tests can sometimes do this.)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 0599f865f3cfdd1ea31df4151c226d723dd1c44c
      https://github.com/oracle/dtrace-utils/commit/0599f865f3cfdd1ea31df4151c226d723dd1c44c
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_cg.c

  Log Message:
  -----------
  cg: move dt_regset_xalloc for consistency

Pack it next to BPF_CALL_FUNC where possible.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Suggested-by: Eugene Loh <eugene.loh at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


  Commit: 71442deb33a5b8e8645610c3c81d379e065c95b0
      https://github.com/oracle/dtrace-utils/commit/71442deb33a5b8e8645610c3c81d379e065c95b0
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-04-19 (Tue, 19 Apr 2022)

  Changed paths:
    M libdtrace/dt_cg.c
    A test/unittest/funcs/alloca/tst.alloca-funcs.d
    A test/unittest/funcs/alloca/tst.alloca-funcs.r
    A test/unittest/funcs/alloca/tst.string-alloca.d
    A test/unittest/funcs/alloca/tst.string-alloca.r

  Log Message:
  -----------
  alloca: allow passing alloca pointers to actions and subrs

A great many subrs and a good few actions allow the passing-in of
pointers (mostly, but not entirely, char *) which may be in alloca()ed
space.  Since they then usually go on to dereference these pointers,
the pointers must be bounds-checked.  We already handled actions
earlier (they don't do formal dereferences except insofar as it
is already done via the deref operator, so we only needed to check
them for nullity); but subrs remain unhandled.

For non-alloca pointers we can just keep on doing null checking; for
alloca pointers, we need to do the usual access_check/ptr dance, with a
length derived from the parser's idea of the length (which is passed
through identifiers accurately).

In addition to subrs, we also need to handle alloca'ed pointers in
codegen for stringof(), since the arg to stringof() might be in
alloca()ed space too.

A new test that tests more or less all of this is added: it has no
expected results because all we actually care about is that there are no
verifier failures.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>


Compare: https://github.com/oracle/dtrace-utils/compare/9489c3b106fd...71442deb33a5



More information about the DTrace-devel mailing list