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

Nick Alcock noreply at github.com
Tue Apr 19 20:46:48 UTC 2022


  Branch: refs/heads/dev
  Home:   https://github.com/oracle/dtrace-utils
  Commit: a37f2d018f9b7b8b36b63f53746822dd6023e6cf
      https://github.com/oracle/dtrace-utils/commit/a37f2d018f9b7b8b36b63f53746822dd6023e6cf
  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 dereferences to bounds-check alloca scratch-space offsets
against the scratch top and boost the offsets into actual pointers into
scratch memory.  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: 137225d200a2b4225cb1e5c3daaaea72844f7888
      https://github.com/oracle/dtrace-utils/commit/137225d200a2b4225cb1e5c3daaaea72844f7888
  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: 4414e8c16e7aa49fc11a20354f261bf31464a10e
      https://github.com/oracle/dtrace-utils/commit/4414e8c16e7aa49fc11a20354f261bf31464a10e
  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: b4c6e441f4e2bee6ab08853629f80a596762cec6
      https://github.com/oracle/dtrace-utils/commit/b4c6e441f4e2bee6ab08853629f80a596762cec6
  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: 020c85fb67f6ef97787b3e5026d6adebc4d64f9f
      https://github.com/oracle/dtrace-utils/commit/020c85fb67f6ef97787b3e5026d6adebc4d64f9f
  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: e05eb1d7a33f061ffad881cfd129226aa5918d9d
      https://github.com/oracle/dtrace-utils/commit/e05eb1d7a33f061ffad881cfd129226aa5918d9d
  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: 227d469fc547a38258dc86a9245cbfb114bb118c
      https://github.com/oracle/dtrace-utils/commit/227d469fc547a38258dc86a9245cbfb114bb118c
  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: 543e8225758cfbd622dc1ec135da7d072a3f3fbc
      https://github.com/oracle/dtrace-utils/commit/543e8225758cfbd622dc1ec135da7d072a3f3fbc
  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: 91c20623a3d9019bb94373dd7ca1cde4e2883af6
      https://github.com/oracle/dtrace-utils/commit/91c20623a3d9019bb94373dd7ca1cde4e2883af6
  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: a24181dc606c4a946c8f4f383db21eec209b5088
      https://github.com/oracle/dtrace-utils/commit/a24181dc606c4a946c8f4f383db21eec209b5088
  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/71442deb33a5...a24181dc606c



More information about the DTrace-devel mailing list