[DTrace-devel] [PATCH v3 19/19] alloca: allow passing alloca pointers to actions and subrs

Nick Alcock nick.alcock at oracle.com
Thu Mar 24 18:24:45 UTC 2022


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.

Since most of them are char *, we need to check the string length
against the bound, which means we need to figure it out.  So split out
the guts of strlen into a new dt_cg_strlen() and call it both from
dt_cg_subr_strlen() and from a new dt_cg_check_alloca_string_bounds(),
which is called from everywhere where an incoming char * arg might be
an alloca'ed pointer.  Most of these are just ordinary subrs, but there
are a few surprising places: dt_cg_store_val, which is called to
store things passed to actions which might be in alloca()ed space
(both strings and scalars); and 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>
---
 libdtrace/dt_cg.c                             |  89 ++++++++++---
 test/unittest/funcs/alloca/tst.alloca-funcs.d | 125 ++++++++++++++++++
 2 files changed, 198 insertions(+), 16 deletions(-)
 create mode 100644 test/unittest/funcs/alloca/tst.alloca-funcs.d

diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 59e8c206979a..730248c7825d 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -28,6 +28,9 @@ static void dt_cg_node(dt_node_t *, dt_irlist_t *, dt_regset_t *);
 static void _dt_unused_
 dt_cg_trace(dt_irlist_t *dlp _dt_unused_, dt_regset_t *drp _dt_unused_,
 	    int isreg _dt_unused_, int counter _dt_unused_, uint64_t val _dt_unused_);
+static void dt_cg_check_scratch_bounds(dt_irlist_t *dlp, dt_regset_t *drp, int reg,
+				       int size, int sizemax);
+static void dt_cg_strlen(dt_irlist_t *dlp, dt_regset_t *drp, int str);
 
 /*
  * The value stored in an alloca'ed variable if what was stored is literal
@@ -765,6 +768,25 @@ dt_cg_check_notnull(dt_irlist_t *dlp, dt_regset_t *drp, int reg)
 		   BPF_NOP());
 }
 
+/*
+ * Check the bounds of the string in node STR, if need be (if it is in alloca()ed
+ * space).  Should be called for every node that is an argument to a function
+ * and that might come from alloca'ed space.
+ */
+static void
+dt_cg_check_alloca_string_bounds(dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *str)
+{
+	int opt_scratchsize = yypcb->pcb_hdl->dt_options[DTRACEOPT_SCRATCHSIZE];
+
+	if (!(str->dn_flags & DT_NF_ALLOCA))
+		return;
+
+	dt_regset_xalloc(drp, BPF_REG_0);
+	dt_cg_strlen(dlp, drp, str->dn_reg);
+	dt_cg_check_scratch_bounds(dlp, drp, str->dn_reg, BPF_REG_0, opt_scratchsize);
+	dt_regset_free(drp, BPF_REG_0);
+}
+
 /*
  * Check whether mst->fault indicates a fault was triggered.  If so, abort the
  * current clause by means of a straight jump to the exit label.
@@ -973,6 +995,25 @@ dt_cg_tstring_free(dt_pcb_t *pcb, dt_node_t *dnp)
 	}
 }
 
+/*
+ * Generate code to take the length of the string in reg STR: return it in
+ * BPF_REG_0 (which must be xalloced before the call).
+ */
+static void
+dt_cg_strlen(dt_irlist_t *dlp, dt_regset_t *drp, int str)
+{
+	dt_ident_t	*idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_strlen");
+
+	assert(idp != NULL);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
+	emit(dlp, BPF_MOV_REG(BPF_REG_2, str));
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
+	dt_regset_free_args(drp);
+}
+
 static const uint_t	ldstw[] = {
 					0,
 					BPF_B,	BPF_H,	0, BPF_W,
@@ -1061,6 +1102,7 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind,
 		size_t	strsize = pcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE];
 
 		dt_cg_check_notnull(dlp, drp, dnp->dn_reg);
+		dt_cg_check_alloca_string_bounds(dlp, drp, dnp);
 
 		TRACE_REGSET("store_val(): Begin ");
 		off = dt_rec_add(pcb->pcb_hdl, dt_cg_fill_gap, kind, size + 1,
@@ -1096,6 +1138,12 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind,
 		TRACE_REGSET("store_val(): Begin ");
 		dt_cg_check_notnull(dlp, drp, dnp->dn_reg);
 
+		if (dnp->dn_flags & DT_NF_ALLOCA) {
+			int opt_scratchsize = yypcb->pcb_hdl->dt_options[DTRACEOPT_SCRATCHSIZE];
+			dt_cg_check_scratch_bounds(dlp, drp, dnp->dn_reg, size,
+						   opt_scratchsize);
+		}
+
 		if (dt_regset_xalloc_args(drp) == -1)
 			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
 
@@ -4105,9 +4153,10 @@ dt_cg_subr_path_helper(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
 	TRACE_REGSET("    subr-path_helper:Begin");
 	dt_cg_node(str, dlp, drp);
 	dt_cg_check_notnull(dlp, drp, str->dn_reg);
+	dt_cg_check_alloca_string_bounds(dlp, drp, str);
 
 	/*
-	 * The result needs be be a temporary string, so we request one.
+	 * The result needs to be a temporary string, so we request one.
 	 */
 	dnp->dn_reg = dt_regset_alloc(drp);
 	if (dnp->dn_reg == -1)
@@ -4169,6 +4218,9 @@ dt_cg_subr_index(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	if (start != NULL)
 		dt_cg_node(start, dlp, drp);
 
+	dt_cg_check_alloca_string_bounds(dlp, drp, s);
+	dt_cg_check_alloca_string_bounds(dlp, drp, t);
+
 	if (dt_regset_xalloc_args(drp) == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
 	emit(dlp,  BPF_MOV_REG(BPF_REG_1, s->dn_reg));
@@ -4285,6 +4337,9 @@ dt_cg_subr_rindex(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	if (start != NULL)
 		dt_cg_node(start, dlp, drp);
 
+	dt_cg_check_alloca_string_bounds(dlp, drp, s);
+	dt_cg_check_alloca_string_bounds(dlp, drp, t);
+
 	/* start setting up function call to BPF function dt_rindex() */
 	if (dt_regset_xalloc_args(drp) == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -4540,6 +4595,7 @@ dt_cg_subr_strchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_cg_node(str, dlp, drp);
 	dt_cg_check_notnull(dlp, drp, str->dn_reg);
 	dt_cg_node(chr, dlp, drp);
+	dt_cg_check_alloca_string_bounds(dlp, drp, str);
 
 	if (dt_regset_xalloc_args(drp) == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -4551,7 +4607,7 @@ dt_cg_subr_strchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_regset_free(drp, chr->dn_reg);
 
 	/*
-	 * The result needs be be a temporary string, so we request one.
+	 * The result needs to be a temporary string, so we request one.
 	 */
 	dnp->dn_reg = dt_regset_alloc(drp);
 	if (dnp->dn_reg == -1)
@@ -4596,6 +4652,7 @@ dt_cg_subr_strrchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_cg_node(str, dlp, drp);
 	dt_cg_check_notnull(dlp, drp, str->dn_reg);
 	dt_cg_node(chr, dlp, drp);
+	dt_cg_check_alloca_string_bounds(dlp, drp, str);
 
 	if (dt_regset_xalloc_args(drp) == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -4607,7 +4664,7 @@ dt_cg_subr_strrchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_regset_free(drp, chr->dn_reg);
 
 	/*
-	 * The result needs be be a temporary string, so we request one.
+	 * The result needs to be a temporary string, so we request one.
 	 */
 	dnp->dn_reg = dt_regset_alloc(drp);
 	if (dnp->dn_reg == -1)
@@ -4637,28 +4694,20 @@ dt_cg_subr_strrchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 static void
 dt_cg_subr_strlen(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 {
-	dt_ident_t	*idp = dt_dlib_get_func(yypcb->pcb_hdl, "dt_strlen");
 	dt_node_t	*str = dnp->dn_args;
 
-	assert(idp != NULL);
-
 	TRACE_REGSET("    subr-strlen:Begin");
 
 	dt_cg_node(str, dlp, drp);
 	dt_cg_check_notnull(dlp, drp, str->dn_reg);
+	dt_cg_check_alloca_string_bounds(dlp, drp, str);
 	dnp->dn_reg = str->dn_reg;		/* re-use register */
 
-	if (dt_regset_xalloc_args(drp) == -1)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
-
-	emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
-	emit(dlp, BPF_MOV_REG(BPF_REG_2, str->dn_reg));
-	dt_regset_free(drp, str->dn_reg);
-	dt_cg_tstring_free(yypcb, str);
 	dt_regset_xalloc(drp, BPF_REG_0);
-	emite(dlp,BPF_CALL_FUNC(idp->di_id), idp);
+	dt_cg_strlen(dlp, drp, str->dn_reg);
+	dt_cg_tstring_free(yypcb, str);
 
-	dt_regset_free_args(drp);
+	dt_regset_free (drp, str->dn_reg);
 	dnp->dn_reg = dt_regset_alloc(drp);
 	if (dnp->dn_reg == -1)
 		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -4683,9 +4732,11 @@ dt_cg_subr_strjoin(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_cg_check_notnull(dlp, drp, s1->dn_reg);
 	dt_cg_node(s2, dlp, drp);
 	dt_cg_check_notnull(dlp, drp, s2->dn_reg);
+	dt_cg_check_alloca_string_bounds(dlp, drp, s1);
+	dt_cg_check_alloca_string_bounds(dlp, drp, s2);
 
 	/*
-	 * The result needs be be a temporary string, so we request one.
+	 * The result needs to be a temporary string, so we request one.
 	 */
 	dnp->dn_reg = dt_regset_alloc(drp);
 	if (dnp->dn_reg == -1)
@@ -4730,6 +4781,8 @@ dt_cg_subr_strstr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_cg_check_notnull(dlp, drp, s->dn_reg);
 	dt_cg_node(t, dlp, drp);
 	dt_cg_check_notnull(dlp, drp, t->dn_reg);
+	dt_cg_check_alloca_string_bounds(dlp, drp, s);
+	dt_cg_check_alloca_string_bounds(dlp, drp, t);
 
 	/* call dt_index() call */
 	if (dt_regset_xalloc_args(drp) == -1)
@@ -4831,6 +4884,7 @@ dt_cg_subr_strtok(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 		/* string is present:  copy it to internal state */
 		dt_cg_node(str, dlp, drp);
 		dt_cg_check_notnull(dlp, drp, str->dn_reg);
+		dt_cg_check_alloca_string_bounds(dlp, drp, str);
 
 		if (dt_regset_xalloc_args(drp) == -1)
 			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -4860,6 +4914,7 @@ dt_cg_subr_strtok(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	/* get delimiters */
 	dt_cg_node(del, dlp, drp);
 	dt_cg_check_notnull(dlp, drp, del->dn_reg);
+	dt_cg_check_alloca_string_bounds(dlp, drp, del);
 
 	/* allocate temporary string for result */
 	dnp->dn_reg = dt_regset_alloc(drp);
@@ -4914,6 +4969,7 @@ dt_cg_subr_substr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_cg_node(idx, dlp, drp);
 	if (cnt != NULL)
 		dt_cg_node(cnt, dlp, drp);
+	dt_cg_check_alloca_string_bounds(dlp, drp, str);
 
 	/*
 	 * Allocate a temporary string slot for the result.
@@ -5363,6 +5419,7 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 
 	case DT_TOK_STRINGOF:
 		dt_cg_node(dnp->dn_child, dlp, drp);
+		dt_cg_check_alloca_string_bounds(dlp, drp, dnp->dn_child);
 		dnp->dn_reg = dnp->dn_child->dn_reg;
 		break;
 
diff --git a/test/unittest/funcs/alloca/tst.alloca-funcs.d b/test/unittest/funcs/alloca/tst.alloca-funcs.d
new file mode 100644
index 000000000000..eca281ac5f70
--- /dev/null
+++ b/test/unittest/funcs/alloca/tst.alloca-funcs.d
@@ -0,0 +1,125 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 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.
+ */
+
+/*
+ * ASSERTION: Function calls to alloca()ed space work.
+ *
+ * SECTION: Actions and Subroutines/alloca()
+ */
+
+#pragma D option quiet
+
+/*
+ * Everything is in an independent clause with its own alloca(),
+ * to try to make sure that the verifier's bound proofs don't leak
+ * from one test to the next.
+ */
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	x[0] = 0;
+	printf("%s\n", stringof(x));
+	trace(x);
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	x[0] = 0;
+	trace(basename(x));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	y = (char *) alloca(8);
+	x[0] = 0;
+	y[0] = 0;
+	trace(index(x, y));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	y = (char *) alloca(8);
+	x[0] = 0;
+	y[0] = 0;
+	trace(rindex(x, y));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	x[0] = 0;
+	trace(strchr(x, 0));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	x[0] = 0;
+	trace(strrchr(x, 0));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	x[0] = 0;
+	trace(strlen(x));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	y = (char *) alloca(8);
+	x[0] = 0;
+	y[0] = 0;
+	trace(strjoin(x, y));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	y = (char *) alloca(8);
+	x[0] = 0;
+	y[0] = 0;
+	trace(strstr(x, y));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	y = (char *) alloca(8);
+	x[0] = 0;
+	y[0] = 0;
+	trace(strtok(x, y));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	x[0] = 0;
+	trace(strtok(NULL, x));
+}
+
+BEGIN
+{
+	x = (char *) alloca(8);
+	x[0] = 0;
+	trace(substr(x, 0, 1));
+}
+
+BEGIN
+{
+	exit(0);
+}
+
+ERROR
+{
+	exit(0);
+}
-- 
2.35.1.261.g8402f930ba.dirty




More information about the DTrace-devel mailing list