[DTrace-devel] [oracle/dtrace-utils] 22998a: parser: support slices of typedefs, etc

Nick Alcock noreply at github.com
Wed Mar 2 13:44:12 UTC 2022


  Branch: refs/heads/nix/review/alloca
  Home:   https://github.com/oracle/dtrace-utils
  Commit: 22998a9a29c5a24fb601b73749b617d025065177
      https://github.com/oracle/dtrace-utils/commit/22998a9a29c5a24fb601b73749b617d025065177
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_parser.c

  Log Message:
  -----------
  parser: support slices of typedefs, etc

Slices in CTF are almost totally transparent to the user, *except* that
ctf_type_resolve will halt when it encounters a slice and will drill no
further, since going further would lose information about the encoding
of the slice.  Since you can have slices of typedefs, this means you
can't rely on ctf_type_resolve getting rid of all typedefs any more.

This change drills through to base types where necessary -- but I
suspect that what we actually want is a ctf_type_resolve_me_harder that
drills through slices too... but I have no idea what to call it
(ctf_type_resolve_base?), and user code will still need adjustment
because you cannot safely call ctf_type_encoding on a type returned from
such a function (the slice, if any, has been sheared away).

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 2bd9c2c4ffc87e08211f8fd3d6b0206d90c060ea
      https://github.com/oracle/dtrace-utils/commit/2bd9c2c4ffc87e08211f8fd3d6b0206d90c060ea
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/procfs.d.in

  Log Message:
  -----------
  translators: procfs.d: rename projid_t

The Linux kernel already has a type named projid_t, with a definition
that conflicts with that in use (for an unused field) by this
translator.  libctf correctly diagnoses this as a conflict (where
libdtrace-ctf didn't): rename the type in the translator.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 1f065d85f0c0815130abbba5d245fedc54e04030
      https://github.com/oracle/dtrace-utils/commit/1f065d85f0c0815130abbba5d245fedc54e04030
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M runtest.sh
    M test/unittest/providers/tst.dtrace_cleanup.sh

  Log Message:
  -----------
  runtest: pass timeouts down to tests: use it in one test

A few tests implement their own timeouts to let them see whether dtrace
has started up properly.  This is less than ideal when valgrind is in
use, since that routinely causes startup to slow down by an order of
magnitude.  The runtest script adapts the script-wide timeout, but
this is not passed down to tests so they cannot do the same even if
they wanted to.

So pass down $timeout, and adjust one test that spuriously fails.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 21bb81cf2e2c4d54d351f09f1e040a7d11fe38a1
      https://github.com/oracle/dtrace-utils/commit/21bb81cf2e2c4d54d351f09f1e040a7d11fe38a1
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_module.c

  Log Message:
  -----------
  modules: fix can-never-fail comparison

Spotted by GCC 11.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 1840e62042cbeb74f31d42dfac76b7402fe576b1
      https://github.com/oracle/dtrace-utils/commit/1840e62042cbeb74f31d42dfac76b7402fe576b1
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_bpf.c

  Log Message:
  -----------
  speculations, bpf: check for nonexistent CTF before using it

This turns coredumps into a not-all-that-much-more-helpful "no such file
or directory" error (but turning DT_DEBUG on at least tells you which
file is missing, i.e. some ctfa file)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 8a7cba9f250211128654d5719d41df5c6d54e017
      https://github.com/oracle/dtrace-utils/commit/8a7cba9f250211128654d5719d41df5c6d54e017
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dtrace.h

  Log Message:
  -----------
  drop obsolete GCC bug workaround

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 60082bf99231274d126f4b8923116d60560848d1
      https://github.com/oracle/dtrace-utils/commit/60082bf99231274d126f4b8923116d60560848d1
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 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>


  Commit: bee8e535c195773c2075ea4133d59118f713fe25
      https://github.com/oracle/dtrace-utils/commit/bee8e535c195773c2075ea4133d59118f713fe25
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M Makeoptions
    M bpf/Build
    A bpf/trace_ptr.c
    M libdtrace/dt_cg.c
    M libdtrace/dt_dlibs.c

  Log Message:
  -----------
  cg: add dt_cg_trace

A call to

   dt_cg_trace(dlp, drp, 1, 666, foo);

will trace the value of reg FOO into
/sys/kernel/debug/tracing/trace_pipe when dtrace is compiled with
debugging=yes.  '666' above is a counter value which is also logged in
the resulting trace output, to distinguish trace calls from each other.

A call to

  dt_cg_trace(dlp, drp, 0, 666, foo);

will trace the immediate value of FOO at codegen time.

Under debugging=no this call has no effect and is compiled out.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: e9074ef491f5b263c326d9e77d8b08fbaa3e74b9
      https://github.com/oracle/dtrace-utils/commit/e9074ef491f5b263c326d9e77d8b08fbaa3e74b9
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_cg.c

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

Add a label to dt_cg_probe_error, which is dropped on the first
instruction if set; eschew regset allocation on the grounds that
spilling to the stack is pointless when the regs will all be thrown away
in any case, and can trigger errors if some of the regs are not properly
initialized, as is relatively likely in an error condition.

Also add a new dt_cg_probe_error_regval, which is like dt_cg_probe_error
except the last argument is not an illegal value but a register whose
value should be emitted.  This is a bit trickier: we must move the
illegal register arg into place first because we don't use the regset
allocation code, so it might be using one of the regs we're about to
overwrite; and don't emit a move at all if we're by chance already using
that reg (the verifier doesn't like mov %r4, %r4 very much).

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 90333019759f1d7672985b460a9a263c91352f72
      https://github.com/oracle/dtrace-utils/commit/90333019759f1d7672985b460a9a263c91352f72
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_cg.c

  Log Message:
  -----------
  memcpy: bounds-check

This FIXME is pretty easy to implement.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: a6518096449696220f20edef46c5c994cdc1a229
      https://github.com/oracle/dtrace-utils/commit/a6518096449696220f20edef46c5c994cdc1a229
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

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

  Log Message:
  -----------
  alloca: new faults

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.)

We'll use DTRACEFLT_INTERR to indicate can-never-happen errors where we
have tested the actual condition already (and emitted a proper error)
followed by a check to placate the verifier (which will always succeed
if the actual condition check failed).  The latter failure happening at
runtime always indicates a bug in DTrace, not in the user's code, so
indicate as much.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 7a29264ca3fc5277f7d6c137f23848400b0d641f
      https://github.com/oracle/dtrace-utils/commit/7a29264ca3fc5277f7d6c137f23848400b0d641f
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 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 256, just like in days of
old), and a new scratchmem dctx pointer.  This is allocated twice as
much space as we asked for, 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 with two
values in the machine state: DMST_SCRATCH_TOP, the current top of
scratch space, used for bounds-checking lookups, and DMST_SCRATCH_NEXT,
which is an 8-byte-aligned DMST_SCRATCH_TOP, and is where the next
allocation comes from.  (Thus we ensure that all allocations are aligned
adequately for all D basic types, without losing bounds checking of the
most recently alloca()ed value.)

Both values are 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
use them directly but you can't assign them to variables, because
on retrieval the verifier will think they're just arbitrary scalars and
prohibit accesses through them.  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>


  Commit: 855e4bd8c153f602dc7d46c0103d33af14339176
      https://github.com/oracle/dtrace-utils/commit/855e4bd8c153f602dc7d46c0103d33af14339176
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_cg.c

  Log Message:
  -----------
  cg: comment typo fix

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: ae7d449439f546b321e6b039255aee0311332b70
      https://github.com/oracle/dtrace-utils/commit/ae7d449439f546b321e6b039255aee0311332b70
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_cg.c

  Log Message:
  -----------
  alloca: load and store

With alloca() and the parser taint code in place, we can handle storing
and loading alloca()ed values from variables.  We detect these easily
enough: stores from nodes with DT_NF_ALLOCA turned on, to identifiers
with DT_IDFLG_ALLOCA turned on are derived from alloca() and need
reduction to scalar offsets via subtraction of the DCTX_SCRATCHMEM
value; loads from identifiers with DT_IDFLG_ALLOCA turned on need
addition again.

The store case is fairly easy (a straight subtraction, hanging on to the
un-subtracted value for use as the return value of the allocation
itself), with one wrinkle: we detect stores from nodes with
DT_NF_NONALLOCA turned on, and complain if both DT_NF_ALLOCA and
DT_NF_NONALLOCA are simultaneously active, because this indicates that
someone is reusing a variable used to store alloca()ed pointers for
non-alloca purposes too.  We can't have that, since we are modifying the
variable at both load and store time!

The load case is trickier, and is pushed into a new dt_cg_load_alloca
function to keep it out of the way.  Converting the offset back into a
map_value pointer requires quite a few contortions: we have to
bounds-check everything because most of the values we need start out as
scalars, but we're trying to add them to a map_value... but the verifier
only uses conditionals to adjust its idea of bounds if the conditional
is == or != (not useful for us), or if one side of the conditional is a
constant.  So we have to come up with constant upper bounds for things
to satisfy the verifier, before checking the *real* bounds in
conditionals the verifier will always accept.

In particular, the scratchlen turns up as an unbounded scalar: we bound
it by ANDing it with one less than the next highest power of two above
the scratchlen option's value (which is guaranteed not to affect the
actual value, which must be less than that, but bounds the scratchlen
for the verifier).

The actual bounds checking is shuffled off into a new function,
dt_cg_check_bounds: among other things this is passed the reg to
bounds-check, the actual scratchlen to check against, and a constant
maximum scratch upper bound for the verifier (twice the actual
scratchlen, so that when we are chcking real accesses with nonzero
lengths, rather than simple pointers, we don't need to worry about
accesses right at the end of the allocated space: we can just let the
verifier assume that all accesses might be max-length, and they will
still fit).  The reg comes back suitably bounded to do arithmetic on it
(to convert it back from an offset to a pointer), and suitably
bounds-checked to dereference.

dt_cg_check_bounds is significantly more flexible than we need here:
it will check pointers, not just offsets (subtracting them from a base
register, i.e. the scratch base), and it will check ranges, not just a
simple pointer, so we can use it to make sure that accesses to scratch
space are in range.

There is no support for TLS storage yet, nor even diagnosis of attempts
to do so.  I don't know what it would involve.  Help solicited: it's
probably either next to impossible or dead easy :)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 6fdf32c917fc6149419fd73ee519af3047afd385
      https://github.com/oracle/dtrace-utils/commit/6fdf32c917fc6149419fd73ee519af3047afd385
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_cg.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-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-load-crossing-top.d
    A test/unittest/funcs/alloca/err.alloca-load-crossing-top.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/err.alloca-store-crossing-top.d
    A test/unittest/funcs/alloca/err.alloca-store-crossing-top.r
    A test/unittest/funcs/alloca/err.alloca-store-load-aliasing-arith-bottom.d
    A test/unittest/funcs/alloca/err.alloca-store-load-aliasing-arith-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.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
    M test/unittest/funcs/err.badalloca2.d
    M test/unittest/funcs/err.badalloca2.r
    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

After the last commit we can alloca()te, store the returned pointers in
variables and get them back out looking identical to how they were
stored, with suitable verifier bounds.

All that is left is handling dereferences.  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.  In both cases we must
re-bounds-check: the pointer that we are dealing with will have been
bounds-checked already, one way or another, but it is common to modify
that pointer (usually via addition, sometimes, e.g. in lvalues,
compiler-generated addition), and we must bounds-check again after that
to make sure it is still in bounds.

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.  We do the actual bounds-checking
using a new dt_cg_check_scratch_bounds, which handles the annoying
getting-the-scratch-base part and then calls the bounds checker we
already wrote, dt_cg_check_bounds (exploiting its previously unused
ability to bounds-check not just offsets but pointers too).

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, 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 variable storage, and dereferencing all work.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 3390b18c14915f8c40cd028dbf963113b9b1ac94
      https://github.com/oracle/dtrace-utils/commit/3390b18c14915f8c40cd028dbf963113b9b1ac94
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M libdtrace/dt_cg.c
    M libdtrace/dt_dctx.h
    M test/unittest/codegen/tst.stack_layout.r
    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
    M test/unittest/funcs/err.badalloca.d
    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
    M test/unittest/funcs/tst.bcopy.d

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

bcopy can mostly use our existing bounds-checking machinery, but not
entirely: we also need to handle the 'not in scratch' condition of the
src argument (a condition I'd be happy to drop, frankly, if
bpf_probe_read can handle overlapping copies: it doesn't gain us much
and adds a lot of complexity).  This requires a whole new
bounds-checking function, since it's not an inverse of 'in scratch'; in
particular, both 'not in scratch' and 'in scratch' should reject copies
that overlap the edges of scratch space, and 'not in scratch' has to
handle size overflows, copies that wrap around the edge of the address
space and craziness like that.

This is all complicated a bit because although the bounds checker is
mostly being passed map_values, it must do most bounds-checking on
scalars, because the verifier ignores most bounds checking when either
arg is non-scalar.  To help with this, we add a scratch DMST_SCALARIZER
value ot the machine state, which is just a uint64_t you can round-trip
through to turn pointer-like values into scalars.  (At the end of
bounds-checking, we turn it back into a map_value again via the usual
addition-to-scratchbot approach.)

We add a bunch of tests for all this too.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 6008d21375df4252282cd05d33f184c3a40d0d7d
      https://github.com/oracle/dtrace-utils/commit/6008d21375df4252282cd05d33f184c3a40d0d7d
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-01 (Tue, 01 Mar 2022)

  Changed paths:
    M runtest.sh

  Log Message:
  -----------
  tests: always check expected results as text, not binary

Even if we get \0 in results, we want to see the output, not helpful
"binary files differ" messages.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 8a0a3c6c17a885ec3865e2874c256986ea911fa9
      https://github.com/oracle/dtrace-utils/commit/8a0a3c6c17a885ec3865e2874c256986ea911fa9
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-02 (Wed, 02 Mar 2022)

  Changed paths:
    M libdtrace/dt_cg.c

  Log Message:
  -----------
  alloca: fix verifier failure checking the bounds of scalars

dt_check_scratch_bounds works for all genuine pointer inputs... but the
nature of the beast that the caller might pass it *anything*.  What if
the caller passes it a random integer, or a random kernel address?
The first thing we do is turn it into an offset by subtracting the
baseptr from it, and oops that is now maths between a scalar and a
map_value and the verifier fails.

So don't rely on the offset reduction to turn the reg into a scalar,
since it can fail if it already *is* one; instead, scalarize it
explicitly like we do for out-of-bounds checking.  That means we
have to scalarize the baseptr too, since if we already scalarized the
reg, subtracting the baseptr from it is now maths between a map_value
and a scalar again!

(We work on a copy of the baseptr, since we need a real map_value
baseptr at the end to turn the scalar reg offset back into a map_value.)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 602cbfd575b0d055b9f5ef69dc34b502aebb0113
      https://github.com/oracle/dtrace-utils/commit/602cbfd575b0d055b9f5ef69dc34b502aebb0113
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-02 (Wed, 02 Mar 2022)

  Changed paths:
    M libdtrace/dt_bpf.c
    M libdtrace/dt_cg.c
    M libdtrace/dt_dctx.h
    M libdtrace/dt_dlibs.c
    M libdtrace/dt_parser.c
    M test/unittest/codegen/tst.stack_layout.r
    M test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.var-clash.d
    M test/unittest/funcs/alloca/err.D_ALLOCA_INCOMPAT.var-clash.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

  Log Message:
  -----------
  alloca: support null pointers

Variables used to store pointers obtained from alloca() cannot be reused
for any other purpose -- but there is one purpose people are almost
certain to want, which is assigning NULL to them.  You don't want that
to elicit an error message!

Making this work is quite tricky.  We can't just stuff a 0 into
variables and load it, because that would lead to us subtracting
scratchbot from it and then adding scratchbot back afterwards, giving
null pointers a huge random value in the variables.  So we detect
the specific case of var = NULL at codegen time by literal matching the
parse tree and turn it into a store of a sentinel value,
DT_CG_ALLOCA_NULLPTR (~(1<<30)).  We can then detect that at load time
and turn it back into a NULL.

But that's not enough.  We might say "it's up to the user to not
dereference it", but the verifier forces us to ensure that this can't
happen.  We can't leave a 0 in there either because it's not a pointer
value and the verifier's tracking of what happens if a 0 is dereferenced
hits map_value-versus-literal problems: since every *other* value we
can hold in an alloca parse tree node is a pointer, we should arrange
for this to be one too.

But a pointer to what?  It can't be a pointer to scratch space, and it
certainly can't be a pointer to NULL -- that's not a map_value!  So we
introduce a new "null pointer space", in the shape of a one-byte BPF
array, and set null pointers to point at that at load time.  Then it's
just a matter of having the bounds-checkers check for both 0 and the
null pointer space value and raise suitable out-of-bounds errors in both
cases.  (As usual, in-scratch and out-of-scratch bounds checkers
diverge: *both* consider null pointers out of bounds, but the in-scratch
checker doesn't need to do anything to detect that because null pointers
are outside scratch space anyway.  This means null pointer detection
adds very little code except to bcopy.)

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


  Commit: 8b3328294730c05b974365a765980e0b3c5269a2
      https://github.com/oracle/dtrace-utils/commit/8b3328294730c05b974365a765980e0b3c5269a2
  Author: Nick Alcock <nick.alcock at oracle.com>
  Date:   2022-03-02 (Wed, 02 Mar 2022)

  Changed paths:
    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
    A test/unittest/funcs/err.badbcopy7.r

  Log Message:
  -----------
  alloca, tests: add test/unittest/funcs/err.badbcopy*.d expected results

The lack of these nearly led to a verifier failure being overlooked
because a verifier failure from an err.* test with no err tag or
expected results just looks like a perfectly valid error.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>


Compare: https://github.com/oracle/dtrace-utils/compare/22998a9a29c5%5E...8b3328294730



More information about the DTrace-devel mailing list