CVE-2026-87497
Overview
Background
- Buffered-image mode
- a libjpeg decode mode (
info_.buffered_image) that lets a multi-scan JPEG be re-read scan-by-scan and progressively output; the decoder enters it via thekJpegDecompressBufferedImagestate (renamed fromkJpegDecompressProgressive). - `coef_bits`
- a libjpeg field that is non-null only for progressive JPEGs (it tracks per-component coefficient bit positions); for non-progressive multi-scan files (non-interleaved sequential or lossless) it is always null.
- `all_components_seen`
- an inline lambda that decides whether every component’s scan has been received, gating the call to
jpeg_start_output()and the emission of output pixels. - `jpeg_input_complete`
- a libjpeg predicate that returns true only once the entire compressed input has been parsed.
Root Cause Analysis
The vulnerable code lives in the kJpegDecompressBufferedImage case (formerly kJpegDecompressProgressive) of the JPEG decoder state machine in jpeg_image_decoder.cc. The gating lambda all_components_seen checked info.coef_bits and, when it iterated the progressive coefficient array without finding an unseen component, fell through to return true; crucially, when info.coef_bits was null — the non-progressive, non-interleaved multi-scan case — the if body was skipped entirely and the function still returned true. As the removed comment path shows, this meant all_components_seen() reported completion on the very first scan, before the remaining component scans had arrived, so the decoder invoked jpeg_start_output() and output pixel buffers for components that had never been parsed — i.e. uninitialized memory.
The fix tightens the check: for the coef_bits == null branch it now returns jpeg_input_complete(&info) instead of an unconditional true, deferring any output until the whole input is parsed and every component scan is complete.
The added regression test nonInterleavedSequentialBufferedDecompress confirms that a half-supplied non-interleaved JPEG now yields neither kFramePartial nor kFrameComplete until the full buffer is provided.
all_components_seen treated a null info.coef_bits (the non-progressive case) as “all components seen” by unconditionally returning true, when in fact null coef_bits gives it no completion signal at all; the fix substitutes jpeg_input_complete(&info) so completion is asserted only when the input is genuinely finished.Attack Path
- Serve a crafted JPEG
An attacker hosts a non-interleaved multi-scan (sequential or lossless) JPEG, which the decoder routes into buffered-image mode (
info_.buffered_imagetrue, enteringkJpegDecompressBufferedImage) whereinfo.coef_bitsis null. - Deliver only a partial scan
The image is streamed/decoded while only the first component’s scan has been parsed and later component scans are still absent;
jpeg_input_completewould still be false at this point. - Premature completion signal
all_components_seenskips itscoef_bitsloop and returnstrue, so the decoder callsjpeg_start_output()and emits output for components whose scans were never decoded. - Uninitialized memory in output
The produced
ImageFramecontains uninitialized buffer contents for the unparsed components, matching the advisory’s “uninitialized resource in Codecs.” Any further disclosure of that memory to script (e.g. via canvas readback) or downstream escalation is(inferred)— the diff shows only the premature output, not a read primitive.
Impact Assessment
(inferred) and not demonstrated by the patch.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc |
modified | |
forthird_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc |
modified | |
TESTthird_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc |
modified |
Files Changed
third_party/blink/web_tests/platform/win/images/12-55-expected.pngthird_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.pngthird_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.pngthird_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.pngthird_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.ccthird_party/blink/web_tests/platform/linux/images/12-55-expected.pngthird_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.pngthird_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.pngthird_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/12-55-expected.pngthird_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.pngthird_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.pngthird_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.pngthird_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.pngthird_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.pngthird_party/blink/web_tests/platform/mac/images/12-55-expected.pngthird_party/blink/web_tests/platform/mac/images/182-expected.pngthird_party/blink/web_tests/platform/win/images/182-expected.pngthird_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.pngthird_party/blink/web_tests/platform/linux/images/182-expected.pngthird_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.pngthird_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.pngthird_party/blink/web_tests/platform/linux/images/23-55-expected.pngthird_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.pngthird_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.pngthird_party/blink/web_tests/platform/win/images/23-55-expected.pngthird_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/182-expected.pngthird_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.pngthird_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.pngthird_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/23-55-expected.pngthird_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.pngthird_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.ccthird_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.pngthird_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.pngthird_party/blink/web_tests/platform/mac/images/23-55-expected.png
Audit Directions
- Completion gates on optional/null metadataAudit any decoder logic that infers “fully received / all parts seen” from a field that is only populated in a subset of formats (like
coef_bitsfor progressive JPEGs); a null/absent field must not be silently treated as “complete.” - Buffered/incremental decode output pathsReview other codecs and image decoders that emit frames incrementally to confirm they gate output on a true input-complete predicate (e.g.
jpeg_input_complete-equivalent) rather than a per-scan heuristic, especially for uncommon subtypes (non-interleaved sequential, lossless). - Uninitialized output buffersLook for cases where output/pixel buffers are handed downstream before every contributing component or channel has been parsed, since these can leak uninitialized memory even without an explicit out-of-bounds access.
Patch
From 154727b1485f72b4a02bcb9b02c1ffd004170b2e Mon Sep 17 00:00:00 2001
From: Florin Malita <fmalita@chromium.org>
Date: Wed, 29 Jul 2026 08:41:42 -0700
Subject: [PATCH] Reland "Fix premature output of non-interleaved multi-scan JPEGs"
This reverts commit a4ddaf23c2cbb85af1b09a8c0435265d68129c34.
Reason for revert: reland with additional rebaselines
Original change's description:
> Revert "Fix premature output of non-interleaved multi-scan JPEGs"
>
> This reverts commit 191dfbeb4fd602cd0ee1dcbc9f401268675e3892.
>
> Reason for revert: Causing failures on Mac15 and Win11 bots.
>
> Failure Link: https://ci.chromium.org/ui/p/chromium/builders/ci/win11-arm64-rel-tests/8088/overview
> https://ci.chromium.org/p/chromium/builders/ci/mac15-x64-rel-tests/b8675028340104321601
>
> Original change's description:
> > Fix premature output of non-interleaved multi-scan JPEGs
> >
> > In spite of its name, the kJpegDecompressProgressive code path handles
> > both progressive and non-progressive multi-scan (non-interleaved
> > sequential or lossless) JPEGs. Technically it corresponds to enabling
> > buffered-image mode.
> >
> > Previously, all_components_seen() checked if info.coef_bits was
> > non-null, which covers the progressive case.
> >
> > For non-progressive files, info.coef_bits is null. Because of this,
> > all_components_seen() returned true on the very first scan before
> > subsequent component scans were received. This triggered premature calls
> > to jpeg_start_output() and output of uninitialized memory buffers for
> > the unparsed components.
> >
> > Resolve this by checking jpeg_input_complete() for the non-progressive
> > case. This safely postpones output generation until the entire input has
> > been parsed and all component scans are complete.
> >
> > Add a unit test nonInterleavedSequentialBufferedDecompress to verify
> > that sequential, non-interleaved JPEGs are not progressively decoded
> > while input is incomplete.
> >
> > Rename kJpegDecompressProgressive -> kJpegDecompressBufferedImage to
> > better reflect its semantics.
> >
> > Bug: 504670493
> > Test: blink_platform_unittests --gtest_filter=JPEGImageDecoderTest.*
> > Change-Id: I32a44452c2b74d259a3957176eb64f95159287fe
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8141869
> > Commit-Queue: Florin Malita <fmalita@chromium.org>
> > Reviewed-by: Wan-Teh Chang <wtc@google.com>
> > Cr-Commit-Position: refs/heads/main@{#1669371}
>
> Bug: 504670493, 539967091
> Bug: 504670493
> Change-Id: I5fa309dd6f2b827eb359db19dd9288a2be71418c
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8161825
> Reviewed-by: Wan-Teh Chang <wtc@google.com>
> Auto-Submit: Dibyajyoti Pal <dibyapal@chromium.org>
> Reviewed-by: Florin Malita <fmalita@chromium.org>
> Commit-Queue: Florin Malita <fmalita@chromium.org>
> Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
> Owners-Override: Dibyajyoti Pal <dibyapal@chromium.org>
> Commit-Queue: Dibyajyoti Pal <dibyapal@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1669683}
Bug: 504670493, 539967091
Bug: 504670493
Change-Id: I1122e1404f84ef80d57d0a90c7dc011060405a2c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8165123
Reviewed-by: Wan-Teh Chang <wtc@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1670283}
---
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
index 4d4b446a6..da1a69a4 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
@@ -168,8 +168,8 @@
enum jstate {
kJpegHeader, // Reading JFIF headers
kJpegStartDecompress,
- kJpegDecompressProgressive, // Output progressive pixels
- kJpegDecompressSequential, // Output sequential pixels
+ kJpegDecompressBufferedImage, // Output pixels in buffered-image mode.
+ kJpegDecompressSequential, // Output pixels in sequential mode.
kJpegDone
};
@@ -570,8 +570,8 @@
return false; // I/O suspension.
}
- // If this is a progressive JPEG ...
- state_ = (info_.buffered_image) ? kJpegDecompressProgressive
+ // If this is a JPEG requiring buffered-image mode.
+ state_ = (info_.buffered_image) ? kJpegDecompressBufferedImage
: kJpegDecompressSequential;
[[fallthrough]];
@@ -587,9 +587,9 @@
}
[[fallthrough]];
- case kJpegDecompressProgressive:
- if (state_ == kJpegDecompressProgressive) {
- auto all_components_seen = [](const jpeg_decompress_struct& info) {
+ case kJpegDecompressBufferedImage:
+ if (state_ == kJpegDecompressBufferedImage) {
+ auto all_components_seen = [](jpeg_decompress_struct& info) -> bool {
if (info.coef_bits) {
for (int c = 0; c < info.num_components; ++c) {
if (UNSAFE_TODO(info.coef_bits[c])[0] == -1) {
@@ -597,8 +597,12 @@
return false;
}
}
+ return true;
}
- return true;
+ // For non-progressive (e.g. non-interleaved sequential or lossless)
+ // images coef_bits is always null, and all components are only seen
+ // when the input is complete.
+ return jpeg_input_complete(&info);
};
int status = 0;
int first_scan_to_display =
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
index a9966f7..adab7cfc 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
@@ -259,6 +259,35 @@
EXPECT_TRUE(test_decoder->Failed());
}
+// This tests decoding a multi-scan non-interleaved sequential JPEG.
+// Decoding should fail with incomplete data.
+TEST(JPEGImageDecoderTest, nonInterleavedSequentialBufferedDecompress) {
+ Vector<char> test_data =
+ ReadFile("/images/jpeg-suite/baseline/32x32x8_ycbcr.jpg");
+ ASSERT_FALSE(test_data.empty());
+
+ const size_t partial_size = test_data.size() / 2;
+ scoped_refptr<SharedBuffer> partial_buffer = SharedBuffer::Create();
+ partial_buffer->Append(base::span<const char>(test_data).first(partial_size));
+
+ std::unique_ptr<ImageDecoder> test_decoder = CreateJPEGDecoder();
+ test_decoder->SetData(partial_buffer, false);
+
+ ImageFrame* frame = test_decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_NE(frame->GetStatus(), ImageFrame::kFramePartial);
+ EXPECT_NE(frame->GetStatus(), ImageFrame::kFrameComplete);
+ EXPECT_FALSE(test_decoder->Failed());
+
+ scoped_refptr<SharedBuffer> full_buffer = SharedBuffer::Create();
+ full_buffer->Append(base::span<const char>(test_data));
+ test_decoder->SetData(full_buffer, true);
+
+ frame = test_decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete);
+}
+
// Decode a JPEG with EXIF data that defines a density corrected size. The EXIF
// data has the initial IFD at the end of the data blob, and out-of-line data
// defined just after the header.
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png
index 253052e5..1b02678a 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png
index 335d4394..1b02678a 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/12-55-expected.png b/third_party/blink/web_tests/platform/linux/images/12-55-expected.png
index dbefbc5..5671bec 100644
--- a/third_party/blink/web_tests/platform/linux/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/182-expected.png b/third_party/blink/web_tests/platform/linux/images/182-expected.png
index 9341f9a60..5671bec 100644
--- a/third_party/blink/web_tests/platform/linux/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/23-55-expected.png b/third_party/blink/web_tests/platform/linux/images/23-55-expected.png
index 941256dc..5671bec 100644
--- a/third_party/blink/web_tests/platform/linux/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png
index 3a6bc1e..0be40aa 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png
index cc6a061..0be40aa 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png
index b54ebe5..0be40aa 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png
index 335d4394..1b02678a 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/12-55-expected.png
deleted file mode 100644
index 2e05869c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/12-55-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/182-expected.png
deleted file mode 100644
index 82ab4ee5..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/182-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/23-55-expected.png
deleted file mode 100644
index 91d3852..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/23-55-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png
index bbb54c3..9738f19 100644
--- a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png
index 8aa0b49..9738f19 100644
--- a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png
index 8cdcd02..9738f19 100644
--- a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac/images/12-55-expected.png
index ec38fe3f..7ed9d78bc 100644
--- a/third_party/blink/web_tests/platform/mac/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/182-expected.png b/third_party/blink/web_tests/platform/mac/images/182-expected.png
index 3609f33..7ed9d78bc 100644
--- a/third_party/blink/web_tests/platform/mac/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/mac/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac/images/23-55-expected.png
index f687a17..7ed9d78bc 100644
--- a/third_party/blink/web_tests/platform/mac/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png
index b3527d2..2d590a86 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png
index 95ab680..2d590a86 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png
index 8113274..2d590a86 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/12-55-expected.png b/third_party/blink/web_tests/platform/win/images/12-55-expected.png
index 3261260..3588010e 100644
--- a/third_party/blink/web_tests/platform/win/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/182-expected.png b/third_party/blink/web_tests/platform/win/images/182-expected.png
index eca92b1..3588010e 100644
--- a/third_party/blink/web_tests/platform/win/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/23-55-expected.png b/third_party/blink/web_tests/platform/win/images/23-55-expected.png
index 32fc3fc..3588010e 100644
--- a/third_party/blink/web_tests/platform/win/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png
index 4c599f97..b035de6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png
index 3ab08e4..b035de6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png
index 2f970193..b035de6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png
index b504399..389fb49 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png
index 329d9f12..389fb49 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png
index 6e3fed2..53edfcd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png
index 4bb6cc7..53edfcd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png
index ec7653d..53edfcd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
Regression Test / PoC
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
index a9966f7..adab7cfc 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
@@ -259,6 +259,35 @@
EXPECT_TRUE(test_decoder->Failed());
}
+// This tests decoding a multi-scan non-interleaved sequential JPEG.
+// Decoding should fail with incomplete data.
+TEST(JPEGImageDecoderTest, nonInterleavedSequentialBufferedDecompress) {
+ Vector<char> test_data =
+ ReadFile("/images/jpeg-suite/baseline/32x32x8_ycbcr.jpg");
+ ASSERT_FALSE(test_data.empty());
+
+ const size_t partial_size = test_data.size() / 2;
+ scoped_refptr<SharedBuffer> partial_buffer = SharedBuffer::Create();
+ partial_buffer->Append(base::span<const char>(test_data).first(partial_size));
+
+ std::unique_ptr<ImageDecoder> test_decoder = CreateJPEGDecoder();
+ test_decoder->SetData(partial_buffer, false);
+
+ ImageFrame* frame = test_decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_NE(frame->GetStatus(), ImageFrame::kFramePartial);
+ EXPECT_NE(frame->GetStatus(), ImageFrame::kFrameComplete);
+ EXPECT_FALSE(test_decoder->Failed());
+
+ scoped_refptr<SharedBuffer> full_buffer = SharedBuffer::Create();
+ full_buffer->Append(base::span<const char>(test_data));
+ test_decoder->SetData(full_buffer, true);
+
+ frame = test_decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete);
+}
+
// Decode a JPEG with EXIF data that defines a density corrected size. The EXIF
// data has the initial IFD at the end of the data blob, and out-of-line data
// defined just after the header.
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png
index 253052e5..1b02678a 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png
index 335d4394..1b02678a 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/12-55-expected.png b/third_party/blink/web_tests/platform/linux/images/12-55-expected.png
index dbefbc5..5671bec 100644
--- a/third_party/blink/web_tests/platform/linux/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/182-expected.png b/third_party/blink/web_tests/platform/linux/images/182-expected.png
index 9341f9a60..5671bec 100644
--- a/third_party/blink/web_tests/platform/linux/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/23-55-expected.png b/third_party/blink/web_tests/platform/linux/images/23-55-expected.png
index 941256dc..5671bec 100644
--- a/third_party/blink/web_tests/platform/linux/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png
index 3a6bc1e..0be40aa 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png
index cc6a061..0be40aa 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png
index b54ebe5..0be40aa 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png
index 335d4394..1b02678a 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/12-55-expected.png
deleted file mode 100644
index 2e05869c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/12-55-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/182-expected.png
deleted file mode 100644
index 82ab4ee5..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/182-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/23-55-expected.png
deleted file mode 100644
index 91d3852..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/23-55-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png
index bbb54c3..9738f19 100644
--- a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png
index 8aa0b49..9738f19 100644
--- a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png
index 8cdcd02..9738f19 100644
--- a/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac26-arm64/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac/images/12-55-expected.png
index ec38fe3f..7ed9d78bc 100644
--- a/third_party/blink/web_tests/platform/mac/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/182-expected.png b/third_party/blink/web_tests/platform/mac/images/182-expected.png
index 3609f33..7ed9d78bc 100644
--- a/third_party/blink/web_tests/platform/mac/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/mac/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac/images/23-55-expected.png
index f687a17..7ed9d78bc 100644
--- a/third_party/blink/web_tests/platform/mac/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png
index b3527d2..2d590a86 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png
index 95ab680..2d590a86 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png
index 8113274..2d590a86 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/12-55-expected.png b/third_party/blink/web_tests/platform/win/images/12-55-expected.png
index 3261260..3588010e 100644
--- a/third_party/blink/web_tests/platform/win/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/182-expected.png b/third_party/blink/web_tests/platform/win/images/182-expected.png
index eca92b1..3588010e 100644
--- a/third_party/blink/web_tests/platform/win/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/23-55-expected.png b/third_party/blink/web_tests/platform/win/images/23-55-expected.png
index 32fc3fc..3588010e 100644
--- a/third_party/blink/web_tests/platform/win/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png
index 4c599f97..b035de6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png
index 3ab08e4..b035de6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png
index 2f970193..b035de6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png
index b504399..389fb49 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png
index 329d9f12..389fb49 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/23-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png
index 6e3fed2..53edfcd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/12-55-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png
index 4bb6cc7..53edfcd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/182-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png
index ec7653d..53edfcd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/exotic-color-space/images/23-55-expected.png
Binary files differ
Original Bug Report
Uninitialized heap disclosure in libjpeg-turbo via progressive display
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential uninitialized heap disclosure exists in libjpeg-turbo’s lossless decoder when combined with Blink’s progressive image decoding. A premature watermark advancement in libjpeg-turbo’s memory manager, coupled with a flaw in Blink’s JPEGImageDecoder::all_components_seen(), allows an attacker to leak uninitialized PartitionAlloc heap memory to a canvas. This can be used as an ASLR-defeat primitive.
Affected files:
third_party/libjpeg_turbo/src/jddiffct.cthird_party/libjpeg_turbo/src/jmemmgr.cthird_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.ccthird_party/libjpeg_turbo/src/jdlhuff.cthird_party/libjpeg_turbo/src/jdcolor.c
Estimated timestamp from git blame: 2025-08-07
Summary
A potential uninitialized heap disclosure vulnerability exists in the libjpeg-turbo lossless decoder (used for SOF3 JPEGs) when combined with Blink’s progressive image decoding. A Time-of-Check Time-of-Use (TOCTOU) flaw in libjpeg-turbo’s memory management, combined with incomplete state tracking in Blink’s JPEGImageDecoder, allows an attacker to read renderer process PartitionAlloc heap memory via a <canvas> element.
Technical Details
The vulnerability arises from the interaction between libjpeg-turbo’s virtual array management and Blink’s progressive display logic:
- Premature Watermark Advancement: In
jddiffct.c, the lossless difference controller allocates virtual sample arrays withpre_zero=FALSE. When processing a scan,consume_data()callsaccess_virt_sarraywithwritable=TRUE. Insidejmemmgr.c:987, the memory manager immediately advances thefirst_undef_rowwatermark before any data is written to the buffer. - Suspension: If the input stream is truncated or stalls mid-row,
decompress_data()returnsJPEG_SUSPENDED. The current row is left completely unwritten, but is incorrectly marked as ‘defined’ by the memory manager because the watermark was already advanced. - Progressive Display Bypass: Back in Blink,
JPEGImageDecoder::Decodechecks if the partial image can be displayed progressively using theall_components_seenlambda (jpeg_image_decoder.cc:592). This lambda checksinfo.coef_bits. Because lossless JPEGs do not use DCT,coef_bitsis NULL. The lambda immediately returnstrue, causing Blink to bypass the check and force output of the incomplete scan viajpeg_start_output. - Uninitialized Read: During output generation,
output_data()(jddiffct.c:315) callsaccess_virt_sarraywithwritable=FALSEto read the stalled row. Because thefirst_undef_rowwatermark was advanced in Step 1, the memory manager does not throw aJERR_BAD_VIRTUAL_ACCESSerror. - Leak to Canvas:
output_datacopies the uninitialized PartitionAlloc heap memory into the output buffer. Blink’sOutputScanlinesthen writes this data into theImageFrame. An attacker can subsequently draw the partially decoded image to a canvas and usegetImageData()to read the leaked heap data.
Potential Attacker Steps to Trigger
Note: These are suggested steps based on code analysis; a working Proof of Concept has not been run.
- Serve a crafted, multi-scan 3-component SOF3 (lossless) JPEG.
- Provide full entropy for the first two scans.
- For the third scan, provide valid entropy for the first
Rrows, then artificially stall the TCP connection mid-way through rowR+1. - In the victim’s browser, load the image and draw it to a
<canvas>usingctx.drawImage(). - Call
ctx.getImageData()and inspect the color channels corresponding to the stalled scan of rowR+1. The resulting bytes will contain uninitialized heap memory.
Suggested Fix
- Blink: Fix the logic in
all_components_seen(jpeg_image_decoder.cc:592) so that it accurately tracks component availability for lossless JPEGs, rather than defaulting totruewheninfo.coef_bitsis NULL. - libjpeg-turbo: Either initialize the virtual arrays by setting
pre_zero=TRUEwhen allocating them injddiffct.c:392, or delay advancingfirst_undef_rowinjmemmgr.cuntil the caller signals that the write has actually completed.
Evaluated with Chrome root at commit: 7353d249d9cacf9c7218e1d7b8a39cf39c72d646
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.