High firefox Memory Corruption 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionCertain canvas operations could have lead to memory corruption.
ComponentDOM
Bug ClassMemory Corruption
Tracker1966083
Fix commit2d53f4e1dec2 (firefox) +156/-17
CISA KEVNot listed
CreditedYannis Juglaret and Steven Michaud
Disclosed2025-06-10

Changed Functions

FunctionChangeNotes
if
dom/canvas/TexUnpackBlob.cpp
modified
RecordedCanvasBeginTransaction
gfx/layers/RecordedCanvasEventImpl.h
modified
RecordedAddExportSurface
gfx/layers/RecordedCanvasEventImpl.h
modified
mActualSurface
gfx/layers/RecordedCanvasEventImpl.h
modified
if
gfx/layers/RecordedCanvasEventImpl.h
modified
RecordedRemoveExportSurface
gfx/layers/RecordedCanvasEventImpl.h
modified

Files Changed

  • dom/canvas/TexUnpackBlob.cpp
  • gfx/2d/2D.h
  • gfx/2d/InlineTranslator.h
  • gfx/layers/RecordedCanvasEventImpl.h
  • gfx/layers/ipc/CanvasChild.cpp
  • gfx/layers/ipc/CanvasTranslator.cpp
  • gfx/layers/ipc/CanvasTranslator.h
diff --git a/dom/canvas/TexUnpackBlob.cpp b/dom/canvas/TexUnpackBlob.cpp
index f0c8e11f8d6..815ca11c461 100644
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -1040,6 +1040,30 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
         gfxCriticalNote << "TexUnpackSurface failed to get CanvasSurface";
         return false;
       }
+      if (NS_WARN_IF(surf->GetSize().width < GLint(size.x)) ||
+          NS_WARN_IF(surf->GetSize().height < GLint(size.y))) {
+        RefPtr<gfx::DrawTarget> adjusted = gfx::Factory::CreateDrawTarget(
+            gfx::BackendType::SKIA, gfx::IntSize(size.x, size.y),
+            surf->GetFormat());
+        if (!adjusted) {
+          gfxCriticalNote
+              << "Failed to created adjusted target for CanvasSurface";
+          return false;
+        }
+        adjusted->CopySurface(surf, surf->GetRect(), gfx::IntPoint(0, 0));
+        if (RefPtr<gfx::SourceSurface> snapshot = adjusted->Snapshot()) {
+          surf = snapshot->GetDataSurface();
+          if (!surf) {
+            gfxCriticalNote
+                << "Failed to get adjusted snapshot data for CanvasSurface";
+            return false;
+          }
+        } else {
+          gfxCriticalNote
+              << "Failed to create adjusted snapshot for CanvasSurface";
+          return false;
+        }
+      }
     } else {
       MOZ_ASSERT_UNREACHABLE("Unexpected surface descriptor!");
     }
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
index 11e44b5dad5..c4161836607 100644
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -724,7 +724,7 @@ class SourceSurface : public SupportsThreadSafeWeakPtr<SourceSurface> {
   }
 
   /** Tries to generate a SurfaceDescriptor for the surface, if possible. */
-  virtual bool GetSurfaceDescriptor(layers::SurfaceDescriptor& aDesc) const {
+  virtual bool GetSurfaceDescriptor(layers::SurfaceDescriptor& aDesc) {
     return false;
   }
 
diff --git a/gfx/2d/InlineTranslator.h b/gfx/2d/InlineTranslator.h
index 3e52568b4cc..334edb8404a 100644
--- a/gfx/2d/InlineTranslator.h
+++ b/gfx/2d/InlineTranslator.h
@@ -54,10 +54,6 @@ class InlineTranslator : public Translator {
     return result;
   }
 
-  bool HasSourceSurface(ReferencePtr aRefPtr) const {
-    return mSourceSurfaces.GetWeak(aRefPtr) != nullptr;
-  }
-
   SourceSurface* LookupSourceSurface(ReferencePtr aRefPtr) final {
     SourceSurface* result = mSourceSurfaces.GetWeak(aRefPtr);
     MOZ_ASSERT(result);
diff --git a/gfx/layers/RecordedCanvasEventImpl.h b/gfx/layers/RecordedCanvasEventImpl.h
index 40d443b7769..ea3bef03a7f 100644
--- a/gfx/layers/RecordedCanvasEventImpl.h
+++ b/gfx/layers/RecordedCanvasEventImpl.h
@@ -49,7 +49,9 @@ const EventType PRESENT_TEXTURE = EventType(EventType::LAST + 18);
 const EventType DEVICE_RESET_ACKNOWLEDGED = EventType(EventType::LAST + 19);
 const EventType AWAIT_TRANSLATION_SYNC = EventType(EventType::LAST + 20);
 const EventType RESOLVE_EXTERNAL_SNAPSHOT = EventType(EventType::LAST + 21);
-const EventType LAST_CANVAS_EVENT_TYPE = RESOLVE_EXTERNAL_SNAPSHOT;
+const EventType ADD_EXPORT_SURFACE = EventType(EventType::LAST + 22);
+const EventType REMOVE_EXPORT_SURFACE = EventType(EventType::LAST + 23);
+const EventType LAST_CANVAS_EVENT_TYPE = REMOVE_EXPORT_SURFACE;
 
 class RecordedCanvasBeginTransaction final
     : public RecordedEventDerived<RecordedCanvasBeginTransaction> {
@@ -874,6 +876,92 @@ RecordedPresentTexture::RecordedPresentTexture(S& aStream)
   ReadElement(aStream, mLastRemoteTextureId.mId);
 }
 
+class RecordedAddExportSurface final
+    : public RecordedEventDerived<RecordedAddExportSurface> {
+ public:
+  RecordedAddExportSurface(ReferencePtr aExportID,
+                           const RefPtr<gfx::SourceSurface>& aActualSurface)
+      : RecordedEventDerived(ADD_EXPORT_SURFACE),
+        mExportID(aExportID),
+        mActualSurface(aActualSurface) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedAddExportSurface(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedAddExportSurface"; }
+
+ private:
+  ReferencePtr mExportID;
+  ReferencePtr mActualSurface;
+};
+
+inline bool RecordedAddExportSurface::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  RefPtr<gfx::SourceSurface> surface =
+      aTranslator->LookupSourceSurface(mActualSurface);
+  if (!surface) {
+    return false;
+  }
+
+  aTranslator->AddExportSurface(mExportID, surface);
+  return true;
+}
+
+template <class S>
+void RecordedAddExportSurface::Record(S& aStream) const {
+  WriteElement(aStream, mExportID);
+  WriteElement(aStream, mActualSurface);
+}
+
+template <class S>
+RecordedAddExportSurface::RecordedAddExportSurface(S& aStream)
+    : RecordedEventDerived(ADD_EXPORT_SURFACE) {
+  ReadElement(aStream, mExportID);
+  ReadElement(aStream, mActualSurface);
+}
+
+class RecordedRemoveExportSurface final
+    : public RecordedEventDerived<RecordedRemoveExportSurface> {
+ public:
+  explicit RecordedRemoveExportSurface(ReferencePtr aExportID)
+      : RecordedEventDerived(REMOVE_EXPORT_SURFACE), mExportID(aExportID) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedRemoveExportSurface(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedRemoveExportSurface"; }
+
+ private:
+  ReferencePtr mExportID;
+};
+
+inline bool RecordedRemoveExportSurface::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  aTranslator->RemoveExportSurface(mExportID);
+  return true;
+}
+
+template <class S>
+void RecordedRemoveExportSurface::Record(S& aStream) const {
+  WriteElement(aStream, mExportID);
+}
+
+template <class S>
+RecordedRemoveExportSurface::RecordedRemoveExportSurface(S& aStream)
+    : RecordedEventDerived(REMOVE_EXPORT_SURFACE) {
+  ReadElement(aStream, mExportID);
+}
+
 #define FOR_EACH_CANVAS_EVENT(f)                                    \
   f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction);      \
   f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction);          \
@@ -896,7 +984,9 @@ RecordedPresentTexture::RecordedPresentTexture(S& aStream)
   f(PRESENT_TEXTURE, RecordedPresentTexture);                       \
   f(DEVICE_RESET_ACKNOWLEDGED, RecordedDeviceResetAcknowledged);    \
   f(AWAIT_TRANSLATION_SYNC, RecordedAwaitTranslationSync);          \
-  f(RESOLVE_EXTERNAL_SNAPSHOT, RecordedResolveExternalSnapshot);
+  f(RESOLVE_EXTERNAL_SNAPSHOT, RecordedResolveExternalSnapshot);    \
+  f(ADD_EXPORT_SURFACE, RecordedAddExportSurface);                  \
+  f(REMOVE_EXPORT_SURFACE, RecordedRemoveExportSurface);
 
 }  // namespace layers
 }  // namespace mozilla
diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp
index c32621ef417..93378de00f5 100644
--- a/gfx/layers/ipc/CanvasChild.cpp
+++ b/gfx/layers/ipc/CanvasChild.cpp
@@ -115,19 +115,21 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
 
   ~SourceSurfaceCanvasRecording() {
     ReferencePtr surfaceAlias = this;
+    ReferencePtr exportID = mExportID;
     if (NS_IsMainThread()) {
       ReleaseOnMainThread(std::move(mRecorder), surfaceAlias,
-                          std::move(mRecordedSurface), std::move(mCanvasChild));
+                          std::move(mRecordedSurface), std::move(mCanvasChild),
+                          exportID);
       return;
     }
 
     mRecorder->AddPendingDeletion(
Loading diff…