High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionPrivilege escalation due to use-after-free in the Graphics: Canvas2D component
ComponentGraphics
Bug ClassLogic Error
Tracker2059011
Fix commit62e595ba156c (firefox) +19/-0
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-27

Changed Functions

FunctionChangeNotes
while
gfx/2d/DrawTargetCairo.cpp
modified
if
gfx/2d/DrawTargetSkia.cpp
modified

Files Changed

  • gfx/2d/DrawTargetCairo.cpp
  • gfx/2d/DrawTargetRecording.cpp
  • gfx/2d/DrawTargetSkia.cpp
  • gfx/2d/DrawTargetSkia.h
diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp
index 121b0f0f6ef..24c18750d61 100644
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1720,6 +1720,9 @@ void DrawTargetCairo::PopClip() {
 }
 
 bool DrawTargetCairo::RemoveAllClips() {
+  if (!mPushedLayers.empty()) {
+    return false;
+  }
   while (mClipDepth > 0) {
     PopClip();
   }
diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp
index 4d213d393a0..1f8ad9ea5b5 100644
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -765,6 +765,9 @@ void DrawTargetRecording::PopClip() {
 }
 
 bool DrawTargetRecording::RemoveAllClips() {
+  if (!mPushedLayers.empty()) {
+    return false;
+  }
   RecordEventSelfSkipFlushTransform(RecordedRemoveAllClips());
   return true;
 }
diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp
index 2d1fdb75aad..2431e8e4e4e 100644
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -2151,11 +2151,18 @@ void DrawTargetSkia::PushClipRect(const Rect& aRect) {
 }
 
 void DrawTargetSkia::PopClip() {
+  if (!mPushedLayers.empty() &&
+      mCanvas->getSaveCount() <= mPushedLayers.back().mSaveCount) {
+    return;
+  }
   mCanvas->restore();
   SetTransform(GetTransform());
 }
 
 bool DrawTargetSkia::RemoveAllClips() {
+  if (!mPushedLayers.empty()) {
+    return false;
+  }
   mCanvas->restoreToCount(1);
   SetTransform(GetTransform());
   return true;
@@ -2272,6 +2279,7 @@ void DrawTargetSkia::PushLayerWithBlend(bool aOpaque, Float aOpacity,
           (aCopyBackground ? SkCanvas::kInitWithPrevious_SaveLayerFlag : 0));
 
   mCanvas->saveLayer(saveRec);
+  mPushedLayers.back().mSaveCount = mCanvas->getSaveCount();
 
   SetPermitSubpixelAA(aOpaque);
 
@@ -2307,6 +2315,10 @@ void DrawTargetSkia::PopLayer() {
     }
   }
 
+  // Remove any unbalanced saves before the layer restore.
+  if (mCanvas->getSaveCount() > layer.mSaveCount) {
+    mCanvas->restoreToCount(layer.mSaveCount);
+  }
   // Restore the layer itself, and the clip we pushed for the mask bounds.
   mCanvas->restore();
   if (layer.mMask) {
diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h
index 34641cd0047..a5b3907a950 100644
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -192,6 +192,7 @@ class DrawTargetSkia : public DrawTarget {
     Matrix mMaskToDevice;
     CompositionOp mCompositionOp = CompositionOp::OP_OVER;
     bool mOldPermitSubpixelAA = false;
+    int mSaveCount = 0;
   };
   std::vector<PushedLayer> mPushedLayers;
 
Loading diff…