CVE-2026-12026
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
switchmedia/gpu/v4l2/v4l2_image_processor_backend.cc |
modified | |
ifmedia/gpu/v4l2/v4l2_image_processor_backend.cc |
modified | |
formedia/gpu/v4l2/v4l2_image_processor_backend.cc |
modified |
Files Changed
media/gpu/v4l2/v4l2_image_processor_backend.cc
Patch
From 56803c6f1d4afdb538daa1cf0a6d1d7b652493fc Mon Sep 17 00:00:00 2001 From: Hirokazu Honda <[email protected]> Date: Wed, 03 Jun 2026 13:30:24 -0700 Subject: [PATCH] media/gpu/v4l2IP: Remove input USERPTR support `V4L2_MEMORY_USERPTR` in V4L2ImageProcessor is no longer used today because the other image processors are used for v4l2 video encoding pre-processing. Bug: 517347084 Test: video_encode_accelerator_tests Change-Id: If9b0c1b02328a8d4ef9e2895ce9a1f313d0beda8 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7891569 Reviewed-by: Nathan Hebert <[email protected]> Commit-Queue: Hirokazu Honda <[email protected]> Cr-Commit-Position: refs/heads/main@{#1641154} --- diff --git a/media/gpu/v4l2/v4l2_image_processor_backend.cc b/media/gpu/v4l2/v4l2_image_processor_backend.cc index 0523947..5fa747e9 100644 --- a/media/gpu/v4l2/v4l2_image_processor_backend.cc +++ b/media/gpu/v4l2/v4l2_image_processor_backend.cc @@ -140,6 +140,7 @@ base::SingleThreadTaskRunnerThreadMode::DEDICATED)) { DVLOGF(2); DETACH_FROM_SEQUENCE(poll_sequence_checker_); + CHECK_EQ(input_memory_type_, V4L2_MEMORY_DMABUF); DCHECK_NE(output_memory_type_, V4L2_MEMORY_USERPTR); VLOGF(2) << "V4L2ImageProcessorBackend constructed with input: " @@ -216,14 +217,12 @@ v4l2_memory InputStorageTypeToV4L2Memory(VideoFrame::StorageType storage_type) { switch (storage_type) { - case VideoFrame::STORAGE_OWNED_MEMORY: - case VideoFrame::STORAGE_UNOWNED_MEMORY: - case VideoFrame::STORAGE_SHMEM: - return V4L2_MEMORY_USERPTR; case VideoFrame::STORAGE_DMABUFS: case VideoFrame::STORAGE_MAPPABLE_SHARED_IMAGE: return V4L2_MEMORY_DMABUF; default: + VLOGF(2) << "Unsupported storage type:" + << VideoFrame::StorageTypeToString(storage_type); return static_cast<v4l2_memory>(0); } } @@ -261,8 +260,7 @@ const v4l2_memory input_memory_type = InputStorageTypeToV4L2Memory(input_config.storage_type); - if (input_memory_type != V4L2_MEMORY_USERPTR && - input_memory_type != V4L2_MEMORY_DMABUF) { + if (input_memory_type != V4L2_MEMORY_DMABUF) { VLOGF(2) << "Unsupported input storage type"; return nullptr; } @@ -875,25 +873,6 @@ DCHECK(input_queue_); switch (input_memory_type_) { - case V4L2_MEMORY_USERPTR: { - const size_t num_planes = - GetNumPlanesOfV4L2PixFmt(input_config_.fourcc.ToV4L2PixFmt()); - std::vector<void*> user_ptrs(num_planes); - for (size_t i = 0; i < num_planes; ++i) { - int bytes_used = - VideoFrame::PlaneSize(job_record->input_frame->format(), i, - input_config_.size) - .GetArea(); - buffer.SetPlaneBytesUsed(i, bytes_used); - user_ptrs[i] = const_cast<uint8_t*>(job_record->input_frame->data(i)); - } - if (!std::move(buffer).QueueUserPtr(user_ptrs)) { - VPLOGF(1) << "Failed to queue a DMABUF buffer to input queue"; - NotifyError(); - return false; - } - break; - } case V4L2_MEMORY_DMABUF: { auto input_handle = CreateHandle(job_record->input_frame.get()); if (!input_handle) {
Original Bug Report
Potential GPU memory disclosure via stale v4l2_plane.length in V4L2ImageProcessorBackend
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 potential vulnerability in V4L2ImageProcessorBackend on Linux/ChromeOS ARM platforms could allow a compromised renderer to trigger an out-of-bounds read of GPU process memory. By manipulating the frame stride of the first frame to configure a large buffer length, the stale length value is retained when subsequent smaller frames are processed. This can result in a DMA over-read of adjacent GPU process memory during USERPTR operations, exposing cross-origin secrets.
Affected files:
media/gpu/v4l2/v4l2_image_processor_backend.ccmedia/gpu/v4l2/v4l2_queue.cc
Estimated timestamp from git blame: 2020-04-03
Description
There is a potential cross-origin GPU process information disclosure vulnerability in the V4L2ImageProcessorBackend component, used primarily on Linux and ChromeOS ARM-based platforms.
When V4L2ImageProcessorBackend is configured to use V4L2_MEMORY_USERPTR for input frames (which occurs when processing software-allocated shared memory buffers), it fails to update the plane size (v4l2_plane.length) when queueing subsequent input frames. Instead, the driver retains the stale plane length initialized during the queue’s initial format configuration (VIDIOC_S_FMT / VIDIOC_QUERYBUF).
If an attacker can force the backend to initialize with an extremely large stride, the V4L2 queue is configured with a very large plane size (e.g., ~5.9 MB). When a subsequent normal-sized frame is queued (e.g., ~1.38 MB), the stale large length is retained. When VIDIOC_QBUF is executed, the kernel pins and maps a memory range matching the stale length from the user pointer, resulting in a DMA over-read of up to several megabytes of adjacent virtual memory in the GPU process. This leaked memory is scaled and encoded into the compressed output bitstream, directly accessible by the renderer.
Code Analysis
In media/gpu/v4l2/v4l2_image_processor_backend.cc, EnqueueInputRecord handles queueing USERPTR buffers:
// media/gpu/v4l2/v4l2_image_processor_backend.cc
case V4L2_MEMORY_USERPTR: {
const size_t num_planes =
GetNumPlanesOfV4L2PixFmt(input_config_.fourcc.ToV4L2PixFmt());
std::vector<void*> user_ptrs(num_planes);
for (size_t i = 0; i < num_planes; ++i) {
int bytes_used =
VideoFrame::PlaneSize(job_record->input_frame->format(), i,
input_config_.size)
.GetArea();
buffer.SetPlaneBytesUsed(i, bytes_used); // Sets .bytesused only
user_ptrs[i] = const_cast<uint8_t*>(job_record->input_frame->data(i));
}
if (!std::move(buffer).QueueUserPtr(user_ptrs)) { ... }
Unlike V4L2VideoEncodeAccelerator::QueueInputFrame which explicitly calls input_buf.SetPlaneSize(i, ...) to scale down the plane length matching the frame constraints, V4L2ImageProcessorBackend only updates bytesused and leaves the length field in the underlying v4l2_buffer unmodified.
During QueueUserPtr, only the userptr field is updated in media/gpu/v4l2/v4l2_queue.cc before triggering VIDIOC_QBUF via DoQueue:
// media/gpu/v4l2/v4l2_queue.cc
for (size_t i = 0; i < ptrs.size(); i++) {
self.buffer_data_->v4l2_buffer_.m.planes[i].m.userptr =
reinterpret_cast<unsigned long>(ptrs[i]);
}
Potential Attack Steps
Please note that these are potential steps derived from static code analysis; our automated tooling does not currently have the capability to execute code or run a live proof of concept.
- A compromised renderer process initiates a video encoding session that requires an image processor (e.g., NV12 format conversion mismatch).
- For the first frame, the renderer supplies a frame with an excessively wide stride, forcing
V4L2ImageProcessorBackend::ProcessJobsto invokeReconfigureV4L2Formatand configure a wide format with the driver’s maximum allowed size. This populatesv4l2_planes_[i].lengthwith a large size (e.g., ~5.9 MB). - For subsequent frames, the renderer supplies a much smaller buffer (e.g., normal 1280x720 footprint of ~1.38 MB). Because the queue is already streaming,
ReconfigureV4L2Formatis bypassed. - During
EnqueueInputRecord, the stale.length(~5.9 MB) is retained, andVIDIOC_QBUFis called. - The kernel pins and maps a memory range matching the stale length starting from the user pointer, pinning ~4.5 MB of adjacent GPU process memory (which can contain cross-origin textures, frames, and metadata).
- The hardware scales and encodes this over-read memory into the output bitstream, exfiltrating it directly back to the compromised renderer.
Suggested Fix
In V4L2ImageProcessorBackend::EnqueueInputRecord under the V4L2_MEMORY_USERPTR case, explicitly update the plane size to match the frame size before queueing, similar to V4L2VideoEncodeAccelerator:
// Suggested fix in media/gpu/v4l2/v4l2_image_processor_backend.cc
for (size_t i = 0; i < num_planes; ++i) {
int bytes_used =
VideoFrame::PlaneSize(job_record->input_frame->format(), i,
input_config_.size)
.GetArea();
buffer.SetPlaneBytesUsed(i, bytes_used);
buffer.SetPlaneSize(i, bytes_used); // Update plane size to match current frame constraints
user_ptrs[i] = const_cast<uint8_t*>(job_record->input_frame->data(i));
}
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
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.