[DTrace-devel] [PATCH] cg: ensure string results in a ternary are padded to strsize

Eugene Loh eugene.loh at oracle.com
Fri Feb 7 19:17:44 UTC 2025


Reviewed-by: Eugene Loh <eugene.loh at oracle.com>

That said:

*)  Is it possible to construct a test?

*)  Has there been an audit to ferret out other instances of this 
problem?  Also, I'm confused what position we're taking here. We're 
saying that it's possible for a string (which in D has strsize) to have 
garbage after the NUL byte, but we'll deal with this situation only in 
the case that it's an argument of a ternary operation?  Why do we not 
say instead that a strsize string will not have garbage after the NUL 
byte, so that a strsize copy of a string will be safe (regardless of 
whether it's a ternary op)?

*)  What position are we taking here on BPF register pressure? E.g., 
what if dst were %r0 (okay, let's hope not) or even just %r1-%r5 (even 
just %r4 or %r5)?  One position is that we don't do a robust job of BPF 
reg management anyhow and someday we're going to have to clean that up 
and so being sloppy now is okay or even necessary.  Another position is 
that we can add a little code to improve things (even if other areas of 
dt_cg.c vary considerably in quality).  E.g., one can fill-spill regs 
between the two function calls, costing extra BPF instructions only if 
needed. (That's already a pretty common practice in dt_cg.c.)  For extra 
robustness (beyond what we currently do in dt_cg.c), one can move 
%r1=dst and %r3=src in whatever order makes sense (more complicated and 
less likely to be useful).  Anyhow, fill-spill regs between function 
calls is a pretty common practice in dt_cg.c and I would think would 
make to do here.

On 2/7/25 01:11, Kris Van Hees wrote:
> The result of a string ternary was copied from either left or right
> value using a memcpy() of strsize, but that could result in garbage
> being included in the result beyond the terminating 0-byte of the
> string value.  If the result was e.g. assigned to a char-array
> translator member, that garbage could affect consumer output.
>
> The ternary will now copy the string using dt_cg_strcpy() which ensures
> that any string < strsize will be padded with 0-bytes up to strsize.
>
> Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
> ---
>   libdtrace/dt_cg.c | 68 ++++++++++++++++++++++++++++++++++++-----------
>   1 file changed, 52 insertions(+), 16 deletions(-)
>
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index a5c9aa09..b48e27f0 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -1371,6 +1371,19 @@ dt_cg_fill_gap(dt_pcb_t *pcb, int gap)
>   		emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, off, 0));
>   }
>   
> +/*
> + * Store a pointer to the 'memory block of zeros' in reg.
> + */
> +static void
> +dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp)
> +{
> +	dtrace_hdl_t	*dtp = yypcb->pcb_hdl;
> +	dt_ident_t	*zero_off = dt_dlib_get_var(dtp, "ZERO_OFF");
> +
> +	dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB);
> +	emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off);
> +}
> +
>   static void
>   dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size)
>   {
> @@ -1394,6 +1407,43 @@ dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size)
>   	dt_regset_free(drp, BPF_REG_0);
>   }
>   
> +/*
> + * Copy a string from the pointer stored in the src register to the pointer
> + * stored in the dst register.  At most strsize characters will be copied, and
> + * if the source string is less than strsize characters, the remainder will be
> + * filled with 0s.
> + */
> +static void
> +dt_cg_strcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src)
> +{
> +	dtrace_hdl_t	*dtp = yypcb->pcb_hdl;
> +	uint_t		lbl_ok = dt_irlist_label(dlp);
> +	size_t		size = dtp->dt_options[DTRACEOPT_STRSIZE];
> +
> +	if (dt_regset_xalloc_args(drp) == -1)
> +		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> +
> +	emit(dlp,  BPF_MOV_REG(BPF_REG_1, dst));
> +	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, size));
> +	emit(dlp,  BPF_MOV_REG(BPF_REG_3, src));
> +	dt_regset_xalloc(drp, BPF_REG_0);
> +	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel_str]));
> +
> +	emit(dlp,  BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_ok));
> +	dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, DT_ISREG, src);
> +
> +	emitl(dlp, lbl_ok,
> +		   BPF_NOP());
> +	emit(dlp,  BPF_MOV_REG(BPF_REG_1, dst));
> +	emit(dlp,  BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_0));
> +	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, size));
> +	emit(dlp,  BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_0));
> +	dt_cg_zerosptr(BPF_REG_3, dlp, drp);
> +	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
> +	dt_regset_free(drp, BPF_REG_0);
> +	dt_regset_free_args(drp);
> +}
> +
>   static void
>   dt_cg_spill_store(int reg)
>   {
> @@ -3040,19 +3090,6 @@ dt_cg_pop_stack(int reg, dt_irlist_t *dlp, dt_regset_t *drp)
>   	dt_regset_free(drp, treg);
>   }
>   
> -/*
> - * Store a pointer to the 'memory block of zeros' in reg.
> - */
> -static void
> -dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp)
> -{
> -	dtrace_hdl_t	*dtp = yypcb->pcb_hdl;
> -	dt_ident_t	*zero_off = dt_dlib_get_var(dtp, "ZERO_OFF");
> -
> -	dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB);
> -	emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off);
> -}
> -
>   /*
>    * Generate code to promote signed scalars (size < 64 bits) to native register
>    * size (64 bits).
> @@ -4603,7 +4640,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>   	 * dn_right).
>   	 */
>   	if (dt_node_is_string(dnp)) {
> -		uint_t lbl_null = dt_irlist_label(dlp);
> +		uint_t	lbl_null = dt_irlist_label(dlp);
>   
>   		emit(dlp,  BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_reg, 0, lbl_null));
>   
> @@ -4619,8 +4656,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
>   		dt_cg_access_dctx(dnp->dn_reg, dlp, drp, DCTX_MEM);
>   		emit(dlp,  BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value));
>   
> -		dt_cg_memcpy(dlp, drp, dnp->dn_reg, BPF_REG_0,
> -			     yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE]);
> +		dt_cg_strcpy(dlp, drp, dnp->dn_reg, BPF_REG_0);
>   
>   		emitl(dlp, lbl_null,
>   			   BPF_NOP());



More information about the DTrace-devel mailing list