CVE-2026-79231
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifmedia/ffmpeg/ffmpeg_common.cc |
modified | |
TEST_Pmedia/filters/audio_file_reader_unittest.cc |
modified |
Files Changed
DEPSmedia/ffmpeg/ffmpeg_common.ccmedia/filters/audio_file_reader_unittest.ccmedia/test/pipeline_integration_test.ccthird_party/ffmpeg
Patch
From 493d2dfa61ffb1e9cf218c24e356f31e1b76b66a Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <eugene@chromium.org>
Date: Thu, 16 Jul 2026 14:17:41 -0700
Subject: [PATCH] Roll ffmpeg for M152
Fix tests and glue code to account for newly introduced in ffmpeg AAC
initial_padding
Bug: 527501449, 504356442, 403330011
Change-Id: I3471e8e4621c037958316ebc09cd38dd59ce110f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8066846
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1663509}
---
diff --git a/DEPS b/DEPS
index 706eb209..8c596f1 100644
--- a/DEPS
+++ b/DEPS
@@ -523,7 +523,7 @@
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling ffmpeg
# and whatever else without interference from each other.
- 'ffmpeg_revision': 'fcf00a65aa4168c2ae1ce6e55f59e8bc1edd6c78',
+ 'ffmpeg_revision': '2b68d2babae73714846961fb0ee47e3b3d2e39a9',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling webpagereplay
# and whatever else without interference from each other.
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc
index e5ebfb17..280dce68 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -443,10 +443,21 @@
.copy_from_nonoverlapping(AVCodecContextExtraDataToSpan(codec_context));
}
+ // FFmpeg exports AAC edit list padding in
+ // AVCodecParameters::initial_padding, which propagates to
+ // codec_context->delay. AAC does not have a pipeline decoder delay, and
+ // this padding is already discarded using container-level discard padding.
+ // Pass 0 here to prevent AudioDiscardHelper from treating it as decoder
+ // delay and failing.
+ int codec_delay = codec_context->delay;
+ if (codec == AudioCodec::kAAC) {
+ codec_delay = 0;
+ }
+
config->Initialize(codec, sample_format,
{channel_layout, codec_context->ch_layout.nb_channels},
codec_context->sample_rate, extra_data, encryption_scheme,
- seek_preroll, codec_context->delay);
+ seek_preroll, codec_delay);
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
// These are bitstream formats unknown to ffmpeg, so they don't have
diff --git a/media/filters/audio_file_reader_unittest.cc b/media/filters/audio_file_reader_unittest.cc
index d138e450..0432e78 100644
--- a/media/filters/audio_file_reader_unittest.cc
+++ b/media/filters/audio_file_reader_unittest.cc
@@ -270,8 +270,8 @@
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
TEST_P(AudioFileReaderTest, AAC) {
- RunTest("sfx.m4a", "2.47,2.30,2.45,2.80,3.06,3.56,", 1, 44100,
- base::Microseconds(347665), 15333, 12719);
+ RunTest("sfx.m4a", "0.79,2.31,4.15,4.92,4.04,1.44,", 1, 44100,
+ base::Microseconds(347665), 15333, 12701);
}
TEST_P(AudioFileReaderTest, AAC_SinglePacket) {
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 03bf168a..66962cc 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -2060,20 +2060,9 @@
Play();
ASSERT_TRUE(WaitUntilOnEnded());
- // Verify preroll is stripped. This file uses a preroll of 2112 frames, which
- // spans all three packets in the file. Postroll is not correctly stripped at
- // present; see the note below.
- EXPECT_AUDIO_HASH("3.84,4.25,4.33,3.58,3.27,3.16,");
-
- // Note the above hash is incorrect since the <audio> path doesn't properly
- // trim trailing silence at end of stream for AAC decodes. This isn't a huge
- // deal since plain src= tags can't splice streams and MSE requires an
- // explicit append window for correctness.
- //
- // The WebAudio path via AudioFileReader computes this correctly, so the hash
- // below is taken from that test.
- //
- // EXPECT_AUDIO_HASH("3.77,4.53,4.75,3.48,3.67,3.76,");
+ // Verify preroll and postroll are correctly stripped. This file uses a
+ // preroll of 2112 frames, which spans all three packets in the file.
+ EXPECT_AUDIO_HASH("3.77,4.53,4.75,3.48,3.67,3.76,");
}
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
diff --git a/third_party/ffmpeg b/third_party/ffmpeg
index fcf00a6..2b68d2b 160000
--- a/third_party/ffmpeg
+++ b/third_party/ffmpeg
@@ -1 +1 @@
-Subproject commit fcf00a65aa4168c2ae1ce6e55f59e8bc1edd6c78
+Subproject commit 2b68d2babae73714846961fb0ee47e3b3d2e39a9
Regression Test / PoC
diff --git a/media/filters/audio_file_reader_unittest.cc b/media/filters/audio_file_reader_unittest.cc
index d138e450..0432e78 100644
--- a/media/filters/audio_file_reader_unittest.cc
+++ b/media/filters/audio_file_reader_unittest.cc
@@ -270,8 +270,8 @@
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
TEST_P(AudioFileReaderTest, AAC) {
- RunTest("sfx.m4a", "2.47,2.30,2.45,2.80,3.06,3.56,", 1, 44100,
- base::Microseconds(347665), 15333, 12719);
+ RunTest("sfx.m4a", "0.79,2.31,4.15,4.92,4.04,1.44,", 1, 44100,
+ base::Microseconds(347665), 15333, 12701);
}
TEST_P(AudioFileReaderTest, AAC_SinglePacket) {
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 03bf168a..66962cc 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -2060,20 +2060,9 @@
Play();
ASSERT_TRUE(WaitUntilOnEnded());
- // Verify preroll is stripped. This file uses a preroll of 2112 frames, which
- // spans all three packets in the file. Postroll is not correctly stripped at
- // present; see the note below.
- EXPECT_AUDIO_HASH("3.84,4.25,4.33,3.58,3.27,3.16,");
-
- // Note the above hash is incorrect since the <audio> path doesn't properly
- // trim trailing silence at end of stream for AAC decodes. This isn't a huge
- // deal since plain src= tags can't splice streams and MSE requires an
- // explicit append window for correctness.
- //
- // The WebAudio path via AudioFileReader computes this correctly, so the hash
- // below is taken from that test.
- //
- // EXPECT_AUDIO_HASH("3.77,4.53,4.75,3.48,3.67,3.76,");
+ // Verify preroll and postroll are correctly stripped. This file uses a
+ // preroll of 2112 frames, which spans all three packets in the file.
+ EXPECT_AUDIO_HASH("3.77,4.53,4.75,3.48,3.67,3.76,");
}
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
Original Bug Report
Potential Heap OOB write in FFmpeg FLAC RISC-V Vector kernels via loop underflow
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 go/chrome-ai-generated-security-bugs-faq for more information.
Overview: The RISC-V Vector (RVV) FLAC LPC kernels in FFmpeg fail to guard against cases where the number of samples equals the predictor order. This leads to an immediate loop counter underflow and a sequential heap out-of-bounds write in the renderer process. The issue is reachable via standard web audio APIs on supported hardware.
Affected files:
third_party/ffmpeg/libavcodec/riscv/flacdsp_rvv.Sthird_party/ffmpeg/libavcodec/flacdec.cthird_party/ffmpeg/libavcodec/riscv/flacdsp_init.c
Estimated timestamp from git blame: 2024-05-27
Description
A potential heap out-of-bounds (OOB) write vulnerability exists in the RISC-V Vector (RVV) implementation of FLAC LPC (Linear Predictive Coding) reconstruction kernels within Chromium’s FFmpeg copy.
In third_party/ffmpeg/libavcodec/riscv/flacdsp_rvv.S, the DSP kernels (e.g., ff_flac_lpc16_rvv) calculate their loop iteration count by subtracting the predictor order (pred_order) from the total length (len). The kernels use a do-while loop structure without an initial guard to ensure len > pred_order. If len == pred_order, the loop counter initializes to 0, immediately underflows to -1 (0xFFFFFFFFFFFFFFFF) inside the loop, and continues for approximately 2^64 iterations, causing a massive sequential OOB read-modify-write across the heap.
Root Cause Analysis
There are two interacting flaws that make this reachable:
- Permissive Check in Decoder: In
third_party/ffmpeg/libavcodec/flacdec.c, the functiondecode_residualsverifies the predictor order against the number of samples in a partition:if (pred_order > samples) { ... return AVERROR_INVALIDDATA; }(line 270). This check uses strict inequality. If a crafted frame setsblocksizeto 32,pred_orderto 32, andrice_orderto 0, thensamplesevaluates to 32. The check passes because 32 is not greater than 32. - Missing Guard in Assembly: The execution proceeds to call the DSP functions (e.g.,
s->dsp.lpc16) passings->blocksize(32) as thelenargument and 32 aspred_order. Inflacdsp_rvv.S(line 23,ff_flac_lpc16_rvv):Unlike the C and x86/ARM assembly implementations which safely skip the loop ifsub a4, a4, a2 // a4 (counter) = 32 - 32 = 0 ... 1: // ... loop body ... addi a4, a4, -1 // a4 underflows to -1 // ... out of bounds memory write occurs here ... bnez a4, 1b // Branches back because -1 != 0len <= pred_order, the RVV implementation falls straight through.
Potential Impact & Exploitability
This is a classic ‘wild copy’ vulnerability occurring in the sandboxed renderer process when processing media via the <audio> tag or WebCodecs. While the enormous loop will inevitably attempt to access an unmapped page and trigger a SIGSEGV, the sequential writing takes measurable CPU time.
An attacker could potentially exploit this by running a concurrent Web Worker. By grooming the heap layout prior to the OOB write, the Web Worker could race the crash window to hijack corrupted Javascript objects or function pointers before the kernel terminates the process, potentially leading to Remote Code Execution (RCE) within the sandbox. Because the corruption occurs in uninstrumented third-party assembly via raw pointers, MiraclePtr (BackupRefPtr) provides no protection.
Potential Reproduction Steps
(Note: These are suggested steps based on static analysis; our tooling agent cannot execute code to verify.)
- Use a Chromium build for
riscv64with RVV support enabled (e.g., in QEMU with-cpu rv64,v=true). - Craft a FLAC audio file containing a frame where:
blocksizeis explicitly set to 32.- A subframe is type LPC with a prediction order of 32.
- The subframe’s residual data uses a
rice_orderof 0.
- Host the file and load it in the renderer using an HTML5
<audio>tag. - The
ff_flac_lpc16_rvvfunction will trigger the massive loop, ultimately causing a renderer crash (SIGSEGV), demonstrating the memory corruption.
Suggested Fix
- Assembly Guard: Update the RVV kernels in
third_party/ffmpeg/libavcodec/riscv/flacdsp_rvv.Sto include a branch instruction before the loop to exit early if the calculated iteration count is zero or negative (e.g.,blez a4, end). - Upstream FFmpeg: Ensure this fix is contributed back to upstream FFmpeg.
- Decoder Check (Optional but recommended): Re-evaluate the logic in
flacdec.cto determine ifpred_order == samplesis semantically valid for all partitions. If it implies no residuals are present, skipping the DSP call entirely in C might be safer.
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.