Low firefox Integer Overflow 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionInteger overflow in the Audio/Video: Playback component
ComponentDOM
Bug ClassInteger Overflow
Tracker2050477
Fix commitff01a9ab9dda (firefox) +21/-17
CISA KEVNot listed
Credited5up3rh3i
Disclosed2026-07-21

Background

`WebMDemuxer`
the Gecko component that parses a WebM container and extracts track metadata such as frame and crop dimensions.
`IsValidVideoRegion`
a validation helper that checks a frame size, picture rectangle, and display size are within safe bounds before video-frame creation.
`CheckedInt`
a Mozilla integer wrapper type that detects overflow on arithmetic and exposes validity via isValid().
Crop values
container-supplied crop_left, crop_right, crop_top, and crop_bottom offsets that shrink the coded frame into the displayed picture region.

Root Cause Analysis

In WebMDemuxer::ReadMetadata, the horizontal and vertical crop totals were computed as static_cast<uint64_t>(params.crop_left) + params.crop_right and the vertical equivalent, then compared against params.width/params.height; while the addition itself was widened, the resulting pictureRect was populated from params.width - cropH without robustly rejecting hostile crop combinations, so an attacker-controlled WebM could drive the picture rectangle to values inconsistent with the frame. The invariant that the picture region must lie fully inside the frame (aPicture.x >= 0, aPicture.XMost() <= aFrame.width, and the vertical equivalents) was not enforced, letting a picture rect escape the frame bounds and feed later aPicture.width * aPicture.height arithmetic that could overflow int32_t.

The fix converts the crop sums to CheckedInt<uint32_t> and continues (dropping the track) whenever the sum overflows or meets/exceeds the frame dimension, and constructs pictureRect from validated croppedWidth/croppedHeight. IsValidVideoRegion is simultaneously hardened to reject negative aPicture.x/aPicture.y and to require XMost() <= aFrame.width and YMost() <= aFrame.height, so the picture rectangle is proven to sit inside the frame before any multiplication.

Key insight
The core mistake was trusting container-reported crop offsets to yield an in-bounds picture rectangle without checked arithmetic or a containment check against the frame. The fix uses CheckedInt to reject overflowing crop sums and adds explicit lower-bound and XMost/YMost containment checks so no out-of-range picture region reaches size-computing code.

Attack Path

  1. Craft malicious WebM An attacker authors a WebM file with a video track whose crop_left/crop_right or crop_top/crop_bottom fields are chosen to overflow or exceed the frame dimensions.
  2. Deliver to victim The file is served over the web or embedded so the victim’s browser demuxes it via WebMDemuxer::ReadMetadata.
  3. Bypass weak crop check The old crop-sum comparison and unbounded pictureRect population let a picture rectangle be built that is inconsistent with or outside the coded frame.
  4. Reach overflowing arithmetic The malformed picture rect flows into aPicture.width * aPicture.height and related size math, where int32_t multiplication can overflow.

Impact Assessment

An attacker gains an integer overflow in video-region size computation during WebM playback, running in the content or media-handling process that demuxes untrusted media. The precondition is only that the victim loads or plays an attacker-supplied WebM file; per the metadata this is rated low severity/impact, consistent with a validation-hardening fix rather than a demonstrated memory-corruption primitive.

Changed Functions

FunctionChangeNotes
if
dom/media/webm/WebMDemuxer.cpp
modified

Files Changed

  • dom/media/VideoUtils.cpp
  • dom/media/webm/WebMDemuxer.cpp

Audit Directions

  • Container-derived dimensions
    Audit every demuxer path (MP4, Ogg, etc.) that computes crop, offset, or picture regions from untrusted metadata for the same missing bounds and overflow checks.
  • Unchecked integer arithmetic
    Search for +/* on parsed media fields not wrapped in CheckedInt, especially where the result feeds allocation or int32_t sizes.
  • Region containment invariants
    Verify that any picture-rect-versus-frame validation enforces both lower bounds and XMost/YMost containment, not just upper MAX_DIMENSION limits.
diff --git a/dom/media/VideoUtils.cpp b/dom/media/VideoUtils.cpp
index bfab6e2d663..4e45a9be4b8 100644
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -228,13 +228,14 @@ bool IsValidVideoRegion(const gfx::IntSize& aFrame,
          aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
          aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
          aPicture.width > 0 &&
-         aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
+         aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION && aPicture.x >= 0 &&
          aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
          aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
-         aPicture.height > 0 &&
+         aPicture.XMost() <= aFrame.width && aPicture.height > 0 &&
          aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
-         aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
+         aPicture.y >= 0 && aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
          aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
+         aPicture.YMost() <= aFrame.height &&
          aPicture.width * aPicture.height <=
              MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
          aDisplay.width > 0 &&
diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp
index dfa3a39a28f..df0aa9090ef 100644
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -20,6 +20,7 @@
 #include "XiphExtradata.h"
 #include "gfx2DGlue.h"
 #include "gfxUtils.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/SharedThreadPool.h"
@@ -526,26 +527,28 @@ nsresult WebMDemuxer::ReadMetadata() {
 
       mInfo.mVideo.mHDRMetadata = ParseWebMMasteringMetadata(params);
 
-      // Picture region, taking into account cropping, before scaling
-      // to the display size. Default to the full frame and apply cropping only
-      // when it leaves a non-empty region within the frame.
-      gfx::IntRect pictureRect(0, 0, AssertedCast<int32_t>(params.width),
-                               AssertedCast<int32_t>(params.height));
-      uint64_t cropH =
-          static_cast<uint64_t>(params.crop_left) + params.crop_right;
-      uint64_t cropV =
-          static_cast<uint64_t>(params.crop_top) + params.crop_bottom;
-      if (cropH < params.width && cropV < params.height) {
-        pictureRect.x = AssertedCast<int32_t>(params.crop_left);
-        pictureRect.y = AssertedCast<int32_t>(params.crop_top);
-        pictureRect.width = AssertedCast<int32_t>(params.width - cropH);
-        pictureRect.height = AssertedCast<int32_t>(params.height - cropV);
+      CheckedInt<uint32_t> cropH =
+          CheckedInt<uint32_t>(params.crop_left) + params.crop_right;
+      CheckedInt<uint32_t> cropV =
+          CheckedInt<uint32_t>(params.crop_top) + params.crop_bottom;
+      if (!cropH.isValid() || !cropV.isValid() ||
+          cropH.value() >= params.width || cropV.value() >= params.height) {
+        WEBM_DEBUG("Invalid crop values left: {} right: {} top: {} bottom: {}",
+                   params.crop_left, params.crop_right, params.crop_top,
+                   params.crop_bottom);
+        continue;
       }
 
       // Validate the container-reported frame and pictureRect sizes. This
       // ensures that our video frame creation code doesn't overflow.
       gfx::IntSize displaySize(params.display_width, params.display_height);
       gfx::IntSize frameSize(params.width, params.height);
+      const uint32_t croppedWidth = params.width - cropH.value();
+      const uint32_t croppedHeight = params.height - cropV.value();
+      gfx::IntRect pictureRect(AssertedCast<int32_t>(params.crop_left),
+                               AssertedCast<int32_t>(params.crop_top),
+                               AssertedCast<int32_t>(croppedWidth),
+                               AssertedCast<int32_t>(croppedHeight));
       if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
         // Video track's frame sizes will overflow. Ignore the video track.
         continue;
Loading diff…