YCbCr Display Rect OOB Heap Read via PTexture IPC
Categories
(Core :: Graphics, defect)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox149 | --- | affected |
People
(Reporter: truber, Unassigned)
Details
(Keywords: ai-involved, csectype-sandbox-escape)
Attachments
(4 files)
Vulnerability Analysis: YCbCr Display Rect OOB Heap Read via PTexture IPC
Bug Overview
A heap-buffer-overflow read vulnerability exists in Firefox's texture compositing pipeline due to a missing validation of the display rect field in YCbCrDescriptor. A compromised content process can send a malicious YCbCrDescriptor over IPC (via the PTexture actor constructor in PImageBridge) where the display rect is significantly larger than the actual ySize. The parent process validates the shared memory size against ySize but uses display().Size() when rendering, causing an out-of-bounds heap read in the parent/compositor process.
Vulnerability Details
Root Cause
The PTexture protocol is a sub-actor of PImageBridge/PCompositorBridge. When a content process creates a texture, it sends a SurfaceDescriptor containing a BufferDescriptor (which can be a YCbCrDescriptor) to the parent process. The YCbCrDescriptor contains:
ySize/cbCrSize— the actual dimensions of the Y and CbCr planesyStride/cbCrStride— row strides for each planedisplay— anIntRectrepresenting the visible region to render
The bug: The parent-side validation in CreateBackendIndependentTextureHost() (TextureHost.cpp:280-283) validates the shared memory size using ySize() and cbCrSize():
reqSize = ImageDataSerializer::ComputeYCbCrBufferSize(
ycbcr.ySize(), ycbcr.yStride(), ycbcr.cbCrSize(),
ycbcr.cbCrStride(), ycbcr.yOffset(), ycbcr.cbOffset(),
ycbcr.crOffset());
But multiple rendering paths use display().Size() instead:
-
BufferTextureHost::PushResourceUpdates()(TextureHost.cpp:551):gfx::IntSize ySize = desc.display().Size(); // BUG: uses display, not ySize -
RenderBufferTextureHost::Lock()(RenderBufferTextureHost.cpp:82):mYSurface = gfx::Factory::CreateWrappingDataSourceSurface( ..., desc.yStride(), desc.display().Size(), ...); // BUG -
RenderBufferTextureHost::MapPlane()(RenderBufferTextureHost.cpp:216):aPlaneInfo.mSize = desc.display().Size(); // BUG -
ImageDataSerializer::SizeFromBufferDescriptor()(ImageDataSerializer.cpp:144):return aDescriptor.get_YCbCrDescriptor().display().Size(); // BUG -
SWGL compositor (
composite.h):linearRowTapsR8/textureLinearRowR8/linear_row_yuvread texture data based on the display dimensions, causing the actual OOB read.
There is no validation anywhere that display fits within ySize.
Attack Vector
A compromised content process can:
- Allocate a shared memory region of legitimate size (e.g., 16KB for a small RGB texture)
- When serializing the texture for IPC (
ShmemTextureData::Serialize), replace theRGBDescriptorwith aYCbCrDescriptorthat has:- Small
ySize(e.g., 16x16) — passes shmem size validation - Large
display(e.g., 16x4096) — causes renderer to read 65536 bytes from Y channel
- Small
- Send this malicious descriptor via the
PTextureactor constructor message - Parent process validates shmem size against
ySize✅ (passes) - Parent process renders using
display().Size()❌ (massive OOB heap read)
Impact
- Heap-buffer-overflow READ in parent process — ASAN confirms reading freed/unallocated heap memory
- The crash occurs in the WebRender software compositor thread (
SwCompositeThread) in the parent process - An attacker can read up to tens of thousands of bytes beyond the allocated shared memory region
- This can leak sensitive parent process heap data (cookies, tokens, other content process data, etc.)
- In theory, careful exploitation could turn this into an information disclosure primitive for a full sandbox escape chain
Crash Details
AddressSanitizer: heap-buffer-overflow on address 0x7b1794cba1f0
READ of size 2 at 0x7b1794cba1f0 thread T90
Stack:
#0 glsl::Unaligned::load (vector_type.h:510)
#2 linearRowTapsR8 (composite.h:785)
#4 linear_row_yuv (composite.h:1100)
#5 linear_convert_yuv (composite.h:1206)
#6 CompositeYUV (composite.h:1390)
#7 SwCompositeJob::composite_rect (sw_compositor.rs:621)
The crash is in the parent process's SwCompositeThread, which composites YUV texture data. The read at 0x7b1794cba1f0 is 5648 bytes before a freed 131104-byte heap region, confirming the out-of-bounds access.
Suggested Fix
Add validation in CreateBackendIndependentTextureHost() that the display rect is fully contained within the ySize dimensions:
case BufferDescriptor::TYCbCrDescriptor: {
const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor();
// Validate display rect fits within ySize
gfx::IntRect yBounds(0, 0, ycbcr.ySize().width, ycbcr.ySize().height);
if (!yBounds.Contains(ycbcr.display())) {
NS_ERROR("YCbCrDescriptor display rect exceeds ySize bounds!");
return nullptr;
}
reqSize = ImageDataSerializer::ComputeYCbCrBufferSize(...);
break;
}
Affected Components
gfx/layers/ipc/PTexture.ipdl— the IPC protocol that transmits the malicious descriptorgfx/layers/ipc/PImageBridge.ipdl— manager protocol for PTexturegfx/layers/composite/TextureHost.cpp— parent-side validation (insufficient)gfx/webrender_bindings/RenderBufferTextureHost.cpp— usesdisplay().Size()for renderinggfx/wr/swgl/src/composite.h— SWGL compositor where crash occursgfx/layers/ImageDataSerializer.cpp—SizeFromBufferDescriptoruses display
Reproduction
The testcase creates a canvas element that generates shmem-backed textures. The C++ patch intercepts ShmemTextureData::Serialize in the content process and replaces the RGB descriptor with a malicious YCbCr descriptor before it's sent over IPC. The parent process accepts the texture (validation passes) but crashes during compositing due to the OOB heap read.
| Reporter | ||
Comment 1•8 months ago
|
||
| Reporter | ||
Comment 2•8 months ago
|
||
| Reporter | ||
Comment 3•8 months ago
|
||
| Reporter | ||
Comment 4•8 months ago
|
||
Full log from manual repro.
| Reporter | ||
Updated•8 months ago
|
Comment 5•8 months ago
|
||
Based on the suggested fix, I think this is a duplicate of bug 2015268.
Updated•3 months ago
|
Description
•