Medium CVSS 9.6 webkit OOB 🔧 Commit mapped

Overview

Medium
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected process crash
ComponentThirdParty ANGLE
Bug ClassOOB
Tracker274165
Fix commit9d7ec80f7803 (WebKit/WebKit)
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedAn anonymous researcher
Disclosed2024-07-29

Background

ANGLE
The Almost Native Graphics Layer Engine, a translation layer that implements WebGL/GLES on top of native GL/Direct3D/Metal backends, here the GL backend used by WebKit’s GPU process.
PixelUnpackState
The set of GL parameters (row length, alignment, image height, skip rows/pixels, and bound unpack buffer) that describe how pixel data is laid out and read from client memory during an upload.
Compressed sub-image upload
glCompressedTexSubImage2D/3D, which copies a block of pre-compressed texel data into a region of an existing texture.
StateManager::setPixelUnpackState
ANGLE’s helper that programs the native GL context’s unpack state (and binds/unbinds the unpack buffer) so a subsequent upload reads exactly the region ANGLE validated.
Out-of-bounds read
A memory-safety fault where code reads past the end of a buffer, here the source pixel data, potentially crashing or disclosing adjacent memory.

Root Cause Analysis

The patch adds a single line, ANGLE_TRY(stateManager->setPixelUnpackState(context, unpack)), to TextureGL::setCompressedSubImage in ANGLE’s OpenGL backend, immediately after binding the texture and before the code dispatches to the native glCompressedTexSubImage2D/3D path. setCompressedSubImage uploads a block of application-supplied compressed pixel data into an existing texture region. The gl::PixelUnpackState (the unpack parameter passed to the function) carries client-controlled parameters such as GL_UNPACK_ROW_LENGTH, alignment, skip rows/pixels, and, importantly, the bound GL_PIXEL_UNPACK_BUFFER used as the data source. The invariant ANGLE’s GL backend relies on is that before issuing a native texture-upload call, the GL state manager’s cached/native unpack state must be synchronized to the unpack parameters ANGLE resolved for this call; the non-compressed upload paths already do this via setPixelUnpackState, but the compressed path was missing the call. Because of that omission, the driver performed the compressed sub-image upload using stale or default unpack state that did not match the dimensions and byte-count ANGLE validated against, so the amount and layout of data the driver read from the source (client memory or the unpack buffer) could disagree with what was actually available. That mismatch lets the underlying GL driver read past the intended bounds of the source data, i.e. an out-of-bounds read during the upload.

The fix restores the invariant by explicitly programming the pixel-unpack state before the compressed upload, exactly as the uncompressed paths do, so the driver’s read geometry matches the validated buffer. It is worth noting (inference) that the precise faulting read happens inside the platform GL driver, which is not in the diff; the diff establishes only the missing state-synchronization call in ANGLE. Apple characterizes the observable result as an unexpected process crash, consistent with an OOB read rather than a write primitive.

Key insight
The compressed-texture upload path forgot the pixel-unpack state synchronization that every uncompressed path performs, so the driver read the source data with mismatched geometry; the one-line fix simply restores parity by calling setPixelUnpackState before the native compressed upload.

Attack Path

  1. Obtain a WebGL context Malicious web content creates a WebGL/WebGL2 context, which routes GPU commands through ANGLE’s OpenGL backend in the GPU process.
  2. Set up a compressed texture Create and bind a texture with a compressed internal format supported by the path, so subsequent uploads take the setCompressedSubImage route.
  3. Configure unpack parameters and/or an unpack buffer Set pixel-unpack parameters (row length, alignment, skip) and optionally bind a PIXEL_UNPACK_BUFFER so that the intended read geometry differs from the stale unpack state ANGLE would leave in place.
  4. Call compressedTexSubImage2D/3D Issue a compressed sub-image upload; because setPixelUnpackState was not applied, the native driver reads the source using unsynchronized unpack state and accesses memory beyond the validated data extent (out-of-bounds read).
  5. Cause a crash or leak The OOB read faults, crashing the GPU process (the observed impact); in principle a controlled OOB read could disclose adjacent GPU-process memory, but the diff supports only the crash/read, not a write or code-execution primitive.

Impact Assessment

The primitive is an out-of-bounds read during a compressed texture upload inside ANGLE’s GL backend, reachable from any web page via WebGL. Its realistic outcome is an unexpected crash of the GPU process (as Apple describes) with a theoretical, harder-to-control potential for reading adjacent GPU-process memory; the patch shows no write or type-confusion primitive, so escalation to code execution is not directly supported by this diff. The bug is confined to the sandboxed GPU process, so full compromise would require chaining an additional GPU-process-to-system sandbox escape. Overall this is a comparatively weak, mostly denial-of-service-grade primitive relative to a heap-write UAF/OOB-write.

Changed Functions

FunctionChangeNotes
TextureGL::setCompressedSubImage
Source/ThirdParty/ANGLE/src/libANGLE/renderer/gl/TextureGL.cpp
modified Adds ANGLE_TRY(stateManager->setPixelUnpackState(context, unpack)) after bindTexture and before the native compressed upload, synchronizing the driver's pixel-unpack state to the call's unpack parameters as the uncompressed paths already do.

Audit Directions

  • Other upload paths in TextureGL missing setPixelUnpackState
    In TextureGL.cpp compare setImage/setSubImage/setCompressedImage/setCompressedSubImage and any 3D/array variants: grep each for a setPixelUnpackState (or setPixelPackState for readback) call before the native texImage/texSubImage dispatch and flag any upload/readback that binds the texture but skips the state sync.
  • Same missing-state pattern in other ANGLE backends
    Check the Vulkan/Metal/D3D TextureXX.cpp implementations for equivalent compressed-vs-uncompressed asymmetry where unpack/pack parameters or the unpack buffer are applied on one path but not the parallel compressed path.
  • State-manager sync omissions generally
    Across renderer/gl grep for native glTexSubImage*/glReadPixels*/glCompressed* calls and verify each is preceded by the corresponding stateManager->setPixel{Unpack,Pack}State; a bind-then-upload sequence without a preceding state sync is the concrete tell for this bug class.
  • Unpack-buffer-aware validation vs. dispatch mismatches
    Audit places that resolve a gl::PixelUnpackState (row length/skip/buffer) for validation but then dispatch to the driver without programming that state, since the validated byte-count and the driver’s actual read geometry can diverge and produce OOB reads.

Original Bug Report

The reporter's bug is still restricted on the tracker.