High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionPrivilege escalation in the Graphics: CanvasWebGL component
ComponentDOM
Bug ClassLogic Error
Tracker2055056
Fix commitfa9df688142f (firefox) +1/-0
CISA KEVNot listed
CreditedJacolon Walker
Disclosed2026-08-18

Background

Transform Feedback
a WebGL feature that captures the outputs of a vertex/geometry shader into GPU buffer objects during a draw call.
`WebGLTransformFeedback`
the Gecko object that tracks the state of a transform feedback session, including which buffers are bound and how much room remains for captured vertices.
`mActive_VertCapacity`
the maximum number of vertices that the currently bound feedback buffers can hold, computed from the smallest bound buffer range.
`mActive_VertPosition`
a running counter of how many vertices have already been written into the feedback buffers during the active session.

Root Cause Analysis

When a transform feedback session is (re)prepared in WebGLTransformFeedback::PrepareTransformFeedback, the code recomputes mActive_VertCapacity from the currently bound buffers (minVertCapacity) but did not correspondingly re-clamp the stale mActive_VertPosition carried over from a prior state. This violates the invariant that mActive_VertPosition <= mActive_VertCapacity, which downstream draw/capacity accounting relies on to bound writes into the feedback buffers. If a newly computed capacity was smaller than the leftover position value, later captured vertices would be written starting past the intended buffer boundary, producing an out-of-bounds condition.

The fix adds mActive_VertPosition = std::min(mActive_VertPosition, mActive_VertCapacity);, forcing the position back within the freshly computed capacity so the invariant holds before any vertices are captured. Because the clamp runs on every preparation of the session, any subsequent capacity accounting starts from a valid, in-bounds position.

Key insight
The single mistake was recomputing the buffer capacity while leaving the write position untouched, so a shrunken capacity could sit below an already-advanced position; the fix restores the position <= capacity invariant by clamping the position to the new capacity.

Attack Path

  1. Set up feedback buffers Script binds transform feedback buffer ranges and begins a session so that mActive_VertPosition advances during captures.
  2. Force a capacity change The attacker re-prepares the transform feedback with bound buffers whose smallest range yields a minVertCapacity lower than the current mActive_VertPosition.
  3. Skip the clamp Without the fix, mActive_VertCapacity shrinks while the stale position remains larger, breaking the capacity invariant.
  4. Trigger captures A draw call captures vertices using the now-inconsistent position/capacity accounting, writing past the intended buffer bounds.

Impact Assessment

An attacker running WebGL from web content can drive captured-vertex writes out of the intended buffer bounds within the content/GPU process, corrupting adjacent memory. This requires the attacker to script a transform feedback session and re-prepare it so the recomputed capacity falls below the already-advanced position. The metadata classifies this as a high-impact privilege escalation in the Graphics: CanvasWebGL component.

Files Changed

  • dom/canvas/WebGLTransformFeedback.cpp

Audit Directions

  • Recompute-without-reclamp
    When a capacity, length, or bound field is recomputed, check that every dependent cursor or position field (like mActive_VertPosition) is re-validated against the new value in the same code path.
  • State carried across re-preparation
    Audit WebGL Prepare*/Begin* paths for stale per-session counters that survive a reconfiguration and could exceed freshly derived limits.
  • Min-derived bounds
    Scrutinize capacities derived from a std::min over multiple buffer ranges, since binding a smaller buffer can silently reduce a limit below previously advanced offsets.
diff --git a/dom/canvas/WebGLTransformFeedback.cpp b/dom/canvas/WebGLTransformFeedback.cpp
index 88bf4274278..2f98988d605 100644
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -76,6 +76,7 @@ bool WebGLTransformFeedback::PrepareTransformFeedback() {
   }
 
   mActive_VertCapacity = minVertCapacity;
+  mActive_VertPosition = std::min(mActive_VertPosition, mActive_VertCapacity);
 
   return true;
 }
Loading diff…