Closed Bug 2018098 Opened 8 months ago Closed 8 months ago

YCbCr Display Rect OOB Heap Read via PTexture IPC

Categories

(Core :: Graphics, defect)

defect

Tracking

()

RESOLVED DUPLICATE of bug 2015268
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 planes
  • yStride / cbCrStride — row strides for each plane
  • display — an IntRect representing 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:

  1. BufferTextureHost::PushResourceUpdates() (TextureHost.cpp:551):

    gfx::IntSize ySize = desc.display().Size();  // BUG: uses display, not ySize
    
  2. RenderBufferTextureHost::Lock() (RenderBufferTextureHost.cpp:82):

    mYSurface = gfx::Factory::CreateWrappingDataSourceSurface(
        ..., desc.yStride(), desc.display().Size(), ...);  // BUG
    
  3. RenderBufferTextureHost::MapPlane() (RenderBufferTextureHost.cpp:216):

    aPlaneInfo.mSize = desc.display().Size();  // BUG
    
  4. ImageDataSerializer::SizeFromBufferDescriptor() (ImageDataSerializer.cpp:144):

    return aDescriptor.get_YCbCrDescriptor().display().Size();  // BUG
    
  5. SWGL compositor (composite.h): linearRowTapsR8 / textureLinearRowR8 / linear_row_yuv read 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:

  1. Allocate a shared memory region of legitimate size (e.g., 16KB for a small RGB texture)
  2. When serializing the texture for IPC (ShmemTextureData::Serialize), replace the RGBDescriptor with a YCbCrDescriptor that has:
    • Small ySize (e.g., 16x16) — passes shmem size validation
    • Large display (e.g., 16x4096) — causes renderer to read 65536 bytes from Y channel
  3. Send this malicious descriptor via the PTexture actor constructor message
  4. Parent process validates shmem size against ySize ✅ (passes)
  5. 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 descriptor
  • gfx/layers/ipc/PImageBridge.ipdl — manager protocol for PTexture
  • gfx/layers/composite/TextureHost.cpp — parent-side validation (insufficient)
  • gfx/webrender_bindings/RenderBufferTextureHost.cpp — uses display().Size() for rendering
  • gfx/wr/swgl/src/composite.h — SWGL compositor where crash occurs
  • gfx/layers/ImageDataSerializer.cpp — SizeFromBufferDescriptor uses 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.

Attached patch exploit.patch — — Splinter Review
Assignee: nobody → jschwartzentruber
Attached file exploit_canvas3.html —
Attached file crash_stack.txt —

Full log from manual repro.

Assignee: jschwartzentruber → nobody

Based on the suggested fix, I think this is a duplicate of bug 2015268.

Status: NEW → RESOLVED
Closed: 8 months ago
Duplicate of bug: CVE-2026-4708
Resolution: --- → DUPLICATE
Group: gfx-core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: