CVE-2026-11669
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifmedia/base/video_frame.cc |
modified |
Files Changed
media/base/video_frame.cc
Patch
From 3c569434034b9f87d61aee8ca4fc147e2f565704 Mon Sep 17 00:00:00 2001 From: Hirokazu Honda <[email protected]> Date: Tue, 26 May 2026 18:40:52 -0700 Subject: [PATCH] media/base: Use safe math in VideoFrame::GetVisibleDataInternal This CL updates VideoFrame::GetVisibleDataInternal() to use size_t for the local plane_stride (avoiding implicit truncation from stride()) and refactors offset and size calculations using base::CheckedNumeric to prevent signed integer wraparound/overflow. Bug: 515429352 Test: video.EncodeAccel.* Change-Id: Ia36795a360b501db578684ecd95423c23e59aa48 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7874236 Commit-Queue: Hirokazu Honda <[email protected]> Reviewed-by: Eugene Zemtsov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1636633} --- diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc index 64de9ae..b736fafb 100644 --- a/media/base/video_frame.cc +++ b/media/base/video_frame.cc @@ -1297,19 +1297,35 @@ base::bits::AlignDownDeprecatedDoNotUse( visible_rect_.y(), alignment.height())); - const int plane_stride = stride(plane); + const size_t plane_stride = stride(plane); const gfx::Size subsample = SampleSize(format(), plane); DCHECK(offset.x() % subsample.width() == 0); DCHECK(offset.y() % subsample.height() == 0); - const auto visible_plane_offset = base::checked_cast<size_t>( - // Row offset. - plane_stride * (offset.y() / subsample.height()) + - // Column offset. + + // Use CheckedNumeric for offset calculation to prevent overflow. + // Row offset. + base::CheckedNumeric<size_t> checked_offset = plane_stride; + checked_offset *= base::checked_cast<size_t>(offset.y() / subsample.height()); + // Column offset. + checked_offset += base::checked_cast<size_t>( BytesPerElement(format(), plane) * (offset.x() / subsample.width())); + const size_t visible_plane_offset = checked_offset.ValueOrDie(); + + const size_t visible_rows = base::checked_cast<size_t>(GetVisibleRows(plane)); + const size_t visible_row_bytes = + base::checked_cast<size_t>(GetVisibleRowBytes(plane)); + + // Use CheckedNumeric for size calculation to prevent overflow. // In the last row, bytes between visible width and the full stride are not // the part of the visible plane. - size_t visible_plane_size = - plane_stride * (GetVisibleRows(plane) - 1) + GetVisibleRowBytes(plane); + base::CheckedNumeric<size_t> checked_visible_plane_size = 0; + if (visible_rows > 0) { + checked_visible_plane_size = plane_stride; + checked_visible_plane_size *= (visible_rows - 1); + checked_visible_plane_size += visible_row_bytes; + } + const size_t visible_plane_size = checked_visible_plane_size.ValueOrDie(); + return data.subspan(visible_plane_offset, visible_plane_size); }
Original Bug Report
Potential GPU Process Memory Disclosure via VideoFrame Stride Truncation on ChromeOS
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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A type truncation from size_t to int when handling video frame strides allows a compromised renderer to bypass memory safety checks in the GPU process. Specifically, crafted strides can trigger signed integer wraparound, causing buffer bounds checks to pass incorrectly. This results in out-of-bounds memory reads that can be leaked via encoded video bitstreams.
Affected files:
media/gpu/chromeos/video_frame_resource.ccmedia/base/video_frame.ccmedia/gpu/chromeos/libyuv_image_processor_backend.ccmedia/mojo/mojom/video_frame_mojom_traits.cc
Estimated timestamp from git blame: 2024-11-19
Summary
A potential vulnerability in the ChromeOS video processing pipeline allows a compromised renderer to disclose memory from the privileged GPU process. The issue arises from a size_t to int truncation when handling video frame strides, combined with modular signed integer arithmetic that can bypass internal buffer safety checks.
Root Cause Analysis
The primary issue exists in the interaction between media::VideoFrame and media::VideoFrameResource (used primarily on ChromeOS). While the underlying VideoFrameLayout correctly stores plane strides as size_t (64-bit on 64-bit architectures), several accessors and internal processing functions truncate these values to 32-bit signed int before use.
Specifically:
VideoFrameResource::stride(size_t plane)(inmedia/gpu/chromeos/video_frame_resource.cc) returns anint, while the underlyingVideoFrame::stride()returns asize_t(up to 64 bits).VideoFrame::GetVisibleDataInternal()(inmedia/base/video_frame.cc) truncates the stride to a localint plane_stridebefore calculating the visible plane size for bounds checking.
Because Chromium is compiled with -fno-strict-overflow, signed integer arithmetic follows modular wraparound rules. An attacker can provide a frame with a logical stride of 0x80000000 (2GB), which truncates to INT_MIN (-2147483648) when converted to a signed 32-bit int. In the calculation for the visible plane size:
visible_plane_size = plane_stride * (rows - 1) + row_bytes
If the frame has an odd number of visible rows (e.g., 3), the term INT_MIN * 2 wraps exactly to 0 in 32-bit signed modular arithmetic. This results in a small, positive visible_plane_size, causing the base::span::subspan() memory safety check to pass even though the stride is logically incorrect.
Potential Attack Vector (Suggested Steps)
- Compromise Renderer: The attacker starts from a compromised renderer process.
- Mojo Communication: The attacker utilizes the
mojom::VideoEncodeAcceleratorMojo interface to send a craftedVideoFrameto the GPU process. - Craft Malicious Frame: The attacker prepares a
STORAGE_SHMEMorSTORAGE_DMABUFSframe with an odd visible height (e.g., 3), a stride of0x80000000, and a shared memory region large enough (approx. 4.1GB) to satisfy initial layout validation. - Trigger Image Processing: The GPU process receives the frame and, depending on the hardware encoder requirements, invokes the
LibYUVImageProcessorBackendfor format conversion or scaling. - Out-of-Bounds Read: During processing,
LibYUVuses the truncated negative stride to move between rows. Because pointers are 64-bit, adding the truncated signedint(effectively -2GB) causesLibYUVto read from memory locations 2GB and 4GB before the intended shared memory mapping. - Exfiltrate Data: The data read from these out-of-bounds locations is encoded into the output video bitstream and sent back to the renderer, allowing the attacker to recover privileged GPU process memory.
Impact
This issue provides a potential high-bandwidth memory disclosure primitive from the GPU process, which is unsandboxed on ChromeOS. This could be used to leak sensitive data or to assist in a multi-stage exploit to gain full control of the GPU process.
Suggested Fix
- Update the
FrameResourcehierarchy andVideoFrameResource::stride()to returnsize_tinstead ofintto maintain consistency withmedia::VideoFrame. - Audit
media/base/video_frame.ccto ensure that all stride-related arithmetic inGetVisibleDataInternaland other functions usessize_torbase::CheckedNumericto prevent truncation and overflow. - Improve validation in Mojo traits to reject excessively large strides that are not practical for legitimate video frames but can be used to trigger arithmetic issues in downstream components.
Evaluated with Chrome root at commit: 29093e11cf509e3593f6229e4b1b075cca356049
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
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.