CVE-2026-28859
Overview
Background
- GPU process
- A separate WebKit process that performs graphics/rendering on behalf of WebContent, reached over IPC and meant to be a sandbox boundary.
- Parallel spans
- glyphsAdvances carries glyph IDs and advances as two spans that must be equal length; drawGlyphs iterates them together.
- In-place span vs owned copy
- A span is a non-owning view into a buffer; using a renderer-supplied span in place trusts its length and lifetime, while copying into a Vector captures a consistent snapshot.
Root Cause Analysis
This hardens RemoteGraphicsContext::drawGlyphs in the GPU process against a mismatched/attacker-controlled IPC glyph buffer. drawGlyphs receives glyphsAdvances, an IPC array carrying two parallel spans — the glyph IDs (span<0>) and the per-glyph advances (span<1>) — sent by the WebContent process. The pre-patch code passed the glyph span directly into CoreGraphics text drawing (context().drawGlyphs(*font, glyphsAdvances.span<0>(), Vector<GlyphBufferAdvance>(glyphsAdvances.span<1>()), …)): the advances were copied into an owned Vector, but the glyphs were consumed in place as a view over the IPC-provided buffer. drawGlyphs consumes glyphs and advances as parallel arrays; if the two spans have different lengths (nothing forces span<0>.size()==span<1>.size() across the IPC boundary) or the underlying shared buffer’s lifetime/contents are not pinned for the call, the in-place glyph span can drive an out-of-bounds read (or read racing/attacker-mutated bytes) inside the GPU process while rendering.
The fix materializes BOTH arrays into fixed-inline owned Vectors (Vector<GlyphBufferGlyph,128> glyphs and Vector<GlyphBufferAdvance,128> advances) and passes glyphs.span()/advances.span(), so the GPU process works on consistent, owned copies with matching, captured lengths.
The restored invariant is that the glyph draw operates on validated, owned buffers rather than a live view into renderer-controlled IPC memory. The precise OOB (length mismatch vs buffer lifetime) is inferred; the diff establishes that the glyph span was previously used in place while only advances were copied.
Attack Path
- Send a crafted drawGlyphs message From a compromised WebContent process, issue RemoteGraphicsContext::drawGlyphs with a glyphsAdvances buffer whose glyph and advance spans mismatch or whose backing memory is controlled.
- Use the glyph span in place The GPU process consumes the glyph span directly as a view over the renderer-supplied buffer alongside the copied advances.
- Out-of-bounds read in the GPU process Parallel-array iteration over mismatched lengths (or racing bytes) reads out of bounds during CoreGraphics glyph drawing.
- Escape the sandbox Leverage GPU-process memory disclosure/corruption to process restricted content outside the WebContent sandbox, as the advisory describes.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
RemoteGraphicsContext::drawGlyphsSource/WebKit/GPUProcess/graphics/RemoteGraphicsContext.cpp |
modified | Copies both the glyph IDs and advances from the IPC spans into fixed-inline owned Vectors before calling context().drawGlyphs, instead of consuming the glyph span in place. |
Files Changed
Source/WebKit/GPUProcess/graphics/RemoteGraphicsContext.cpp
Audit Directions
- Other RemoteGraphicsContext handlersGrep RemoteGraphicsContext.cpp and GPUProcess/graphics for span<0>()/span<1>() or IPC spans passed directly into drawing calls without copying/length validation.
- Parallel-array IPCLook for IPC handlers that receive multiple parallel arrays and use one in place; verify equal-length checks or owned copies for all of them.
- MESSAGE_CHECK coverageConfirm GPU-process message handlers validate array sizes and cross-array length equality via MESSAGE_CHECK before use.