High firefox OOB 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionModification of specific WebGL shader attributes could trigger an out-of-bounds read, which, when chained with other vulnerabilities, could be used to escalate privileges.<br>*This bug only affects Firefox for macOS. Other versions of Firefox are unaffected.*
ComponentDOM
Bug ClassOOB
Tracker1937097
Fix commitfef871d1edf0 (firefox) +110/-108
CISA KEVNot listed
Creditedun3xploitable & GF
Disclosed2025-04-29

Changed Functions

FunctionChangeNotes
if
dom/canvas/WebGLContextDraw.cpp
modified
for
dom/canvas/WebGLContextDraw.cpp
modified
switch
dom/canvas/WebGLContextDraw.cpp
modified

Files Changed

  • dom/canvas/WebGLContext.h
  • dom/canvas/WebGLContextDraw.cpp
diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h
index 334e92b5981..8b786ebf61c 100644
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -929,7 +929,6 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
   WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need() const;
   bool DoFakeVertexAttrib0(uint64_t fakeVertexCount,
                            WebGLVertexAttrib0Status whatDoesAttrib0Need);
-  void UndoFakeVertexAttrib0();
 
   bool mResetLayer = true;
   bool mOptionsFrozen = false;
@@ -1220,8 +1219,8 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
   CacheInvalidator mGenericVertexAttribTypeInvalidator;
 
   GLuint mFakeVertexAttrib0BufferObject = 0;
-  intptr_t mFakeVertexAttrib0BufferObjectSize = 0;
-  bool mFakeVertexAttrib0DataDefined = false;
+  intptr_t mFakeVertexAttrib0BufferAllocSize = 0;
+  intptr_t mFakeVertexAttrib0BufferInitializedSize = 0;
   alignas(alignof(float)) uint8_t
       mGenericVertexAttrib0Data[sizeof(float) * 4] = {};
   alignas(alignof(float)) uint8_t
diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp
index 72ef2d47ae4..076d904b0c6 100644
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -781,7 +781,9 @@ void WebGLContext::DrawArraysInstanced(const GLenum mode, const GLint first,
       driverFirst = 0;
     }
   }
-  if (driverFirst != first) {
+  const bool needsFix_InstancedUserAttribFetch = (driverFirst != first);
+
+  if (needsFix_InstancedUserAttribFetch) {
     for (const auto& a : activeAttribs) {
       if (a.location == -1) continue;
       const auto& binding = mBoundVertexArray->AttribBinding(a.location);
@@ -792,6 +794,21 @@ void WebGLContext::DrawArraysInstanced(const GLenum mode, const GLint first,
 
     gl->fUniform1i(mActiveProgramLinkInfo->webgl_gl_VertexID_Offset, first);
   }
+  const auto undoFix_InstancedUserAttribFetch = MakeScopeExit([&]() {
+    if (needsFix_InstancedUserAttribFetch) {
+      gl->fUniform1i(mActiveProgramLinkInfo->webgl_gl_VertexID_Offset, 0);
+
+      for (const auto& a : activeAttribs) {
+        if (a.location == -1) continue;
+        const auto& binding = mBoundVertexArray->AttribBinding(a.location);
+        if (binding.layout.divisor) continue;
+
+        mBoundVertexArray->DoVertexAttrib(a.location, 0);
+      }
+    }
+  });
+
+  // -
 
   {
     const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
@@ -803,22 +820,20 @@ void WebGLContext::DrawArraysInstanced(const GLenum mode, const GLint first,
       fakeVertCount = 0;
     }
 
-    auto undoAttrib0 = MakeScopeExit([&]() {
-      MOZ_RELEASE_ASSERT(whatDoesAttrib0Need !=
-                         WebGLVertexAttrib0Status::Default);
-      UndoFakeVertexAttrib0();
-    });
-    if (fakeVertCount) {
-      if (!DoFakeVertexAttrib0(fakeVertCount, whatDoesAttrib0Need)) {
-        error = true;
-        undoAttrib0.release();
+    const bool needsFix_FakeVertexAttrib0 = bool(fakeVertCount);
+    const auto undoFix_FakeVertexAttrib0 = MakeScopeExit([&]() {
+      if (needsFix_FakeVertexAttrib0) {
+        mBoundVertexArray->DoVertexAttrib(0);
       }
-    } else {
-      // No fake-verts needed.
-      undoAttrib0.release();
+    });
+    if (needsFix_FakeVertexAttrib0) {
+      // fmt::println(FMT_STRING("DoFakeVertexAttrib0(fakeVertCount: {},
+      // whatDoesAttrib0Need: {})"), fakeVertCount, (int)whatDoesAttrib0Need);
+      if (!DoFakeVertexAttrib0(fakeVertCount, whatDoesAttrib0Need)) return;
     }
 
     ScopedDrawCallWrapper wrapper(*this);
+
     if (vertCount && instanceCount) {
       if (HasInstancedDrawing(*this)) {
         gl->fDrawArraysInstanced(mode, driverFirst, vertCount, instanceCount);
@@ -829,18 +844,6 @@ void WebGLContext::DrawArraysInstanced(const GLenum mode, const GLint first,
     }
   }
 
-  if (driverFirst != first) {
-    gl->fUniform1i(mActiveProgramLinkInfo->webgl_gl_VertexID_Offset, 0);
-
-    for (const auto& a : activeAttribs) {
-      if (a.location == -1) continue;
-      const auto& binding = mBoundVertexArray->AttribBinding(a.location);
-      if (binding.layout.divisor) continue;
-
-      mBoundVertexArray->DoVertexAttrib(a.location, 0);
-    }
-  }
-
   Draw_cleanup();
   scopedTF.Advance();
 }
@@ -1031,27 +1034,19 @@ void WebGLContext::DrawElementsInstanced(const GLenum mode,
 
   // -
 
-  bool error = false;
-
-  // -
-
-  auto undoAttrib0 = MakeScopeExit([&]() {
-    MOZ_RELEASE_ASSERT(whatDoesAttrib0Need !=
-                       WebGLVertexAttrib0Status::Default);
-    UndoFakeVertexAttrib0();
-  });
-  if (fakeVertCount) {
-    if (!DoFakeVertexAttrib0(fakeVertCount, whatDoesAttrib0Need)) {
-      error = true;
-      undoAttrib0.release();
+  const bool needsFix_FakeVertexAttrib0 = bool(fakeVertCount);
+  const auto undoFix_FakeVertexAttrib0 = MakeScopeExit([&]() {
+    if (needsFix_FakeVertexAttrib0) {
+      mBoundVertexArray->DoVertexAttrib(0);
     }
-  } else {
-    // No fake-verts needed.
-    undoAttrib0.release();
+  });
+  if (needsFix_FakeVertexAttrib0) {
+    if (!DoFakeVertexAttrib0(fakeVertCount, whatDoesAttrib0Need)) return;
   }
 
   // -
 
+  bool error = false;
   const ScopedResolveTexturesForDraw scopedResolve(this, &error);
   if (error) return;
 
@@ -1189,35 +1184,10 @@ bool WebGLContext::DoFakeVertexAttrib0(
     mAlreadyWarnedAboutFakeVertexAttrib0 = true;
   }
 
-  gl->fEnableVertexAttribArray(0);
-  {
-    const auto& attrib0 = mBoundVertexArray->AttribBinding(0);
-    if (attrib0.layout.divisor) {
-      gl->fVertexAttribDivisor(0, 0);
-    }
-  }
-
   if (!mFakeVertexAttrib0BufferObject) {
     gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
-    mFakeVertexAttrib0BufferObjectSize = 0;
-  }
-  gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
-
-  ////
-
-  switch (mGenericVertexAttribTypes[0]) {
-    case webgl::AttribBaseType::Boolean:
-    case webgl::AttribBaseType::Float:
-      gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, false, 0, 0);
-      break;
-
-    case webgl::AttribBaseType::Int:
-      gl->fVertexAttribIPointer(0, 4, LOCAL_GL_INT, 0, 0);
-      break;
-
-    case webgl::AttribBaseType::Uint:
-      gl->fVertexAttribIPointer(0, 4, LOCAL_GL_UNSIGNED_INT, 0, 0);
-      break;
+    mFakeVertexAttrib0BufferAllocSize = 0;
+    mFakeVertexAttrib0BufferInitializedSize = 0;
   }
 
   ////
@@ -1245,71 +1215,104 @@ bool WebGLContext::DoFakeVertexAttrib0(
   }
   const auto dataSize = checked_dataSize.value();
 
-  if (mFakeVertexAttrib0BufferObjectSize < dataSize) {
+  if (mFakeVertexAttrib0BufferAllocSize < dataSize) {
+    gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
     gl::GLContext::LocalErrorScope errorScope(*gl);
 
     gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr,
-                    LOCAL_GL_DYNAMIC_DRAW);
+                    LOCAL_GL_STATIC_DRAW);
 
     const auto err = errorScope.GetError();
Loading diff…