Medium firefox Integer Overflow 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionInteger overflow in the Graphics: ImageLib component
ComponentImageLib
Bug ClassInteger Overflow
Tracker2052703
Fix commit280012e9acbc (firefox) +1/-1
CISA KEVNot listed
CreditedKai Martin
Disclosed2026-07-21

Background

JPEG XL (JXL)
A raster image codec whose Firefox decoder lives in nsJXLDecoder.cpp within the ImageLib component.
`AllocateFrameBuffers`
The nsJXLDecoder method that sizes and allocates per-frame pixel buffers before decoded samples are written into them.
`PixelFormat::Cmyk8`
An 8-bit-per-channel CMYK pixel format that requires a separate mKBuffer to hold the K (black) channel.
Integer overflow
An arithmetic result that exceeds the range of its operand type and wraps to a smaller value, here corrupting an allocation size.

Root Cause Analysis

In nsJXLDecoder::AllocateFrameBuffers, when the decoded image uses PixelFormat::Cmyk8, the K-channel buffer was sized by evaluating size.width * size.height and passing the result to mKBuffer.resize. Because size.width and size.height are narrow (32-bit) integer types, their product is computed in 32-bit arithmetic and can wrap around for attacker-controlled large dimensions, so resize allocates a buffer far smaller than width * height bytes. The invariant that the K-channel buffer must hold one byte per pixel was thus violated, setting up a subsequent heap buffer overflow when the full-resolution K channel is decoded into the undersized allocation.

The fix casts one operand to size_t (size_t(size.width) * size.height), forcing the multiplication into 64-bit (pointer-width) arithmetic so the product no longer overflows and resize receives the true pixel count.

Key insight
The single mistake was performing the pixel-count multiplication in 32-bit integer arithmetic before widening; the fix widens one operand to size_t first so the entire product is computed at full width and the allocation size is correct.

Attack Path

  1. Craft a malicious JXL An attacker builds a JPEG XL image declaring the Cmyk8 pixel format and dimensions whose width * height overflows 32 bits.
  2. Deliver to victim The image is served on a web page or otherwise loaded so Firefox’s ImageLib routes it to nsJXLDecoder.
  3. Undersized allocation AllocateFrameBuffers computes the wrapped 32-bit product and mKBuffer.resize allocates a buffer far smaller than the real pixel count.
  4. Out-of-bounds write As the K channel is decoded, per-pixel writes run past the end of the undersized mKBuffer, corrupting adjacent heap memory.

Impact Assessment

An attacker who gets a victim to load a crafted JXL image achieves a heap buffer overflow in the image-decoding path, which runs in the content/renderer process. The only precondition is that the target processes the malicious image; the CVE metadata rates the impact moderate/medium with no exploit primitive claimed beyond the memory-safety defect.

Files Changed

  • image/decoders/nsJXLDecoder.cpp

Audit Directions

  • Width-narrow size arithmetic
    Search other decoders and buffer allocators for width * height (or similar dimension products) fed to resize/malloc/Allocate without a size_t cast on at least one operand.
  • Per-format buffer paths
    Review format-specific branches (e.g. CMYK, alpha, ancillary channels) that allocate auxiliary buffers, since these secondary sizing computations are easy to miss during the main-buffer audit.
  • Attacker-controlled dimensions
    Treat image header width/height as untrusted and confirm every allocation derived from them is bounded and computed in a type wide enough to hold the maximum product.
diff --git a/image/decoders/nsJXLDecoder.cpp b/image/decoders/nsJXLDecoder.cpp
index 3cfaf0fd1bb..ed687b6e782 100644
--- a/image/decoders/nsJXLDecoder.cpp
+++ b/image/decoders/nsJXLDecoder.cpp
@@ -418,7 +418,7 @@ nsresult nsJXLDecoder::AllocateFrameBuffers() {
   }
 
   if (mPixelFormat.value() == PixelFormat::Cmyk8 &&
-      !mKBuffer.resize(size.width * size.height)) {
+      !mKBuffer.resize(size_t(size.width) * size.height)) {
     return NS_ERROR_FAILURE;
   }
 
Loading diff…