Bug 944579 - Refuse to create gigantic DrawTargets and surfaces. r=Bas
authorMarkus Stange <mstange@themasta.com>
Fri, 17 Jan 2014 11:06:17 +0100
changeset 163943 99eff7ae7035db16b2b7dfea71d929c0a4dacc3d
parent 163942 6bf3e0350c4a3c760c81b852460cebd8bac7faac
child 163944 942064e2a64e96552a471789de8dda437b042d09
push id38585
push usermstange@themasta.com
push dateFri, 17 Jan 2014 10:08:08 +0000
treeherdermozilla-inbound@99eff7ae7035 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs944579
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 944579 - Refuse to create gigantic DrawTargets and surfaces. r=Bas
gfx/2d/2D.h
gfx/2d/Factory.cpp
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -992,16 +992,22 @@ public:
   virtual ~DrawEventRecorder() { }
 };
 
 class GFX2D_API Factory
 {
 public:
   static bool HasSSE2();
 
+  /* Make sure that the given dimensions don't overflow a 32-bit signed int
+   * using 4 bytes per pixel; optionally, make sure that either dimension
+   * doesn't exceed the given limit.
+   */
+  static bool CheckSurfaceSize(const IntSize &sz, int32_t limit = 0);
+
   static TemporaryRef<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize);
 
   static TemporaryRef<SourceSurface>
     CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface,
                                        SurfaceFormat aFormat);
 
   static TemporaryRef<DrawTarget>
     CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -46,16 +46,18 @@
 #include "DrawTargetRecording.h"
 
 #include "SourceSurfaceRawData.h"
 
 #include "DrawEventRecorder.h"
 
 #include "Logging.h"
 
+#include "mozilla/CheckedInt.h"
+
 #ifdef PR_LOGGING
 PRLogModuleInfo *
 GetGFX2DLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog)
     sLog = PR_NewLogModule("gfx2d");
   return sLog;
@@ -176,19 +178,68 @@ Factory::HasSSE2()
   return true;
 #elif defined(HAVE_CPU_DETECTION)
   return HasCPUIDBit(1u, edx, (1u<<26));
 #else
   return false;
 #endif
 }
 
+bool
+Factory::CheckSurfaceSize(const IntSize &sz, int32_t limit)
+{
+  if (sz.width < 0 || sz.height < 0) {
+    gfxDebug() << "Surface width or height < 0!";
+    return false;
+  }
+
+  // reject images with sides bigger than limit
+  if (limit && (sz.width > limit || sz.height > limit)) {
+    gfxDebug() << "Surface size too large (exceeds caller's limit)!";
+    return false;
+  }
+
+  // make sure the surface area doesn't overflow a int32_t
+  CheckedInt<int32_t> tmp = sz.width;
+  tmp *= sz.height;
+  if (!tmp.isValid()) {
+    gfxDebug() << "Surface size too large (would overflow)!";
+    return false;
+  }
+
+  // assuming 4 bytes per pixel, make sure the allocation size
+  // doesn't overflow a int32_t either
+  CheckedInt<int32_t> stride = sz.width;
+  stride *= 4;
+
+  // When aligning the stride to 16 bytes, it can grow by up to 15 bytes.
+  stride += 16 - 1;
+
+  if (!stride.isValid()) {
+    gfxDebug() << "Surface size too large (stride overflows int32_t)!";
+    return false;
+  }
+
+  CheckedInt<int32_t> numBytes = GetAlignedStride<16>(sz.width * 4);
+  numBytes *= sz.height;
+  if (!numBytes.isValid()) {
+    gfxDebug() << "Surface size too large (allocation size would overflow int32_t)!";
+    return false;
+  }
+
+  return true;
+}
+
 TemporaryRef<DrawTarget>
 Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat)
 {
+  if (!CheckSurfaceSize(aSize)) {
+    return nullptr;
+  }
+
   RefPtr<DrawTarget> retVal;
   switch (aBackend) {
 #ifdef WIN32
   case BackendType::DIRECT2D:
     {
       RefPtr<DrawTargetD2D> newTarget;
       newTarget = new DrawTargetD2D();
       if (newTarget->Init(aSize, aFormat)) {
@@ -268,16 +319,20 @@ Factory::CreateRecordingDrawTarget(DrawE
 
 TemporaryRef<DrawTarget>
 Factory::CreateDrawTargetForData(BackendType aBackend, 
                                  unsigned char *aData, 
                                  const IntSize &aSize, 
                                  int32_t aStride, 
                                  SurfaceFormat aFormat)
 {
+  if (!CheckSurfaceSize(aSize)) {
+    return nullptr;
+  }
+
   RefPtr<DrawTarget> retVal;
 
   switch (aBackend) {
 #ifdef USE_SKIA
   case BackendType::SKIA:
     {
       RefPtr<DrawTargetSkia> newTarget;
       newTarget = new DrawTargetSkia();
@@ -638,16 +693,20 @@ Factory::CreateWrappingDataSourceSurface
 
   return nullptr;
 }
 
 TemporaryRef<DataSourceSurface>
 Factory::CreateDataSourceSurface(const IntSize &aSize,
                                  SurfaceFormat aFormat)
 {
+  if (!CheckSurfaceSize(aSize)) {
+    return nullptr;
+  }
+
   RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
   if (newSurf->Init(aSize, aFormat)) {
     return newSurf;
   }
 
   return nullptr;
 }