Medium firefox Integer Overflow 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionInteger overflow in the Audio/Video component
ComponentDOM
Bug ClassInteger Overflow
Tracker2047221
Fix commit2ad690566b2f (firefox) +25/-0
CISA KEVNot listed
CreditedSteven Julian
Disclosed2026-07-21

Background

`MediaFormatReader`
Gecko’s format-agnostic media reader that demuxes and dispatches compressed samples to platform decoders.
`CryptoSample`
Per-sample encryption metadata carrying parallel mPlainSizes and mEncryptedSizes subsample arrays describing the clear/encrypted byte layout of an encrypted sample.
`CheckedInt<size_t>`
Mozilla’s overflow-detecting integer wrapper whose isValid() reports whether any arithmetic operation overflowed.
Subsample
A (clear, encrypted) byte-length pair used by CENC/CBCS encryption to split a single media sample into alternating plaintext and ciphertext regions.

Root Cause Analysis

The vulnerable path is MediaFormatReader::DecodeDemuxedSamples, which forwarded an encrypted sample’s subsample layout (crypto.mPlainSizes / crypto.mEncryptedSizes) toward the decoder without validating it against the actual sample buffer. The code assumed two invariants that were never enforced: that the plain and encrypted subsample arrays have equal length, and that the sum of all subsample sizes exactly equals aSample->Size(). Because these sizes come from attacker-influenced container/pssh metadata, a crafted stream could declare subsample totals that overflow when summed or that describe more bytes than the buffer holds, letting downstream code read past the sample’s real allocation.

The fix adds an explicit length-equality check plus a CheckedInt<size_t> accumulation of every plain and encrypted size, rejecting the sample with NS_ERROR_DOM_MEDIA_DECODE_ERR if the addition overflows (!isValid()) or if the total does not match aSample->Size(). This closes the gap by establishing the size invariant at the reader boundary before any decoder consumes the subsample map.

Key insight
The single mistake was trusting attacker-supplied subsample size arrays without verifying they are consistent and sum (without integer overflow) to the true sample size; the fix validates array-length parity and performs an overflow-checked total-vs-aSample->Size() comparison before dispatch.

Attack Path

  1. Craft encrypted media Author a media file whose encrypted samples carry malicious CryptoSample subsample metadata with mismatched array lengths or subsample sizes that overflow when summed.
  2. Deliver to victim Serve the file to a page that plays it via the standard media pipeline (e.g. EME/MSE playback).
  3. Reach the reader DecodeDemuxedSamples accepts the demuxed encrypted sample and, pre-fix, forwards the unvalidated subsample layout onward.
  4. Trigger overflow The summed subsample lengths overflow size_t or exceed the real buffer size, so downstream code operates on a length larger than the backing allocation.
  5. Out-of-bounds access The mismatched size drives reads beyond the sample buffer during crypto/decoder handoff.

Impact Assessment

An attacker who gets a victim to play a crafted encrypted media stream can trigger an integer overflow leading to out-of-bounds access on the media sample buffer, within the process running MediaFormatReader (the content/media decoding context). The precondition is media playback of attacker-supplied encrypted content; Mozilla rates the impact moderate/medium with no assigned CVSS in the metadata.

Files Changed

  • dom/media/MediaFormatReader.cpp

Audit Directions

  • Unvalidated parallel arrays
    Flag any use of paired metadata arrays (like mPlainSizes/mEncryptedSizes) where lengths are assumed equal without an explicit check.
  • Unchecked size summation
    Look for loops accumulating attacker-influenced sizes into a plain integer; require CheckedInt or equivalent overflow detection before the total is used as a length or offset.
  • Missing buffer-total invariant
    Audit media/crypto handoff paths where declared subsample or fragment totals are trusted without comparing against the real Size() of the underlying buffer.
diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp
index 02a8ddd0c19..533f6fcf063 100644
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -21,6 +21,7 @@
 #include "VideoUtils.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/CDMProxy.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/Preferences.h"
@@ -2100,6 +2101,30 @@ void MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
           aSample->mEOS ? " eos" : "");
 
   decoder.StartRecordDecodingPerf(aTrack, aSample);
+
+  const CryptoSample& crypto = aSample->mCrypto;
+  if (crypto.IsEncrypted() && !crypto.mPlainSizes.IsEmpty()) {
+    if (crypto.mPlainSizes.Length() != crypto.mEncryptedSizes.Length()) {
+      NotifyError(aTrack,
+                  MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
+                              "Mismatched crypto subsample array lengths"));
+      return;
+    }
+    CheckedInt<size_t> subsampleTotal = 0;
+    for (size_t i = 0; i < crypto.mPlainSizes.Length(); i++) {
+      subsampleTotal += crypto.mPlainSizes[i];
+      subsampleTotal += crypto.mEncryptedSizes[i];
+    }
+    if (!subsampleTotal.isValid() ||
+        subsampleTotal.value() != aSample->Size()) {
+      NotifyError(
+          aTrack,
+          MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
+                      "Crypto subsample sizes don't match sample size"));
+      return;
+    }
+  }
+
   if (aSample->mCrypto.IsEncrypted() &&
       (mMediaEngineId || (mCDMProxy && !!mCDMProxy->AsRemoteCDMProxy()))) {
     aSample->mShouldCopyCryptoToRemoteRawData = true;
Loading diff…