Bug 1466613 - Robustify DrawTargetRecording codepaths that create new drawtargets. r=mstange
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 13 Nov 2018 10:39:02 +0000
changeset 489080 75de649833be8205dab1366634f550858049486c
parent 489079 e80a0b2502e961b5baa5ede031de7b8c266294f8
child 489081 dae86d421021b034d6a2994447157d8518bbc049
push idunknown
push userunknown
push dateunknown
reviewersmstange
bugs1466613
milestone65.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 1466613 - Robustify DrawTargetRecording codepaths that create new drawtargets. r=mstange Badly-behaved consumers of DrawTargetRecording can trigger recording of draw calls that will fail to allocate required draw targets when the recording is replayed. This patch tries to guard against this by detecting these situations at record-time rather than crashing at replay-time. When such a situation is detected, it will crash (for content processes, to catch such scenarios) or gracefully fail (for other processes). Differential Revision: https://phabricator.services.mozilla.com/D11527
gfx/2d/2D.h
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
gfx/2d/DrawTargetDual.cpp
gfx/2d/DrawTargetDual.h
gfx/2d/DrawTargetOffset.h
gfx/2d/DrawTargetRecording.cpp
gfx/2d/DrawTargetRecording.h
gfx/2d/DrawTargetSkia.cpp
gfx/2d/DrawTargetSkia.h
gfx/2d/DrawTargetTiled.h
gfx/2d/DrawTargetWrapAndRecord.cpp
gfx/2d/DrawTargetWrapAndRecord.h
gfx/tests/crashtests/1470437.html
gfx/tests/crashtests/crashtests.list
layout/generic/TextDrawTarget.h
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1371,16 +1371,28 @@ public:
 
   /**
    * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
    */
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0;
 
   /**
+   * Returns false if CreateSimilarDrawTarget would return null with the same
+   * parameters. May return true even in cases where CreateSimilarDrawTarget
+   * return null (i.e. this function returning false has meaning, but returning
+   * true doesn't guarantee anything).
+   */
+  virtual bool
+    CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+  {
+    return true;
+  }
+
+  /**
    * Create a draw target optimized for drawing a shadow.
    *
    * Note that aSigma is the blur radius that must be used when we draw the
    * shadow. Also note that this doesn't affect the size of the allocated
    * surface, the caller is still responsible for including the shadow area in
    * its size.
    */
   virtual already_AddRefed<DrawTarget>
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -999,16 +999,23 @@ DrawTargetD2D1::CreateSimilarDrawTarget(
 
   if (!dt->Init(aSize, aFormat)) {
     return nullptr;
   }
 
   return dt.forget();
 }
 
+bool
+DrawTargetD2D1::CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+  return (mDC->GetMaximumBitmapSize() >= UINT32(aSize.width) &&
+          mDC->GetMaximumBitmapSize() >= UINT32(aSize.height));
+}
+
 already_AddRefed<PathBuilder>
 DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const
 {
   RefPtr<ID2D1PathGeometry> path;
   HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path));
 
   if (FAILED(hr)) {
     gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hexa(hr);
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -112,16 +112,18 @@ public:
                                                                   SurfaceFormat aFormat) const override;
   virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
 
   virtual already_AddRefed<SourceSurface>
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override { return nullptr; }
   
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+  virtual bool
+    CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
 
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
 
   virtual already_AddRefed<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
                         ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
 
--- a/gfx/2d/DrawTargetDual.cpp
+++ b/gfx/2d/DrawTargetDual.cpp
@@ -215,10 +215,16 @@ DrawTargetDual::PushLayer(bool aOpaque, 
 already_AddRefed<DrawTarget>
 DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
 {
   /* Now that we have PushLayer there a very few cases where a user of DrawTargetDual
    * wants to have a DualTarget when creating a similar one. */
   return mA->CreateSimilarDrawTarget(aSize, aFormat);
 }
 
+bool
+DrawTargetDual::CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+  return mA->CanCreateSimilarDrawTarget(aSize, aFormat);
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetDual.h
+++ b/gfx/2d/DrawTargetDual.h
@@ -138,17 +138,19 @@ public:
   virtual already_AddRefed<SourceSurface>
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
   {
     return mA->CreateSourceSurfaceFromNativeSurface(aSurface);
   }
      
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
-     
+  virtual bool
+    CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
   {
     return mA->CreatePathBuilder(aFillRule);
   }
      
   virtual already_AddRefed<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
--- a/gfx/2d/DrawTargetOffset.h
+++ b/gfx/2d/DrawTargetOffset.h
@@ -134,16 +134,22 @@ public:
   }
 
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override
   {
     return mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
   }
 
+  virtual bool
+    CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override
+  {
+    return mDrawTarget->CanCreateSimilarDrawTarget(aSize, aFormat);
+  }
+
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
   {
     return mDrawTarget->CreatePathBuilder(aFillRule);
   }
 
   virtual already_AddRefed<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -562,28 +562,49 @@ DrawTargetRecording::CreateSourceSurface
 {
   MOZ_ASSERT(false);
   return nullptr;
 }
 
 already_AddRefed<DrawTarget>
 DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
 {
-  RefPtr<DrawTarget> similarDT = new DrawTargetRecording(this, aSize, aFormat);
-  mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(similarDT.get(),
-                                                         aSize,
-                                                         aFormat));
+  RefPtr<DrawTarget> similarDT;
+  if (mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat)) {
+    similarDT = new DrawTargetRecording(this, aSize, aFormat);
+    mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(similarDT.get(),
+                                                           aSize,
+                                                           aFormat));
+  } else if (XRE_IsContentProcess()) {
+    // Crash any content process that calls this function with arguments that
+    // would fail to create a similar draw target. We do this to root out bad
+    // callers. We don't want to crash any important processes though so for
+    // for those we'll just gracefully return nullptr.
+    MOZ_CRASH("Content-process DrawTargetRecording can't create requested similar drawtarget");
+  }
   return similarDT.forget();
 }
 
+bool
+DrawTargetRecording::CanCreateSimilarDrawTarget(const IntSize& aSize, SurfaceFormat aFormat) const
+{
+  return mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat);
+}
+
 RefPtr<DrawTarget>
 DrawTargetRecording::CreateClippedDrawTarget(const IntSize& aMaxSize, const Matrix& aTransform, SurfaceFormat aFormat) const
 {
-  RefPtr<DrawTarget> similarDT = new DrawTargetRecording(this, aMaxSize, aFormat);
-  mRecorder->RecordEvent(RecordedCreateClippedDrawTarget(similarDT.get(), aMaxSize, aTransform, aFormat));
+  RefPtr<DrawTarget> similarDT;
+  if (mFinalDT->CanCreateSimilarDrawTarget(aMaxSize, aFormat)) {
+   similarDT = new DrawTargetRecording(this, aMaxSize, aFormat);
+    mRecorder->RecordEvent(RecordedCreateClippedDrawTarget(similarDT.get(), aMaxSize, aTransform, aFormat));
+  } else if (XRE_IsContentProcess()) {
+    // See CreateSimilarDrawTarget
+    MOZ_CRASH("Content-process DrawTargetRecording can't create requested clipped drawtarget");
+  }
   return similarDT;
 }
 
 already_AddRefed<PathBuilder>
 DrawTargetRecording::CreatePathBuilder(FillRule aFillRule) const
 {
   RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(aFillRule);
   return MakeAndAddRef<PathBuilderRecording>(builder, aFillRule);
--- a/gfx/2d/DrawTargetRecording.h
+++ b/gfx/2d/DrawTargetRecording.h
@@ -298,17 +298,19 @@ public:
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
 
   /*
    * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
    */
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
 
-   /**
+  bool CanCreateSimilarDrawTarget(const IntSize& aSize,
+                                  SurfaceFormat aFormat) const override;
+  /**
    * Create a similar DrawTarget whose requested size may be clipped based
    * on this DrawTarget's rect transformed to the new target's space.
    */
   virtual RefPtr<DrawTarget> CreateClippedDrawTarget(const IntSize& aMaxSize,
                                                      const Matrix& aTransform,
                                                      SurfaceFormat aFormat) const override;
 
   /*
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -1701,16 +1701,22 @@ DrawTargetSkia::CreateSimilarDrawTarget(
 
   if (!target->Init(aSize, aFormat)) {
     return nullptr;
   }
   return target.forget();
 }
 
 bool
+DrawTargetSkia::CanCreateSimilarDrawTarget(const IntSize& aSize, SurfaceFormat aFormat) const
+{
+  return size_t(std::max(aSize.width, aSize.height)) < GetMaxSurfaceSize();
+}
+
+bool
 DrawTargetSkia::UsingSkiaGPU() const
 {
 #ifdef USE_SKIA_GPU
   return !!mGrContext;
 #else
   return false;
 #endif
 }
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -121,16 +121,17 @@ public:
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const override;
   virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
   virtual already_AddRefed<SourceSurface> OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const override;
   virtual already_AddRefed<SourceSurface>
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+  virtual bool CanCreateSimilarDrawTarget(const IntSize& aSize, SurfaceFormat aFormat) const override;
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
   virtual already_AddRefed<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
   virtual void SetTransform(const Matrix &aTransform) override;
   virtual void *GetNativeSurface(NativeSurfaceType aType) override;
   virtual void DetachAllSnapshots() override { MarkChanged(); }
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
--- a/gfx/2d/DrawTargetTiled.h
+++ b/gfx/2d/DrawTargetTiled.h
@@ -149,16 +149,20 @@ public:
   }
 
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override
   {
     return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
   }
 
+  virtual bool CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override {
+    return mTiles[0].mDrawTarget->CanCreateSimilarDrawTarget(aSize, aFormat);
+  }
+
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
   {
     return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule);
   }
 
   virtual already_AddRefed<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
--- a/gfx/2d/DrawTargetWrapAndRecord.cpp
+++ b/gfx/2d/DrawTargetWrapAndRecord.cpp
@@ -670,16 +670,22 @@ DrawTargetWrapAndRecord::CreateSimilarDr
   if (!similarDT) {
     return nullptr;
   }
 
   similarDT = new DrawTargetWrapAndRecord(this, similarDT);
   return similarDT.forget();
 }
 
+bool
+DrawTargetWrapAndRecord::CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+  return mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat);
+}
+
 already_AddRefed<PathBuilder>
 DrawTargetWrapAndRecord::CreatePathBuilder(FillRule aFillRule) const
 {
   RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(aFillRule);
   return MakeAndAddRef<PathBuilderRecording>(builder, aFillRule);
 }
 
 already_AddRefed<GradientStops>
--- a/gfx/2d/DrawTargetWrapAndRecord.h
+++ b/gfx/2d/DrawTargetWrapAndRecord.h
@@ -265,16 +265,19 @@ public:
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
 
   /*
    * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
    */
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
 
+  virtual bool
+    CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
   /*
    * Create a path builder with the specified fillmode.
    *
    * We need the fill mode up front because of Direct2D.
    * ID2D1SimplifiedGeometrySink requires the fill mode
    * to be set before calling BeginFigure().
    */
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/1470437.html
@@ -0,0 +1,10 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>
+:last-child{
+    background-image: -moz-element(#id3);
+    border-width: 9596.6vmin;
+    border-style: dotted;
+}
+</style>
+</head><body><rect id="id3">
+</rect></body></html>
\ No newline at end of file
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -162,13 +162,14 @@ load 1308394.html
 load 1317403-1.html # bug 1331533
 load 1325159-1.html
 load 1331683.html
 skip-if(Android) pref(dom.disable_open_during_load,false) load 1343666.html
 load 1408078-1.html
 load 1464243.html
 load 1467847-1.html
 load 1468020.html
+load 1470437.html
 load 1470440.html
 load 1478035.html
 load 1490704-1.html
 load 1501518.html
 load 1503986-1.html
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -564,16 +564,22 @@ public:
   }
 
   already_AddRefed<DrawTarget>
   CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
     return nullptr;
   }
 
+  bool
+  CanCreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override {
+    MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
+    return false;
+  }
+
   already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule) const override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
     return nullptr;
   }
 
   already_AddRefed<FilterNode> CreateFilter(FilterType aType) override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
     return nullptr;