Critical CVSS 5.4 firefox UAF 🔧 Commit mapped

Overview

Critical
Severity
5.4
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactcritical
DescriptionA double-free could have occurred in `vpx_codec_enc_init_multi` after a failed allocation when initializing the encoder for WebRTC. This could have caused memory corruption and a potentially exploitable crash.
ComponentMedia
Bug ClassUAF
Tracker1962421
Fix commit0aafd05b7bb3 (firefox) +14/-1
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
CISA KEVNot listed
CreditedRandell Jesup
Disclosed2025-05-27

Background

libvpx multi-resolution encoding
libvpx (bundled under media/libvpx) can encode multiple resolutions of the same stream; vpx_codec_enc_init_multi_ver initializes an array of encoder contexts, one per resolution.
`mr_cfg` / `mr_low_res_mode_info`
The multi-resolution config passed to each encoder’s init. mr_low_res_mode_info is a heap buffer of mode information shared across the resolution contexts; exactly one context is meant to own and later free it.
`vpx_codec_destroy`
Tears down an encoder instance and frees resources it owns, including mr_low_res_mode_info when the instance is the owning (first) context.

Root Cause Analysis

Ownership of the shared mr_low_res_mode_info buffer was transferred into each encoder context’s priv->oxcf as part of set_vp8e_config, but vp8e_init did not clear that ownership when encoder creation failed. When vp8_create_compressor(&priv->oxcf) returned NULL (a failed allocation), the old code simply set res = VPX_CODEC_MEM_ERROR and left priv->oxcf.mr_low_res_mode_info pointing at the shared buffer with mr_total_resolutions/mr_encoder_id still indicating this context owned/freed it. Control returned to vpx_codec_enc_init_multi_ver, which — on the failure path — tears the contexts down. Both the failed context (via its stale ownership fields) and the real owner could end up freeing the same mr_low_res_mode_info pointer, producing a double free (CWE-416) and heap corruption.

The patch makes the failed context relinquish ownership. On !priv->cpi, under #if CONFIG_MULTI_RES_ENCODING, it zeroes priv->oxcf.mr_total_resolutions = 0, priv->oxcf.mr_encoder_id = 0, and priv->oxcf.mr_low_res_mode_info = NULL before setting the error, so a subsequent vpx_codec_destroy on this instance will not free the shared buffer. The companion comment added to vpx_codec_enc_init_multi_ver codifies the invariant: a ctx takes ownership of mr_cfg.mr_low_res_mode_info only if its init succeeds, and the first ctx entry is responsible for freeing it. The advisory describes this as a double-free after a failed allocation; the diff substantiates exactly that — a freed-memory/ownership-confusion lifetime bug (double free), a member of the use-after-free class (CWE-416).

Key insight
A failed VP8 encoder init left the dead context still claiming ownership of the shared mr_low_res_mode_info buffer, so teardown freed that one buffer twice; the fix nulls the ownership fields on the failure path so only the surviving owner frees it.

Attack Path

  1. Attacker Establishes a WebRTC session (or otherwise drives multi-resolution VP8 encoder init) with the victim’s Firefox, negotiating parameters that use multi-resolution encoding. (UI:R per the vector — victim accepts/engages the call)
  2. Allocation failure Induces or races a memory-allocation failure in vp8_create_compressor during one resolution context’s init (e.g. via memory pressure or crafted large parameters).
  3. Stale ownership vp8e_init returns VPX_CODEC_MEM_ERROR with priv->oxcf.mr_low_res_mode_info still set and ownership fields non-zero.
  4. Double free vpx_codec_enc_init_multi_ver’s failure teardown frees the shared mr_low_res_mode_info via this dead context, and again via the real owner.
  5. Primitive Double free / heap corruption in the media encoder; “a potentially exploitable crash” per the advisory (escalation to code execution inferred, not shown by the diff).

Impact Assessment

Double-free / heap memory corruption in the WebRTC VP8 media-encoding path. It fires on the allocation-failure branch of multi-resolution encoder init, so it requires reaching encoder initialization (a WebRTC call, UI:R) and provoking an allocation failure — a meaningful precondition, consistent with the moderate CVSS despite the critical bug-class label. The demonstrated primitive is a double free of a heap buffer; exploitation for arbitrary code execution is the advisory’s presumption and is not proven by the diff (inferred). The encoder typically runs in a media/utility context.

Changed Functions

FunctionChangeNotes
if
media/libvpx/libvpx/vp8/vp8_cx_iface.c
modified

Files Changed

  • media/libvpx/libvpx/vp8/vp8_cx_iface.c
  • media/libvpx/libvpx/vpx/src/vpx_encoder.c

Audit Directions

  • Error paths in encoder/decoder init
    Audit other libvpx (and other bundled codec) init callbacks for shared buffers whose ownership is set up before a fallible allocation and not relinquished on failure.
  • Partial-init teardown
    Review vpx_codec_destroy and the multi-init failure teardown for other members freed per-context that are actually shared.
diff --git a/media/libvpx/libvpx/vp8/vp8_cx_iface.c b/media/libvpx/libvpx/vp8/vp8_cx_iface.c
index 668bd0816dc..e5d816cef36 100644
--- a/media/libvpx/libvpx/vp8/vp8_cx_iface.c
+++ b/media/libvpx/libvpx/vp8/vp8_cx_iface.c
@@ -732,7 +732,17 @@ static vpx_codec_err_t vp8e_init(vpx_codec_ctx_t *ctx,
 
       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;
+      }
     }
   }
 
diff --git a/media/libvpx/libvpx/vpx/src/vpx_encoder.c b/media/libvpx/libvpx/vpx/src/vpx_encoder.c
index 001d854abe9..3af4cea3a70 100644
--- a/media/libvpx/libvpx/vpx/src/vpx_encoder.c
+++ b/media/libvpx/libvpx/vpx/src/vpx_encoder.c
@@ -114,6 +114,9 @@ vpx_codec_err_t vpx_codec_enc_init_multi_ver(
           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);
         }
 
Loading diff…