[4] WebGL draft-extension setting never reached the GPU process
The GPU process let the renderer vote on which draft extensions it could use.
Medium — nothing here corrupts memory on its own. What it does is decide how much ANGLE and driver code a renderer can reach inside a more privileged process, and the deciding vote was cast by the untrusted side of the IPC.
WebGL does not execute in the process that runs the page. A RemoteGraphicsContextGL in WebContent forwards WebGL calls over IPC to a GraphicsContextGLANGLE living in the sandboxed GPU process, which drives ANGLE and the platform GL driver on the renderer's behalf. Some ANGLE extensions are marked draft — still subject to spec change — and WebKit gates them behind an explicit preference, so the set of extensions a context can activate is supposed to be a function of trusted settings, not of whatever the calling process asks for.
The angle: a compromised WebContent process can ask the GPU process to expose a draft ANGLE extension and get it, regardless of whether the user or policy enabled draft extensions.
Expose ANGLE extensions related to WebGL draft extensions only if the setting is on.
Add the
GraphicsContextGLAttributes.supportWebGLDraftExtensionsproperty since that is what controls theGraphicsContextGLANGLE. Validate this property against the real settings.
Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp
Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLE.cpp
Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
Patch Details
The change adds a per-context attribute carrying the setting across the IPC boundary, replicates the trusted preference into the GPU process so the receiving side has something to compare against, and adds the two enforcement points.
A new supportWebGLDraftExtensions boolean joins GraphicsContextGLAttributes (declared in GraphicsContextGLAttributes.h, serialized in WebGL.serialization.in), populated in WebGLRenderingContextBase::resolveGraphicsContextGLAttributes from scriptExecutionContext.settingsValues().webGLDraftExtensionsEnabled. To give the GPU process a trusted copy of the same value, UnifiedWebPreferences.yaml adds sharedPreferenceForWebProcess: true to WebGLDraftExtensionsEnabled, replicating it into m_sharedPreferencesForWebProcess.
Enforcement lands in two places. GPUConnectionToWebProcess::createGraphicsContextGL gains a MESSAGE_CHECK rejecting the IPC when the WebContent-supplied attribute is true while the GPU-side shared preference is false. Inside GraphicsContextGLANGLE::initialize, the loop that filters ANGLE's requestable-extension list into m_requestableExtensions now skips GCGLExtension::ANGLE_base_vertex_base_instance unless the attribute is set.
Security-relevant setting not plumbed into the GPU-process context-creation IPC, leaving the trusted preference unenforced across the process boundary.
Background
The GPU process and ANGLE.
The GPU process is a separate sandboxed process that hosts WebGL and WebGPU on behalf of WebContent. ANGLE is the OpenGL-ES-on-top-of-platform-GL translation layer backing WebKit's WebGL implementation; it exposes its own set of GL_ANGLE_* and GL_EXT_* extensions, some of which are draft — subject to spec change and, per the commit's rationale for gating them, less hardened than shipped extensions.
Requestable extensions.
ANGLE returns a list of extensions that can be enabled at runtime via GL_REQUESTABLE_EXTENSIONS_ANGLE. WebKit copies this into m_allRequestableExtensions and then filters it into m_requestableExtensions; only entries in the filtered set can be activated by getExtension.
MESSAGE_CHECK.
A WebKit IPC validator macro that terminates the connection when its condition fails. It is the standard tool at trust boundaries for rejecting malformed or attacker-controlled values arriving from a less-trusted process.
sharedPreferenceForWebProcess.
A flag in UnifiedWebPreferences.yaml that causes a preference value to be replicated from the UI process into the GPU process's m_sharedPreferencesForWebProcess. The mechanism exists so the GPU process can hold a trusted copy of a setting independent of anything WebContent claims — without it, a receiving process has no reference value to validate an incoming attribute against, which is exactly the gap this commit closes.
Analysis
The root cause is a missing plumb, not a broken check. GraphicsContextGLANGLE::initialize populated m_requestableExtensions from ANGLE's full requestable-extensions string with no filtering against WebGLDraftExtensionsEnabled. The setting existed, but no state carried it into GraphicsContextGLAttributes, so GPU-process-side context creation had nothing to gate on.
WebContent (untrusted) IPC boundary GPU process
────────────────────── ──────────── ───────────
Settings.webGLDraft [attributes] createGraphicsContextGL
ExtensionsEnabled │ │
│ (consulted here only) │ │ before: no gate
└──► GraphicsContextGL ──────────┼──────────────────►│
Attributes │ ▼
GraphicsContextGLANGLE::initialize
m_allRequestableExtensions
└─ no filter ──► ANGLE_base_vertex_base_instance
reachable from any renderer
The fix closes the loop at three points visible in the diagram above: the attribute now carries the setting across the boundary, the MESSAGE_CHECK rejects the IPC when the attribute disagrees with the GPU-side trusted copy, and the ANGLE init filter drops the draft extension when the attribute is false. Whether any prior gating existed on the WebContent side is not part of the supplied context — the diff establishes the new plumbing, not the absence of an older check elsewhere.
This change removes attack surface rather than adding it: the requestable-extension set in the GPU process is now narrower by default. Before the fix, ANGLE_base_vertex_base_instance was reachable from any WebContent process through the enableExtension/getExtension paths regardless of the setting; the new MESSAGE_CHECK closes the IPC path that let WebContent unilaterally opt in.
There is no standalone exploit here. The relevant scenario is a chain: an attacker first achieves arbitrary script execution or limited renderer-side compromise, then uses the unfiltered IPC to make the GPU process expose ANGLE_base_vertex_base_instance, then attacks bugs reachable only when that draft extension is active. Pre-fix the IPC accepted any caller; post-fix the attacker would need to either flip the preference — a different boundary — or find a separate path to those code paths. The bug provides expanded reachability, not a primitive; it converts to one only if the newly reachable code contains its own bug, for example a validation gap on baseVertex/baseInstance that could mis-index vertex buffers in shader pipelines. A successful chain through such a bug would yield GPU-process compromise inside the sandbox; a full escape would still require an additional GPU-process-to-system bug. Lockdown Mode is independently enforced by the pre-existing MESSAGE_CHECK(!isLockdownModeEnabled()) and is unaffected.
The discovery angle looks like internal review rather than fuzzing: this is the classic "setting only enforced on one side of the IPC" pattern that surfaces when someone systematically maps Settings toggles to their corresponding MESSAGE_CHECKs, and the Apple-internal radar plus the access-restricted bugs.webkit.org entry are consistent with internal disclosure.
This vulnerability weakens the GPU-process trust boundary against a compromised WebContent process. The security model assumes draft ANGLE extensions are gated behind an explicit setting; before the fix that gate did not exist at the IPC layer, so any WebContent could request ANGLE_base_vertex_base_instance and exercise its draw commands inside the GPU process, broadening the surface available for a GPU-process compromise chain.
Insight: the mitigation shape used here — sharedPreferenceForWebProcess: true to replicate the trusted value, plus a MESSAGE_CHECK against that copy at the IPC entry point — is a clean template worth replicating for any other draft, experimental, or privileged WebGL/WebGPU/Media feature gate. The recurring failure is that many WebKit features have a setting gating exposure, but only the WebContent side consults it while the receiving process trusts the attribute struct that crosses the boundary.
Audit directions
- Settings gating experimental features consulted only in WebContent. Audit every IPC entry in
GPUConnectionToWebProcessandRemoteGraphicsContextGL/RemoteGPUfor parameters that mirror a WebCore preference but are not re-validated againstm_sharedPreferencesForWebProcess. Start by greppingMESSAGE_CHECKinSource/WebKit/GPUProcess/and cross-referencing eachSettings/preference name inUnifiedWebPreferences.yamlthat lackssharedPreferenceForWebProcess: true. In review, an attributes struct field that originates fromsettingsValues()and crosses IPC without a matchingMESSAGE_CHECKon the receiving side is the tell. - Feature lists computed from device- or driver-reported strings without a gate filter. Audit
GraphicsContextGLANGLE::initializeand the WebGPU adapter feature enumeration for other entries in the requestable-extensions array that are draft upstream but currently unfiltered. Verify each draftGCGLExtensionenum value has a corresponding gate in the filter loop just introduced forANGLE_base_vertex_base_instance. - Preference-synchronization drift across the IPC. Shared preferences replicated into the GPU or Networking process can drift from WebContent's view, allowing a
MESSAGE_CHECKrace if the IPC is sent across a preference change. Investigate the update sequencing ofm_sharedPreferencesForWebProcessinGPUConnectionToWebProcessversuscreateGraphicsContextGLto ensure preference toggles apply before any in-flight context-creation IPC is processed; a staletruecould permit creation right before the gate is observed asfalse. - The gated extension itself. Audit
ANGLE_base_vertex_base_instancewithin ANGLE for parameter validation onbaseVertex/baseInstancein indexed draw calls. Confirm whether negative or huge values are clamped before reaching the platform GL driver; singling this extension out for gating suggests its validation surface is actively considered untrusted.