Copied from See also: and: Author: James Zern Thu May 01 02:28:48 2025 vpx_codec_enc_init_multi: fix double free on init failure In `vp8e_init()`, the encoder would take ownership of `mr_cfg.mr_low_res_mode_info` even if `vp8_create_compressor()` failed. This caused confusion at the call site as other failures in `vp8e_init()` did not result in ownership transfer and the caller would free the memory. In the case of `vp8_create_compressor()` failure both the caller and `vpx_codec_destroy()` would free the memory, causing a crash. `mr_*` related variables are now cleared on failure to prevent this situation. Bug: webm:413411335 Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1962421 Change-Id: Ie951d42b9029a586bf9059b650bd8863db9f9ffc --- a/vp8/vp8_cx_iface.c +++ b/vp8/vp8_cx_iface.c @@ -727,17 +727,27 @@ static vpx_codec_err_t vp8e_init(vpx_cod priv->pts_offset_initialized = 0; priv->timestamp_ratio.den = priv->cfg.g_timebase.den; priv->timestamp_ratio.num = (int64_t)priv->cfg.g_timebase.num; priv->timestamp_ratio.num *= TICKS_PER_SEC; reduce_ratio(&priv->timestamp_ratio); set_vp8e_config(&priv->oxcf, priv->cfg, priv->vp8_cfg, mr_cfg); priv->cpi = vp8_create_compressor(&priv->oxcf); - if (!priv->cpi) res = VPX_CODEC_MEM_ERROR; + if (!priv->cpi) { +#if CONFIG_MULTI_RES_ENCODING + // Release ownership of mr_cfg->mr_low_res_mode_info on failure. This + // prevents ownership confusion with the caller and avoids a double + // free when vpx_codec_destroy() is called on this instance. + priv->oxcf.mr_total_resolutions = 0; + priv->oxcf.mr_encoder_id = 0; + priv->oxcf.mr_low_res_mode_info = NULL; +#endif + res = VPX_CODEC_MEM_ERROR; + } } } return res; } static vpx_codec_err_t vp8e_destroy(vpx_codec_alg_priv_t *ctx) { #if CONFIG_MULTI_RES_ENCODING --- a/vpx/src/vpx_encoder.c +++ b/vpx/src/vpx_encoder.c @@ -109,16 +109,19 @@ vpx_codec_err_t vpx_codec_enc_init_multi mr_cfg.mr_down_sampling_factor.num = dsf->num; mr_cfg.mr_down_sampling_factor.den = dsf->den; ctx->iface = iface; ctx->name = iface->name; ctx->priv = NULL; ctx->init_flags = flags; ctx->config.enc = cfg; + // ctx takes ownership of mr_cfg.mr_low_res_mode_info if and only if + // this call succeeds. The first ctx entry in the array is + // responsible for freeing the memory. res = ctx->iface->init(ctx, &mr_cfg); } if (res) { const char *error_detail = ctx->priv ? ctx->priv->err_detail : NULL; /* Destroy current ctx */ ctx->err_detail = error_detail; vpx_codec_destroy(ctx);