Firefox · Graphics
CVE-2026-16360
Memory Corruption in Graphics
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifgfx/layers/ImageContainer.cpp |
modified | |
CachedSurfacegfx/layers/ImageContainer.h |
modified | |
ifgfx/layers/ImageContainer.h |
modified |
Files Changed
gfx/layers/ImageContainer.cppgfx/layers/ImageContainer.h
Patch
diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp
index 901ced77a29..a243e651089 100644
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -794,10 +794,11 @@ nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
// If we can copy directly from the surface, let's do that to avoid the YUV
// to RGB conversion.
- if (mSourceSurface && mSourceSurface->GetSize() == size) {
- DataSourceSurface::ScopedMap map(mSourceSurface, DataSourceSurface::READ);
+ RefPtr<gfx::DataSourceSurface> sourceSurface = mSourceSurface.Get();
+ if (sourceSurface && sourceSurface->GetSize() == size) {
+ DataSourceSurface::ScopedMap map(sourceSurface, DataSourceSurface::READ);
if (map.IsMapped() && SwizzleData(map.GetData(), map.GetStride(),
- mSourceSurface->GetFormat(), buffer,
+ sourceSurface->GetFormat(), buffer,
stride, format, size)) {
return NS_OK;
}
@@ -987,9 +988,8 @@ nsresult PlanarYCbCrImage::AdoptData(const Data& aData) {
}
already_AddRefed<gfx::SourceSurface> PlanarYCbCrImage::GetAsSourceSurface() {
- if (mSourceSurface) {
- RefPtr<gfx::SourceSurface> surface(mSourceSurface);
- return surface.forget();
+ if (RefPtr<gfx::DataSourceSurface> cached = mSourceSurface.Get()) {
+ return cached.forget();
}
gfx::IntSize size(mSize);
@@ -1019,30 +1019,24 @@ already_AddRefed<gfx::SourceSurface> PlanarYCbCrImage::GetAsSourceSurface() {
return nullptr;
}
- mSourceSurface = surface;
+ mSourceSurface.Set(surface);
return surface.forget();
}
-PlanarYCbCrImage::~PlanarYCbCrImage() {
- NS_ReleaseOnMainThread("PlanarYCbCrImage::mSourceSurface",
- mSourceSurface.forget());
-}
+PlanarYCbCrImage::~PlanarYCbCrImage() = default;
NVImage::NVImage() : Image(nullptr, ImageFormat::NV_IMAGE), mBufferSize(0) {}
-NVImage::~NVImage() {
- NS_ReleaseOnMainThread("NVImage::mSourceSurface", mSourceSurface.forget());
-}
+NVImage::~NVImage() = default;
IntSize NVImage::GetSize() const { return mSize; }
IntRect NVImage::GetPictureRect() const { return mData.mPictureRect; }
already_AddRefed<SourceSurface> NVImage::GetAsSourceSurface() {
- if (mSourceSurface) {
- RefPtr<gfx::SourceSurface> surface(mSourceSurface);
- return surface.forget();
+ if (RefPtr<gfx::DataSourceSurface> cached = mSourceSurface.Get()) {
+ return cached.forget();
}
// Convert the current NV12 or NV21 data to YUV420P so that we can follow the
@@ -1100,7 +1094,7 @@ already_AddRefed<SourceSurface> NVImage::GetAsSourceSurface() {
return nullptr;
}
- mSourceSurface = surface;
+ mSourceSurface.Set(surface);
return surface.forget();
}
@@ -1122,7 +1116,8 @@ nsresult NVImage::BuildSurfaceDescriptorBuffer(
UniquePtr<uint8_t[]> buffer;
- if (!mSourceSurface) {
+ RefPtr<gfx::DataSourceSurface> sourceSurface = mSourceSurface.Get();
+ if (!sourceSurface) {
const int bufferLength =
ySize.height * mData.mYStride + cbcrSize.height * cbcrSize.width * 2;
buffer = MakeUnique<uint8_t[]>(bufferLength);
@@ -1152,7 +1147,7 @@ nsresult NVImage::BuildSurfaceDescriptorBuffer(
return NS_ERROR_FAILURE;
}
- if (mSourceSurface && mSourceSurface->GetSize() != size) {
+ if (sourceSurface && sourceSurface->GetSize() != size) {
return NS_ERROR_NOT_IMPLEMENTED;
}
@@ -1164,7 +1159,7 @@ nsresult NVImage::BuildSurfaceDescriptorBuffer(
return rv;
}
- if (!mSourceSurface) {
+ if (!sourceSurface) {
rv = gfx::ConvertYCbCrToRGB(aData, format, size, output, stride);
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data");
@@ -1173,12 +1168,12 @@ nsresult NVImage::BuildSurfaceDescriptorBuffer(
return NS_OK;
}
- DataSourceSurface::ScopedMap map(mSourceSurface, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap map(sourceSurface, DataSourceSurface::WRITE);
if (NS_WARN_IF(!map.IsMapped())) {
return NS_ERROR_FAILURE;
}
- if (!SwizzleData(map.GetData(), map.GetStride(), mSourceSurface->GetFormat(),
+ if (!SwizzleData(map.GetData(), map.GetStride(), sourceSurface->GetFormat(),
output, stride, format, size)) {
return NS_ERROR_FAILURE;
}
diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h
index 0fe66df2a34..952fe1a3c60 100644
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -9,6 +9,7 @@
#include "ImageTypes.h" // for ImageFormat, etc
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/DataMutex.h" // for DataMutex
#include "mozilla/Mutex.h" // for Mutex
#include "mozilla/RecursiveMutex.h" // for RecursiveMutex, etc
#include "mozilla/ThreadSafeWeakPtr.h"
@@ -24,6 +25,7 @@
#include "nsISupportsImpl.h" // for Image::Release, etc
#include "nsTArray.h" // for nsTArray
#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsProxyRelease.h" // for NS_ReleaseOnMainThread
#include "mozilla/Atomics.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/EnumeratedArray.h"
@@ -219,6 +221,46 @@ class Image {
static mozilla::Atomic<int32_t> sSerialCounter;
};
+/**
+ * A thread-safe, lazily-populated cache of an Image's rasterized surface.
+ */
+class CachedSurface final {
+ public:
+ CachedSurface() : mSurface("layers::CachedSurface") {}
+
+ ~CachedSurface() {
+ // The surface may have been created off the main thread, but like the rest
+ // of the gfx surface machinery it must be released on the main thread.
+ RefPtr<gfx::DataSourceSurface> surface;
+ {
+ auto guard = mSurface.Lock();
+ surface = guard->forget();
+ }
+ NS_ReleaseOnMainThread("layers::CachedSurface", surface.forget());
+ }
+
+ // Returns the cached surface, or nullptr if nothing has been cached yet.
+ already_AddRefed<gfx::DataSourceSurface> Get() {
+ auto guard = mSurface.Lock();
+ RefPtr<gfx::DataSourceSurface> surface = *guard;
+ return surface.forget();
+ }
+
+ // Stores aSurface unless another thread has already cached one. The caller
+ // may keep using its own surface regardless: both are equivalent
+ // rasterizations of the same image, and keeping the first one avoids dropping
+ // a surface that a concurrent caller may already have handed out.
+ void Set(gfx::DataSourceSurface* aSurface) {
+ auto guard = mSurface.Lock();
+ if (!*guard) {
+ *guard = aSurface;
+ }
+ }
+
+ private:
+ mozilla::DataMutex<RefPtr<gfx::DataSourceSurface>> mSurface;
+};
+
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Image::BuildSdbFlags)
/**
@@ -915,7 +957,7 @@ class PlanarYCbCrImage : public Image {
gfx::IntSize mSize;
gfx::ColorDepth mColorDepth = gfx::ColorDepth::COLOR_8;
gfxImageFormat mOffscreenFormat;
- RefPtr<gfx::DataSourceSurface> mSourceSurface;
+ CachedSurface mSourceSurface;
uint32_t mBufferSize;
};
@@ -978,7 +1020,7 @@ class NVImage final : public Image {
uint32_t mBufferSize;
gfx::IntSize mSize;
Data mData;
- RefPtr<gfx::DataSourceSurface> mSourceSurface;
Loading diff…
References
On This Page