[DTrace-devel] [PATCH] Fix double-free of dtp->dt_strtab

Eugene Loh eugene.loh at oracle.com
Wed Apr 8 14:19:38 PDT 2020


lgtm (with the provision that I don't know this part of the code).

On 04/08/2020 08:00 AM, Kris Van Hees wrote:
> The consolidated string table that is used to populate the strtab BPF
> map when we prepare to load and execute BPF programs was being free'd
> twice (first when the DIFOs are destroyed and second when we are closing
> the DTrace consumer handle).  The reason for this is that dtp->dt_strtab
> was being constructed anew for each DIFO, and then assigned to the
> dtdo_strtab member for the DIFO as well.  So, the last DIFO that got
> assembled would have a reference to the same string table as the DTrace
> consumer handle.
>
> When that DIFO got destroyed (dt_difo_free()), the string table would be
> free'd as well.  Then, in dt_close() we would try to free dtp->dt_strtab
> which arready got free'd.
>
> We now (correctly) generate string table data for each DIFO, and assign
> it to dtp->dt_strtab (size is stored in dtp->strlen).  This is sufficient
> because we only care about the last one that get generated (which by
> design contains all strings from all prior DIFOs).  We know that the
> string table will be free'd in dt_difo_free() so we ensure that the
> dtp->dt_strtab and dtp->dt_strlen are cleared when the DIFO referencing
> the same string table as the consumer handle is free'd, and we refrain
> from freeing dtp->dt_strtab altogether.
>
> Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
> ---
>   libdtrace/dt_as.c   | 32 ++++++++++++++++++++++++--------
>   libdtrace/dt_open.c |  2 --
>   libdtrace/dt_subr.c | 11 +++++++++++
>   3 files changed, 35 insertions(+), 10 deletions(-)
>
> diff --git a/libdtrace/dt_as.c b/libdtrace/dt_as.c
> index 2ddbc886..42c8228a 100644
> --- a/libdtrace/dt_as.c
> +++ b/libdtrace/dt_as.c
> @@ -535,20 +535,36 @@ fail:
>   
>   	/*
>   	 * Allocate memory for the compiled string table and then copy the
> -	 * chunks from the string table into the final string buffer.
> +	 * chunks from the string table into the final string buffer for the
> +	 * DIFO we are constructing.
> +	 *
> +	 * We keep the string table around (dtp->dt_csstab) because further
> +	 * compilations may add more strings.  We will load a consolidated
> +	 * compiled string table into a strtab BPF map so it can be used ny
> +	 * all BPF programs we will be loading.  Since each DIFO will have a
> +	 * string table that comprises the strings of all DIFOs before it along
> +	 * with any new ones, we simply assign the latest (and most complete)
> +	 * string table to dtp->dt_strtab (and the size as dtp->dt_strlen), so
> +	 * that when we are ready to load and execute programs, we know we have
> +	 * the latest and greatest string table to work with.
> +	 *
> +	 * Note that this means that we should *not* free dtp->dt_strtab later
> +	 * because it will be free'd when the DIFO is destroyed.
>   	 */
> -	if ((n = dt_strtab_size(dtp->dt_ccstab)) != 0) {
> -		if ((dtp->dt_strtab = dt_alloc(dtp, n)) == NULL)
> +	dp->dtdo_strlen = dt_strtab_size(dtp->dt_ccstab);
> +	if (dp->dtdo_strlen > 0) {
> +		dp->dtdo_strtab = dt_zalloc(dtp, dp->dtdo_strlen);
> +		if (dp->dtdo_strtab == NULL)
>   			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
>   
>   		dt_strtab_write(dtp->dt_ccstab,
>   				(dt_strtab_write_f *)dt_strtab_copystr,
> -				dtp->dt_strtab);
> -		dtp->dt_strlen = (uint32_t)n;
> +				dp->dtdo_strtab);
>   
> -		dp->dtdo_strtab = dtp->dt_strtab;
> -		dp->dtdo_strlen = dtp->dt_strlen;
> -	}
> +		dtp->dt_strtab = dp->dtdo_strtab;
> +		dtp->dt_strlen = dp->dtdo_strlen;
> +	} else
> +		dp->dtdo_strtab = NULL;
>   
>   	/*
>   	 * Fill in the DIFO return type from the type associated with the
> diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
> index ab60a803..36a65c88 100644
> --- a/libdtrace/dt_open.c
> +++ b/libdtrace/dt_open.c
> @@ -1160,8 +1160,6 @@ dtrace_close(dtrace_hdl_t *dtp)
>   		dt_ident_destroy(idp);
>   	}
>   
> -	if (dtp->dt_strtab != NULL)
> -		dt_free(dtp, dtp->dt_strtab);
>   	if (dtp->dt_ccstab != NULL)
>   		dt_strtab_destroy(dtp->dt_ccstab);
>   	if (dtp->dt_macros != NULL)
> diff --git a/libdtrace/dt_subr.c b/libdtrace/dt_subr.c
> index 14824578..87db2fb4 100644
> --- a/libdtrace/dt_subr.c
> +++ b/libdtrace/dt_subr.c
> @@ -726,7 +726,18 @@ dt_difo_free(dtrace_hdl_t *dtp, dtrace_difo_t *dp)
>   	if (dp == NULL)
>   		return; /* simplify caller code */
>   
> +	/*
> +	 * If we are destroying the DIFO that holds the final consolidated
> +	 * string table, clear the dt_strtab and dt_strlen members in the
> +	 * consumer handle.
> +	 */
> +	if (dtp->dt_strtab == dp->dtdo_strtab) {
> +		dtp->dt_strtab = NULL;
> +		dtp->dt_strlen = 0;
> +	}
> +
>   	dt_free(dtp, dp->dtdo_buf);
> +	dt_free(dtp, dp->dtdo_strtab);
>   	dt_free(dtp, dp->dtdo_vartab);
>   	dt_free(dtp, dp->dtdo_kreltab);
>   	dt_free(dtp, dp->dtdo_ureltab);




More information about the DTrace-devel mailing list