[DTrace-devel] [PATCH v2] cg: fix store/load for struct and union members for alloca pointers

Nick Alcock nick.alcock at oracle.com
Thu Aug 31 18:09:23 UTC 2023


On 31 Aug 2023, Kris Van Hees via DTrace-devel verbalised:

> @@ -3051,7 +3051,7 @@ dt_cg_store(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst)
>  	int		dreg = dst->dn_reg;
>  
>  	/*
> -	 * If we're loading a bit-field, the size of our store is found by
> +	 * If we're storing into a bit-field, the size of our store is found by
>  	 * rounding dst's cte_bits up to a byte boundary and then finding the
>  	 * nearest power of two to this value (see clp2(), above).
>  	 */
> @@ -3062,13 +3062,15 @@ dt_cg_store(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst)
>  		size = dt_node_type_size(dst);
>  
>  	/*
> -	 * If we're loading a writable non-alloca lvalue, and it's a
> -	 * dereference, and *its* child is an alloca pointer, then this is a
> -	 * dereferenced alloca pointer and needs bounds-checking (which could
> -	 * not be done at deref time due to not knowing the size of the write).
> +	 * If we're storing into a writable lvalue that is a dereference of an
> +	 * alloca pointer or if we are storing into a writable alloca lvalue,
> +	 * we need to do bounds-checking and turn the offset value into a real
> +	 * pointer.

Really stupid comment errors... but the deref thing is because I
thought that struct derefs for writable lvalues were always converted
into (*foo).bar by the DT_TOK_PTR code -- but looking at it now, if that
code ever existed, it is gone I think (and this doesn't allow for array
derefs at all, for instance: whoops, didn't think of that).

> -	if (dst->dn_flags & DT_NF_WRITABLE && dst->dn_flags & DT_NF_LVALUE
> -	    && dst->dn_op == DT_TOK_DEREF && dst->dn_child->dn_flags & DT_NF_ALLOCA) {
> +	if (dst->dn_flags & DT_NF_WRITABLE && dst->dn_flags & DT_NF_LVALUE &&
> +	    ((dst->dn_op == DT_TOK_DEREF &&
> +	      dst->dn_child->dn_flags & DT_NF_ALLOCA) ||
> +	     dst->dn_flags & DT_NF_ALLOCA)) {

This looks good, assuming that something somewhere is propagating taint
across the -> (and I think that should happen mostly automatically,
though DT_TOK_PTR and DT_TOK_DOT aren't explicitly doing it).

>  		assert(!(dst->dn_flags & DT_NF_BITFIELD));
>  
>  		if ((dreg = dt_regset_alloc(drp)) == -1)
> @@ -6473,7 +6475,7 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>  		}
>  
>  		if (m.ctm_offset != 0)
> -			emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_left->dn_reg, m.ctm_offset / NBBY));
> +			emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, m.ctm_offset / NBBY));

... um, a few lines below this we see:

        dnp->dn_reg = dnp->dn_left->dn_reg;

Has it even been initialized at this point? If not, I think we should be
using dnp->dn_left still.

>  		if (!(dnp->dn_flags & DT_NF_REF)) {
>  			uint_t	op;
> @@ -6481,8 +6483,20 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>  
>  			op = dt_cg_ldsize(dnp, ctfp, m.ctm_type, &size);
>  
> +			/*
> +			 * If the left-hand side of PTR or DOT is an alloca
> +			 * pointer, bounds-check the pointer reference.
> +			 */
> +			if (dnp->dn_left->dn_flags & DT_NF_ALLOCA) {
> +				assert(dnp->dn_flags & DT_NF_ALLOCA);
> +				dt_cg_alloca_access_check(dlp, drp, dnp->dn_reg,
> +							  DT_ISIMM, size);
> +				dt_cg_alloca_ptr(dlp, drp, dnp->dn_reg,
> +						 dnp->dn_reg);
> +			}

dnp->dn_reg is probably still just as uninitialized here. dnp->dn_left?
(Maybe the code has changed from what I'm looking at, and dnp->dn_reg
is initialized higher up now. In that case, this code is fine.)

The LHS access check is definitely necessary iff we're not always
getting foo->bar and foo[bar] transformed into (*foo).bar etc as I was
assuming we were, But I'm wondering why that isn't happening... I'd
swear it used to.

> +
>  			if (dnp->dn_left->dn_flags & (DT_NF_ALLOCA | DT_NF_DPTR))
> -				emit(dlp, BPF_LOAD(op, dnp->dn_left->dn_reg, dnp->dn_left->dn_reg, 0));
> +				emit(dlp, BPF_LOAD(op, dnp->dn_reg, dnp->dn_reg, 0));

Ditto.

> diff --git a/test/unittest/funcs/alloca/tst.alloca-array-load.d b/test/unittest/funcs/alloca/tst.alloca-array-load.d

Nice tests.




More information about the DTrace-devel mailing list