← All issues

[3] WebGL GPU-process draft-extension setting unenforced across IPC

Severity: Medium | Component: WebKit GPU process — GraphicsContextGLANGLE / GPUConnectionToWebProcess | 0d94848

Rated Medium because the diff closes an IPC trust-boundary gap that allowed any WebContent process to opt into draft-quality ANGLE extensions inside the GPU process regardless of the WebGLDraftExtensionsEnabled setting. There is no direct memory primitive; the impact is broadened reachable code in a more privileged process that becomes available to a follow-on exploit chain.

The patch adds a new supportWebGLDraftExtensions boolean to GraphicsContextGLAttributes (declared in the header, serialized in WebGL.serialization.in). WebGLRenderingContextBase::resolveGraphicsContextGLAttributes populates it from scriptExecutionContext.settingsValues().webGLDraftExtensionsEnabled. In the GPU process, GPUConnectionToWebProcess::createGraphicsContextGL rejects the IPC via MESSAGE_CHECK when the WebContent-supplied attribute is true while the trusted shared preference is false. To make the check meaningful, UnifiedWebPreferences.yaml adds sharedPreferenceForWebProcess: true to WebGLDraftExtensionsEnabled. Inside GraphicsContextGLANGLE::initialize, the requestable-extension filter now skips GCGLExtension::ANGLE_base_vertex_base_instance unless attributes.supportWebGLDraftExtensions is true.

Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp

void GPUConnectionToWebProcess::createGraphicsContextGL(RemoteGraphicsContextGLIdentifier identifier, WebCore::GraphicsContextGLAttributes attributes, ...)
{
MESSAGE_CHECK(!isLockdownModeEnabled());
+ MESSAGE_CHECK(!attributes.supportWebGLDraftExtensions || m_sharedPreferencesForWebProcess.webGLDraftExtensionsEnabled);

Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLE.cpp

for (auto& extensionString : m_allRequestableExtensions) {
- if (auto extension = extensionEnum(extensionString))
+ if (auto extension = extensionEnum(extensionString)) {
+ if (*extension == GCGLExtension::ANGLE_base_vertex_base_instance && !attributes.supportWebGLDraftExtensions)
+ continue;
m_requestableExtensions.add(*extension);
+ }
}

Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp

glAttributes.isWebGL2 = isWebGL2;
+ glAttributes.supportWebGLDraftExtensions = scriptExecutionContext.settingsValues().webGLDraftExtensionsEnabled;

Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml

WebGLDraftExtensionsEnabled:
...
+ sharedPreferenceForWebProcess: true

The change spans four cooperating files: a new attribute on the IPC-serialized struct, a settings read at WebContent-side context creation, a MESSAGE_CHECK at the GPU-process IPC entry, and an extension-filter branch in the ANGLE init path. The YAML change ensures the trusted preference value reaches m_sharedPreferencesForWebProcess so the IPC validator has something to compare against. Existing code paths are not restructured; the patch is an additive cross-process plumbing of one boolean.

Security-relevant setting (draft-extension gate) not plumbed into the GPU-process context-creation IPC, leaving the trusted preference unenforced across the process boundary.

The GPU process is a separate sandboxed process that hosts WebGL/WebGPU on behalf of WebContent; RemoteGraphicsContextGL in WebContent forwards WebGL calls over IPC to a GraphicsContextGLANGLE in the GPU process. ANGLE is the OpenGL-ES-on-top-of-platform-GL translation layer that backs WebKit's WebGL implementation and exposes its own set of GL_ANGLE_* and GL_EXT_* extensions, some of which are draft (subject to spec change, less hardened).

ANGLE returns a list of extensions that can be enabled at runtime (GL_REQUESTABLE_EXTENSIONS_ANGLE); WebKit copies this into m_allRequestableExtensions and then filters to m_requestableExtensions — only entries in the filtered set can be activated by getExtension. MESSAGE_CHECK is a WebKit IPC validator macro that terminates the connection if the assertion fails, used at trust boundaries to reject malformed or attacker-controlled values from a less-trusted process. sharedPreferenceForWebProcess: true in UnifiedWebPreferences.yaml causes a preference value to be replicated from the UI process into the GPU process's m_sharedPreferencesForWebProcess, giving the GPU process a trusted copy independent of WebContent.

This change narrows attack surface. Before the fix, ANGLE_base_vertex_base_instance was reachable from any WebContent process through enableExtension/getExtension paths regardless of the WebGLDraftExtensionsEnabled setting. The newly-introduced MESSAGE_CHECK enforces that draft-extension exposure is reachable only when the trusted shared preference agrees, closing the IPC path that previously let WebContent unilaterally opt in.

Pre-fix, GraphicsContextGLANGLE::initialize populated m_requestableExtensions from ANGLE's full requestable-extensions string without filtering against the WebGLDraftExtensionsEnabled setting. The setting existed, but no piece of state plumbed it into GraphicsContextGLAttributes, so the GPU-process-side context creation had nothing to gate on. Any WebContent process — including a compromised one — could obtain access to draft-quality ANGLE extensions regardless of policy.

This is the recurring "preference enforced only on the WebContent side" pattern. Many WebKit features have a setting that gates exposure but only the WebContent side consults it; the GPU/Networking process trusts the attribute struct that crosses the boundary. The mitigation pattern used here — sharedPreferenceForWebProcess: true plus a MESSAGE_CHECK against the trusted copy at the IPC entry point — is a clean template that should be replicated for any other "draft / experimental / privileged" WebGL/WebGPU/Media feature gates.

This is not directly exploitable as a standalone memory-corruption primitive. 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 MESSAGE_CHECK rejects connections that request draft-extension support when the trusted preference is off, so the attacker would need to either flip the preference (a different boundary) or find a separate path to those code paths. Lockdown Mode is already independently enforced by the existing MESSAGE_CHECK(!isLockdownModeEnabled()) and is unaffected by this change.

This vulnerability weakens the GPU-process trust boundary against a (compromised) WebContent process. The security model assumes that draft, less-mature ANGLE extensions are gated behind an explicit setting because their command pipelines have less hardening / fuzzing maturity than non-draft extensions. Pre-fix that gate did not exist at the IPC layer: any WebContent could request ANGLE_base_vertex_base_instance and exercise its draw commands inside the GPU process. An attacker who plausibly gains script execution in WebContent could reach draft-extension code paths in ANGLE and the platform GL driver, broadening the surface available for a GPU-process compromise / sandbox escape chain.

Note: The pre-fix reachability of ANGLE_base_vertex_base_instance from any WebContent regardless of WebGLDraftExtensionsEnabled, the broader claim that draft extensions are less hardened than non-draft ones, and the speculation about a m_sharedPreferencesForWebProcess update race are inferences from the diff shape rather than directly demonstrated. The structural change — preference plumbed across the IPC + MESSAGE_CHECK against a trusted copy + extension filter — is fully visible in the patch.