High CVSS 8.8 firefox OOB 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionCertain WebGL operations on Apple silicon M series devices could have lead to an out-of-bounds write and memory corruption due to a flaw in Apple's GPU driver. <br>*This bug only affected the application on Apple M series hardware. Other platforms were unaffected.*
ComponentDOM
Bug ClassOOB
Tracker1924184
Fix commit8d3328066d4e (firefox) +2/-6
CWECWE-787 (Out-of-bounds write)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedDohyun Lee (@l33d0hyun) of USELab, Korea University & Youngho Choi of CEL, Korea University & Geumhwan Cho of USELab, Korea University
Disclosed2024-11-26

Background

`WebGLVertexArray`
The WebGL object (VAO) that records, per attribute index, whether the attribute is fed from a bound buffer (an “array” attribute) or from a constant generic value, along with pointer/stride state.
Vertex attribute array
A per-index switch, toggled by EnableVertexAttribArray/DisableVertexAttribArray, that tells the GPU pipeline to read attribute index from a buffer during a draw rather than from a constant.
`DoVertexAttrib`
The WebGLVertexArray method that (re)applies the driver-level attribute state for a given index so the tracked WebGL state and the actual GL/driver state stay consistent.
Compatibility profile
A legacy OpenGL context mode in which attribute index 0 has special, non-disableable semantics, historically requiring DisableVertexAttribArray to skip index 0.

Root Cause Analysis

The vulnerable path was in WebGLContext::EnableVertexAttribArray and WebGLContext::DisableVertexAttribArray, which immediately issued the raw driver calls gl->fEnableVertexAttribArray(index) and gl->fDisableVertexAttribArray(index) and only then updated the WebGLVertexArray bookkeeping via SetAttribIsArray. On Apple silicon M-series hardware, driving the underlying GPU driver directly in this way — including the special-cased skip of index 0 in the compatibility-profile branch — could leave the driver’s notion of which attributes are array-backed inconsistent with the buffer/stride state that Firefox tracked, so a subsequent draw could read or write vertex attribute memory out of bounds due to a flaw in Apple’s GPU driver. The invariant violated is that the driver’s enabled-attribute state must always be reapplied together with, and derived from, the WebGL-tracked attribute state rather than being set eagerly and independently.

The fix removes the direct fEnableVertexAttribArray/fDisableVertexAttribArray calls (and the fragile compatibility-profile conditional) and instead updates SetAttribIsArray and then calls mBoundVertexArray->DoVertexAttrib(index), which reapplies the full, validated attribute state in one consistent step. This routes all attribute enabling/disabling through a single code path that cannot desynchronize the tracked state from what is programmed into the driver.

Key insight
The core mistake was poking the raw GPU driver’s per-attribute enable state directly and out of sync with Firefox’s own WebGLVertexArray tracking, which on Apple M-series drivers could produce an out-of-bounds access; the fix routes the change through DoVertexAttrib, so the driver is only ever programmed from the fully-updated, validated WebGL attribute state.

Attack Path

  1. Create a malicious WebGL context The attacker loads a page that obtains a WebGL context and a vertex array object on an Apple M-series device.
  2. Toggle attribute arrays JavaScript calls enableVertexAttribArray/disableVertexAttribArray in a sequence that exercises the eager fEnableVertexAttribArray/fDisableVertexAttribArray path, including the compatibility-profile index-0 special case.
  3. Desynchronize driver state Because the driver call precedes and diverges from the SetAttribIsArray bookkeeping, the GPU driver’s enabled-attribute/stride view becomes inconsistent with the buffers actually bound.
  4. Trigger a draw A subsequent draw call causes Apple’s GPU driver to read vertex data using the inconsistent attribute state, indexing past the intended buffer bounds.
  5. Achieve memory corruption The out-of-bounds vertex fetch/write corrupts adjacent memory, which the attacker can shape toward controlled corruption.

Impact Assessment

An attacker who can run WebGL from web content on Apple M-series hardware gains an out-of-bounds write and resulting memory corruption in the process handling WebGL, a high-severity primitive that can be leveraged toward further compromise. Exploitation requires the victim to run the malicious page in Firefox on affected Apple silicon; other platforms and non-M-series hardware are unaffected. No special privileges beyond normal web content execution are needed.

Files Changed

  • dom/canvas/WebGLContextVertices.cpp

Audit Directions

  • Eager driver calls before state tracking
    Look for WebGL entry points that call gl->f* driver functions before updating the corresponding WebGLVertexArray/WebGLContext bookkeeping, since the driver may then act on state that Firefox has not validated.
  • Profile-specific special cases
    Scrutinize conditionals keyed on IsCompatibilityProfile or attribute index 0, which introduce divergent paths where tracked and driver state can drift apart.
  • State reapplication coverage
    Verify that every place mutating per-attribute array state funnels through DoVertexAttrib (or an equivalent single reapply point) rather than programming the driver ad hoc.
diff --git a/dom/canvas/WebGLContextVertices.cpp b/dom/canvas/WebGLContextVertices.cpp
index cd2fbc51b2d..2d27dcfcb4e 100644
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -90,10 +90,9 @@ void WebGLContext::EnableVertexAttribArray(GLuint index) {
 
   if (!ValidateAttribIndex(*this, index)) return;
 
-  gl->fEnableVertexAttribArray(index);
-
   MOZ_ASSERT(mBoundVertexArray);
   mBoundVertexArray->SetAttribIsArray(index, true);
+  mBoundVertexArray->DoVertexAttrib(index);
 }
 
 void WebGLContext::DisableVertexAttribArray(GLuint index) {
@@ -102,12 +101,9 @@ void WebGLContext::DisableVertexAttribArray(GLuint index) {
 
   if (!ValidateAttribIndex(*this, index)) return;
 
-  if (index || !gl->IsCompatibilityProfile()) {
-    gl->fDisableVertexAttribArray(index);
-  }
-
   MOZ_ASSERT(mBoundVertexArray);
   mBoundVertexArray->SetAttribIsArray(index, false);
+  mBoundVertexArray->DoVertexAttrib(index);
 }
 
 Maybe<double> WebGLContext::GetVertexAttrib(GLuint index, GLenum pname) {
Loading diff…