b5b06c0fc2 [GPU Process]: Before decoding FEColorMatrix validate the length of the `values` vector
Triage note: IPC input validation added before decoding a filter; PoC uses IPCTestingAPI to send a type/values mismatch that crashes the GPU process.
Contents
The bug at a glance
This is a GPU-process out-of-bounds read caused by trusting an IPC-supplied FEColorMatrix whose values vector length does not match its type. The GPU process handles input from the less-privileged WebContent process, so an OOB read there is a cross-process memory-safety issue that can crash the GPU process or disclose its memory. The PoC uses the IPC testing API to send a SATURATE matrix with an empty values array; because it is a read/crash primitive rather than a demonstrated write, high rather than critical.
WebKit’s GPU process renders SVG filters on behalf of web pages. An FEColorMatrix effect carries a type (matrix, saturate, hue-rotate, luminance-to-alpha) and a values array whose required length depends on that type. The IPC decoder rebuilt an FEColorMatrix from web-process-supplied fields without checking that values matched type, so a compromised or scripted web process could send, e.g., a SATURATE matrix with zero values and have the GPU process index into the empty array.
Root cause
In the DOM path, SVGFEColorMatrixElement enforces the type/length invariant: before the patch isInvalidValuesLength() rejected a MATRIX with size != 20, or a HUEROTATE/SATURATE with size != 1, and createFilterEffect() returned nullptr for an invalid length. That guard lived only on the element side. The FEColorMatrix object itself, once constructed, computes its color transform from m_values assuming the correct count – SATURATE and HUEROTATE read values[0] to build their 3x3 components, MATRIX consumes 20 entries – so an FEColorMatrix with a mismatched (e.g. empty) values vector reads past the end of the vector.
The GPU process reconstructs filter effects from IPC. WebCoreArgumentCoders.serialization.in declared FEColorMatrix as decoding a ColorMatrixType type() and a Vector<float> values() with no cross-field validation, then calling FEColorMatrix::create(type, values, …). A web process (or an attacker using IPCTestingAPI) can therefore send a FilteredNativeImage message whose filter contains an FEColorMatrix with type FECOLORMATRIX_TYPE_SATURATE (2) but values: [] . The decoder builds the effect and, when the filter is applied, the SATURATE path reads values[0] on an empty Vector<float> – an out-of-bounds heap read in the GPU process.
The fix centralizes the invariant into a reusable predicate FEColorMatrix::areValuesValidForType(ColorMatrixType, const Vector<float>&): MATRIX requires size == 20, SATURATE and HUEROTATE require size == 1, LUMINANCETOALPHA is always valid (ignores values), and UNKNOWN is invalid. This predicate is now enforced in three places. FEColorMatrix::create() gains ASSERT(areValuesValidForType(type, values)) as a debug tripwire. SVGFEColorMatrixElement drops isInvalidValuesLength() and calls FEColorMatrix::areValuesValidForType() in both svgAttributeChanged() and createFilterEffect() (returning nullptr when invalid). Critically, the serialization coder adds [Validator='WebCore::FEColorMatrix::areValuesValidForType(*type, *values)'] on the values() member, so the IPC decoder itself rejects a decoded FEColorMatrix whose values length is wrong for its type, causing the decode to fail (surfacing as a TypeError to the sender) before any FEColorMatrix is created in the GPU process.
The validator closing the gap on the IPC boundary is the security-relevant change: DOM-side checks never protected the GPU process against a malicious sender, and this bug is precisely a missing IPC input-validation check that allowed a type/length mismatch to reach length-dependent code.
Key code
New type/length predicate and its IPC validator annotation
bool FEColorMatrix::areValuesValidForType(ColorMatrixType type, const Vector<float>& values)
{
switch (type) {
case ColorMatrixType::FECOLORMATRIX_TYPE_MATRIX:
return values.size() == 20;
case ColorMatrixType::FECOLORMATRIX_TYPE_SATURATE:
case ColorMatrixType::FECOLORMATRIX_TYPE_HUEROTATE:
return values.size() == 1;
case ColorMatrixType::FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
return true;
case ColorMatrixType::FECOLORMATRIX_TYPE_UNKNOWN:
return false;
}
}
// WebCoreArgumentCoders.serialization.in:
// [Validator='WebCore::FEColorMatrix::areValuesValidForType(*type, *values)'] Vector<float> values();
Patch walkthrough
Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in— Annotates the FEColorMatrix values() member with[Validator='WebCore::FEColorMatrix::areValuesValidForType(*type, *values)'], so the generated IPC decoder validates the values vector length against the type and fails the decode on mismatch – the core cross-process fix.Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp— Adds static areValuesValidForType(type, values) encoding the per-type length rules (MATRIX==20, SATURATE/HUEROTATE==1, LUMINANCETOALPHA always valid, UNKNOWN invalid) and adds ASSERT(areValuesValidForType(type, values)) in create().Source/WebCore/platform/graphics/filters/FEColorMatrix.h— Declares WEBCORE_EXPORT static bool areValuesValidForType(ColorMatrixType, const Vector<float>&) so it is usable from the element code and the generated serialization code.Source/WebCore/svg/SVGFEColorMatrixElement.cpp/.h— Removes the local isInvalidValuesLength() and routes svgAttributeChanged() and createFilterEffect() through FEColorMatrix::areValuesValidForType(type, values), returning nullptr when invalid; unifies the DOM and IPC validation on one predicate.LayoutTests/ipc/fecolormatrix-type-values-mismatch-crash.html (+ expected)— New IPC-testing-API test that sends a FilteredNativeImage message containing an FEColorMatrix with type 2 (SATURATE) and values: [] and passes if WebKit (the GPU process) does not crash.
Background
GPU Process — A separate WebKit process that performs graphics/media work (including SVG filter rendering) on behalf of WebContent. It receives serialized objects over IPC from the less-trusted web process, so it must validate all decoded inputs.
FEColorMatrix / SVG feColorMatrix — An SVG filter primitive that applies a color transform. Its type selects one of matrix (20 values), saturate (1 value), hueRotate (1 value), or luminanceToAlpha (no values), and its behavior indexes the values array accordingly.
Type/length invariant — For FEColorMatrix the required length of values is a function of type. Code such as the SATURATE/HUEROTATE component builders reads values[0]; violating the invariant (e.g. empty values with SATURATE) yields an out-of-bounds read of the Vector<float> backing store.
Serialization Validator annotation — WebKit’s IPC serialization DSL supports a [Validator=’…’] attribute on a decoded member that runs a predicate over already-decoded fields; if it returns false the decode fails, blocking construction of an object with an illegal field combination. This is the mechanism used to enforce the invariant at the IPC boundary.
IPCTestingAPI — A debug-only facility exposing window.IPC to script so tests (and, when enabled, attackers) can send arbitrary IPC messages. The regression test uses it to hand-craft a FilteredNativeImage message with a mismatched FEColorMatrix.
Vulnerability window
- Introduction — The FEColorMatrix IPC coder decoded type and values independently with no cross-field check, while length-dependent code assumed a valid count; only the DOM element validated length.
- Trigger — A compromised/scripted web process sends a FilteredNativeImage message whose FEColorMatrix has type SATURATE (2) but an empty values array.
- Decode — The GPU process decodes and constructs the FEColorMatrix without rejecting the mismatch.
- Apply — During filter application the SATURATE path reads values[0] on the empty vector – an out-of-bounds heap read in the GPU process.
- Impact — GPU-process crash or disclosure of adjacent heap memory into the filtered image, depending on layout.
- Fix — 315306@main adds areValuesValidForType and enforces it via the serialization [Validator], so the mismatched message fails to decode before any FEColorMatrix is built.
Proof of concept
The added LayoutTest (IPCTestingAPIEnabled) stands up a RemoteRenderingBackend and RemoteImageBuffer over a stream connection, then sends a FilteredNativeImage message whose filter contains an FEColorMatrix with type 2 (SATURATE) and an empty values array. Pre-fix the GPU process constructs the effect and reads values[0] out of bounds when applying the SATURATE transform, crashing the GPU process; post-fix the serialization [Validator] rejects the decode (surfacing as a TypeError to the sender) and the GPU process does not crash. The expected result file is simply ‘This test passes if WebKit does not crash.’
remoteImageBuffer.FilteredNativeImage({
filter: { subclasses: { variantType: 'WebCore::SVGFilterRenderer', variant: {
primitiveUnits: 0,
expression: { alias: [ {index:0,level:0,geometry:{}}, {index:1,level:0,geometry:{}} ] },
effects: [
{ subclasses: { variantType: 'WebCore::SourceGraphic', variant: { operatingColorSpace: srgbColorSpace } } },
{ subclasses: { variantType: 'WebCore::FEColorMatrix', variant: {
type: 2, // FECOLORMATRIX_TYPE_SATURATE
values: [], // Empty - should have at least 1 value
operatingColorSpace: srgbColorSpace
} } }
],
geometry: { referenceBox:{location:{x:0,y:0},size:{width:128,height:128}},
filterRegion:{location:{x:0,y:0},size:{width:128,height:128}},
scale:{width:1,height:1} },
filterRenderingModes: 1, isShowingDebugOverlay: false, renderingResourceIdentifierIfExists: {}
} } }
});
Exploitation
- Precondition — Attacker needs to send IPC to the GPU process – either via a compromised WebContent process or, in test/debug builds, IPCTestingAPI. The bug is a cross-process input-validation gap, not directly reachable from unprivileged JS in a shipping build.
- Primitive — Send an FEColorMatrix with a type/values length mismatch (e.g. SATURATE with empty values) inside a filter; the GPU process reads values out of bounds when applying the effect.
- Impact — Out-of-bounds heap read in the GPU process: crash (DoS of the rendering process) or, depending on layout, disclosure of adjacent GPU-process heap into rendered output.
- Escalation — As part of a chain starting from a WebContent compromise, an info-leak in the GPU process aids defeating ASLR there. This artifact demonstrates a read/crash, not a write primitive.
Detection & hunting
For defenders and SOC / detection engineers:
- GPU-process crashes in FEColorMatrix application —
- Malformed FEColorMatrix over IPC —
- IPCTestingAPI usage in production —
Audit directions
- Other filter primitives over IPC —
- Validator coverage vs constructor asserts —
- length-dependent FEColorMatrix code —
- DOM/IPC parity —