CVE-2026-6309
Overview
Files Changed
services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc
Patch
From 4002a66778d2aef40fb7d72c580d7bbc10115ea8 Mon Sep 17 00:00:00 2001 From: Vasiliy Telezhnikov <[email protected]> Date: Wed, 01 Apr 2026 11:19:52 -0700 Subject: [PATCH] Fix potential double free in deserializing CopyOutputResults Skia always runs release proc of the SkBitmap::installPixels [1]. [1] https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/include/core/SkBitmap.h;drc=405f385dce2db578ff3b2301686d231ee8f0b042;l=586 Bug: 497846428 Change-Id: I4d3fd2e676fa7aa74e1022cbd2c9d9db8970a90c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7718978 Commit-Queue: Vasiliy Telezhnikov <[email protected]> Reviewed-by: Joe Mason <[email protected]> Cr-Commit-Position: refs/heads/main@{#1608675} --- diff --git a/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc b/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc index 59fbc85e..9d6bdd42 100644 --- a/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc +++ b/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc @@ -111,12 +111,14 @@ return false; } - if (!sk_bitmap->installPixels(image_info, mapping_ptr->memory(), + // Skia guarantees that it will call release proc, so we pass release()'ed + // pointer into it. + void* bitmap_memory = mapping_ptr->memory(); + if (!sk_bitmap->installPixels(image_info, bitmap_memory, data.row_bytes(), &DeleteSharedMemoryMapping, - mapping_ptr.get())) { + mapping_ptr.release())) { return false; } - mapping_ptr.release(); return true; }
Original Bug Report
Potential Double-Free in BitmapInSharedMemory Deserialization via SkBitmap::installPixels
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A potential double-free vulnerability exists in the browser process when deserializing viz::mojom::BitmapInSharedMemory from an untrusted process. If SkBitmap::installPixels fails, it synchronously invokes a release callback that frees a shared memory mapping, which is then freed a second time by a std::unique_ptr when the function returns early.
Affected files:
services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc
Estimated timestamp from git blame: 2020-04-29
Description
A double-free vulnerability exists in services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc within the StructTraits<viz::mojom::BitmapInSharedMemoryDataView, SkBitmap>::Read method. This method is used by the browser process to deserialize SkBitmap data stored in shared memory, typically received from a potentially compromised GPU or Renderer process via IPCs such as viz.mojom.CopyOutputResultSender.SendResult.
In the Read method, a base::WritableSharedMemoryMapping object is allocated on the heap and managed by a std::unique_ptr named mapping_ptr. This mapping is passed to sk_bitmap->installPixels along with a release callback, DeleteSharedMemoryMapping, and the raw pointer mapping_ptr.get() as the context:
// services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc
auto mapping_ptr =
std::make_unique<base::WritableSharedMemoryMapping>(region_opt->Map());
// ...
if (!sk_bitmap->installPixels(image_info, mapping_ptr->memory(),
data.row_bytes(), &DeleteSharedMemoryMapping,
mapping_ptr.get())) {
return false;
}
mapping_ptr.release();
return true;
The DeleteSharedMemoryMapping function is responsible for deleting the mapping object:
void DeleteSharedMemoryMapping(void* not_used, void* context) {
delete static_cast<base::WritableSharedMemoryMapping*>(context);
}
If sk_bitmap->installPixels fails (e.g., due to an invalid row_bytes value that wasn’t caught by earlier checks), it internally invokes the provided releaseProc (DeleteSharedMemoryMapping), which performs the first delete of the mapping object. The Read method then returns false without calling mapping_ptr.release(). Consequently, when the mapping_ptr std::unique_ptr goes out of scope, its destructor attempts to delete the same pointer a second time, resulting in a synchronous double-free on the browser process heap.
Potential Attack Vector
(Note: These are suggested steps based on code analysis; our tooling agent does not yet have the ability to run code to verify an exploit.)
- An attacker compromises the GPU process.
- The browser process requests a screenshot (e.g., via tab thumbnail capture or DevTools) using a
CopyOutputRequest. - The compromised GPU process responds by sending a malicious
viz::mojom::CopyOutputResultover IPC, containing a craftedBitmapInSharedMemorystruct. - The attacker crafts the
BitmapInSharedMemorywith:image_info.width= 1image_info.height= 1image_info.colorType=kAlpha_8_SkColorTyperow_bytes=0x80000000(2147483648)pixels= a valid shared memory region of at least 1 byte.
- During deserialization in the browser process:
- The check
image_info.validRowBytes(0x80000000)passes because0x80000000 >= minRowBytes(which is 1) and alignment is correct. - The check
mapping_ptr->size() < image_info.computeByteSize(data.row_bytes())passes becausecomputeByteSizemultipliesrow_bytesbyheight - 1(which is 0), resulting in a required size of 1 byte. sk_bitmap->installPixelsis called.
- The check
- Inside Skia’s
SkBitmap::installPixels, it callsSkBitmap::setInfo. This function checks ifrow_bytesfits in a signed 32-bit integer:SkTFitsIn<int32_t>(rowBytes). Since0x80000000>INT32_MAX(0x7FFFFFFF), this check fails, causingsetInfoto returnfalse. SkBitmap::installPixelshandles the failure by calling thereleaseProc(DeleteSharedMemoryMapping), performing the first free of theWritableSharedMemoryMappingobject.installPixelsreturnsfalsetoStructTraits::Read.StructTraits::Readtakes the error pathreturn false;. Thestd::unique_ptrmapping_ptrgoes out of scope and its destructor performs the second free of the exact same object.- This corrupts the PartitionAlloc freelist in the Browser process, providing a powerful primitive for arbitrary Remote Code Execution (RCE) and sandbox escape.
Suggested Fix
Release the std::unique_ptr before calling installPixels. Since installPixels guarantees that the releaseProc will be called even if it fails, ownership of the mapping should be transferred to Skia before the call.
auto* mapping_raw = mapping_ptr.release();
if (!sk_bitmap->installPixels(image_info, mapping_raw->memory(),
data.row_bytes(), &DeleteSharedMemoryMapping,
mapping_raw)) {
return false;
}
return true;
Evaluated with Chrome root at commit: 876d480da1f794d87813cfa2e6ff4fcf9771e939
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.