Skip to content

Commit

Permalink
alloca: add alloca() itself
Browse files Browse the repository at this point in the history
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@oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
  • Loading branch information
nickalcock committed Apr 19, 2022
1 parent 7a4b783 commit 8d43f03
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 13 deletions.
5 changes: 3 additions & 2 deletions include/dtrace/options_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*
* Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
*/

/*
Expand Down Expand Up @@ -60,7 +60,8 @@
#define DTRACEOPT_BPFLOGSIZE 30 /* BPF verifier log, max # bytes */
#define DTRACEOPT_MAXFRAMES 31 /* maximum number of stack frames */
#define DTRACEOPT_BPFLOG 32 /* always output BPF verifier log */
#define DTRACEOPT_MAX 33 /* number of options */
#define DTRACEOPT_SCRATCHSIZE 33 /* max scratch size permitted */
#define DTRACEOPT_MAX 34 /* number of options */

#define DTRACEOPT_UNSET (dtrace_optval_t)-2 /* unset option */

Expand Down
13 changes: 13 additions & 0 deletions libdtrace/dt_bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ populate_probes_map(dtrace_hdl_t *dtp, int fd)
* used to store the DTrace machine state, the temporary output
* buffer, and temporary storage for stack traces, string
* manipulation, etc.
* - scratchmem: Storage for alloca() and other per-clause scratch space,
* implemented just as for mem.
* - strtab: String table map. This is a global map with a singleton
* element (key 0) that contains the entire string table as a
* concatenation of all unique strings (each terminated with a
Expand Down Expand Up @@ -238,6 +240,7 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
int ci_mapfd, st_mapfd, pr_mapfd;
uint64_t key = 0;
size_t strsize = dtp->dt_options[DTRACEOPT_STRSIZE];
size_t scratchsize = dtp->dt_options[DTRACEOPT_SCRATCHSIZE];
uint8_t *buf, *end;
char *strtab;

Expand Down Expand Up @@ -310,6 +313,16 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
sizeof(uint32_t), memsz, 1) == -1)
return -1; /* dt_errno is set for us */

/*
* The size for this is twice what it needs to be, to allow us to bcopy
* things the size of the scratch space to the start of the scratch
* space without tripping verifier failures: see dt_cg_check_bounds.
*/
if (scratchsize > 0 &&
create_gmap(dtp, "scratchmem", BPF_MAP_TYPE_PERCPU_ARRAY,
sizeof(uint32_t), scratchsize * 2, 1) == -1)
return -1; /* dt_errno is set for us */

/*
* We need to create the global (consolidated) string table. We store
* the actual length (for in-code BPF validation purposes) but augment
Expand Down
75 changes: 74 additions & 1 deletion libdtrace/dt_cg.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act)
} while(0)

DT_CG_STORE_MAP_PTR("strtab", DCTX_STRTAB);
if (dtp->dt_options[DTRACEOPT_SCRATCHSIZE] > 0)
DT_CG_STORE_MAP_PTR("scratchmem", DCTX_SCRATCHMEM);
if (dt_idhash_datasize(dtp->dt_aggs) > 0)
DT_CG_STORE_MAP_PTR("aggs", DCTX_AGG);
if (dt_idhash_datasize(dtp->dt_globals) > 0)
Expand Down Expand Up @@ -372,6 +374,12 @@ dt_cg_call_clause(dtrace_hdl_t *dtp, dt_ident_t *idp, dt_clause_arg_t *arg)
emit(dlp, BPF_LOAD(BPF_W, BPF_REG_0, BPF_REG_0, 0));
emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, arg->act, arg->lbl_exit));

/*
* dctx.mst->scratch_top = 8;
* // stw [%r7 + DMST_SCRATCH_TOP], 8
*/
emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_7, DMST_SCRATCH_TOP, 8));

/*
* dt_clause(dctx); // mov %r1, %r9
* // call clause
Expand Down Expand Up @@ -3804,6 +3812,71 @@ dt_cg_subr_speculation(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
TRACE_REGSET(" subr-speculation:End ");
}

static void
dt_cg_subr_alloca(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
{
dt_node_t *size = dnp->dn_args;
uint_t lbl_ok = dt_irlist_label(dlp);
uint_t lbl_err = dt_irlist_label(dlp);
int opt_scratchsize = yypcb->pcb_hdl->dt_options[DTRACEOPT_SCRATCHSIZE];
int mst, scratchbot, next;

TRACE_REGSET(" subr-alloca:Begin");

dt_cg_node(size, dlp, drp);

/*
* Compile-error out if the size is too large even absent other
* allocations. (This prevents us generating code for which the
* verifier will fail due to one branch of the conditional below being
* determined to be unreachable.)
*
* We need to adjust the scratchsize to account for the first 8 bytes
* that are used to represent the NULL pointer.
*/
if (size->dn_kind == DT_NODE_INT &&
size->dn_value > opt_scratchsize - 8)
dnerror(dnp, D_ALLOCA_SIZE,
"alloca(%lu) size larger than scratchsize %lu\n",
(unsigned long) size->dn_value,
(unsigned long) opt_scratchsize - 8);

if (((dnp->dn_reg = dt_regset_alloc(drp)) == -1) ||
((mst = dt_regset_alloc(drp)) == -1) ||
((scratchbot = dt_regset_alloc(drp)) == -1) ||
((next = dt_regset_alloc(drp)) == -1))
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);

/* Loading. */

emit(dlp, BPF_LOAD(BPF_DW, mst, BPF_REG_FP, DT_STK_DCTX));
emit(dlp, BPF_LOAD(BPF_DW, mst, mst, DCTX_MST));
emit(dlp, BPF_LOAD(BPF_W, next, mst, DMST_SCRATCH_TOP));

/* Size testing and alignment. */

emit(dlp, BPF_MOV_REG(dnp->dn_reg, next));
emit(dlp, BPF_ALU64_REG(BPF_ADD, next, size->dn_reg));
emit(dlp, BPF_ALU64_IMM(BPF_ADD, next, 7));
emit(dlp, BPF_ALU64_IMM(BPF_AND, next, (int) -8));

emit(dlp, BPF_BRANCH_IMM(BPF_JGT, next, opt_scratchsize + 8, lbl_err));
emit(dlp, BPF_BRANCH_IMM(BPF_JLE, dnp->dn_reg, opt_scratchsize + 8,
lbl_ok));
emitl(dlp, lbl_err,
BPF_NOP());
dt_cg_probe_error(yypcb, DTRACEFLT_NOSCRATCH, DT_ISIMM, 0);
emitl(dlp, lbl_ok,
BPF_STORE(BPF_W, mst, DMST_SCRATCH_TOP, next));

dt_regset_free(drp, mst);
dt_regset_free(drp, scratchbot);
dt_regset_free(drp, next);
dt_regset_free(drp, size->dn_reg);

TRACE_REGSET(" subr-alloca:End ");
}

static void
dt_cg_subr_strchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
{
Expand Down Expand Up @@ -4277,7 +4350,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
[DIF_SUBR_STRLEN] = &dt_cg_subr_strlen,
[DIF_SUBR_COPYOUT] = NULL,
[DIF_SUBR_COPYOUTSTR] = NULL,
[DIF_SUBR_ALLOCA] = NULL,
[DIF_SUBR_ALLOCA] = &dt_cg_subr_alloca,
[DIF_SUBR_BCOPY] = NULL,
[DIF_SUBR_COPYINTO] = NULL,
[DIF_SUBR_MSGDSIZE] = NULL,
Expand Down
22 changes: 13 additions & 9 deletions libdtrace/dt_dctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,24 @@ typedef struct dt_mstate {
uint32_t prid; /* Probe ID */
uint32_t clid; /* Clause ID (unique per probe) */
uint32_t tag; /* Tag (for future use) */
uint32_t scratch_top; /* Current top of scratch space */
int32_t syscall_errno; /* syscall errno */
uint64_t fault; /* DTrace fault flags */
uint64_t tstamp; /* cached timestamp value */
dt_pt_regs regs; /* CPU registers */
uint64_t argv[10]; /* Probe arguments */
} dt_mstate_t;

#define DMST_EPID offsetof(dt_mstate_t, epid)
#define DMST_PRID offsetof(dt_mstate_t, prid)
#define DMST_CLID offsetof(dt_mstate_t, clid)
#define DMST_TAG offsetof(dt_mstate_t, tag)
#define DMST_ERRNO offsetof(dt_mstate_t, syscall_errno)
#define DMST_FAULT offsetof(dt_mstate_t, fault)
#define DMST_TSTAMP offsetof(dt_mstate_t, tstamp)
#define DMST_REGS offsetof(dt_mstate_t, regs)
#define DMST_ARG(n) offsetof(dt_mstate_t, argv[n])
#define DMST_EPID offsetof(dt_mstate_t, epid)
#define DMST_PRID offsetof(dt_mstate_t, prid)
#define DMST_CLID offsetof(dt_mstate_t, clid)
#define DMST_TAG offsetof(dt_mstate_t, tag)
#define DMST_SCRATCH_TOP offsetof(dt_mstate_t, scratch_top)
#define DMST_ERRNO offsetof(dt_mstate_t, syscall_errno)
#define DMST_FAULT offsetof(dt_mstate_t, fault)
#define DMST_TSTAMP offsetof(dt_mstate_t, tstamp)
#define DMST_REGS offsetof(dt_mstate_t, regs)
#define DMST_ARG(n) offsetof(dt_mstate_t, argv[n])

/*
* The DTrace context.
Expand All @@ -48,6 +50,7 @@ typedef struct dt_dctx {
dt_mstate_t *mst; /* DTrace machine state */
char *buf; /* Output buffer scratch memory */
char *mem; /* General scratch memory */
char *scratchmem; /* Scratch space for alloca, etc */
char *strtab; /* String constant table */
char *agg; /* Aggregation data */
char *gvars; /* Global variables */
Expand All @@ -59,6 +62,7 @@ typedef struct dt_dctx {
#define DCTX_MST offsetof(dt_dctx_t, mst)
#define DCTX_BUF offsetof(dt_dctx_t, buf)
#define DCTX_MEM offsetof(dt_dctx_t, mem)
#define DCTX_SCRATCHMEM offsetof(dt_dctx_t, scratchmem)
#define DCTX_STRTAB offsetof(dt_dctx_t, strtab)
#define DCTX_AGG offsetof(dt_dctx_t, agg)
#define DCTX_GVARS offsetof(dt_dctx_t, gvars)
Expand Down
1 change: 1 addition & 0 deletions libdtrace/dt_dlibs.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ static const dt_ident_t dt_bpf_symbols[] = {
DT_BPF_SYMBOL(lvars, DT_IDENT_PTR),
DT_BPF_SYMBOL(mem, DT_IDENT_PTR),
DT_BPF_SYMBOL(probes, DT_IDENT_PTR),
DT_BPF_SYMBOL(scratchmem, DT_IDENT_PTR),
DT_BPF_SYMBOL(specs, DT_IDENT_PTR),
DT_BPF_SYMBOL(state, DT_IDENT_PTR),
DT_BPF_SYMBOL(strtab, DT_IDENT_PTR),
Expand Down
6 changes: 6 additions & 0 deletions libdtrace/dt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ static const char *_dtrace_libdir = DTRACE_LIBDIR; /* default library directory

int _dtrace_strbuckets = 211; /* default number of hash buckets (prime) */
uint_t _dtrace_strsize = 256; /* default size of string intrinsic type */
uint_t _dtrace_scratchsize = 256; /* default size of scratch space */
uint_t _dtrace_stkindent = 14; /* default whitespace indent for stack/ustack */
uint_t _dtrace_pidbuckets = 64; /* default number of pid hash buckets */
uint_t _dtrace_pidlrulim = 8; /* default number of pid handles to cache */
Expand Down Expand Up @@ -793,6 +794,11 @@ dt_vopen(int version, int flags, int *errp,
dtp->dt_options[DTRACEOPT_SPECSIZE] = 1024 * 1024 * 4;
dtp->dt_options[DTRACEOPT_NSPEC] = 1;

/*
* Set the maximum scratch space permitted.
*/
dtp->dt_options[DTRACEOPT_SCRATCHSIZE] = sizeof(uint64_t) + _dtrace_scratchsize;

/*
* Set the default value of maxframes.
*/
Expand Down
18 changes: 17 additions & 1 deletion libdtrace/dt_options.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 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 @@ -750,6 +750,21 @@ dt_opt_size(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
return 0;
}

static int
dt_opt_scratchsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
dtrace_optval_t val = 0;

if (arg != NULL && dt_optval_parse(arg, &val) != 0)
return dt_set_errno(dtp, EDT_BADOPTVAL);

if (val > 0)
val = P2ROUNDUP(val + sizeof(uint64_t), sizeof(uint64_t));

dtp->dt_options[option] = val;
return 0;
}

static int
dt_opt_pcapsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
Expand Down Expand Up @@ -1108,6 +1123,7 @@ static const dt_option_t _dtrace_rtoptions[] = {
{ "maxframes", dt_opt_runtime, DTRACEOPT_MAXFRAMES },
{ "nspec", dt_opt_runtime, DTRACEOPT_NSPEC },
{ "pcapsize", dt_opt_pcapsize, DTRACEOPT_PCAPSIZE },
{ "scratchsize", dt_opt_scratchsize, DTRACEOPT_SCRATCHSIZE },
{ "specsize", dt_opt_size, DTRACEOPT_SPECSIZE },
{ "stackframes", dt_opt_runtime, DTRACEOPT_STACKFRAMES },
{ "statusrate", dt_opt_rate, DTRACEOPT_STATUSRATE },
Expand Down

0 comments on commit 8d43f03

Please sign in to comment.