Bug 963086 - Detect filter rect XMost()/YMost() overflow and protect against out-of-range data acess. f=dholbert, r=Bas
authorMarkus Stange <mstange@themasta.com>
Wed, 05 Mar 2014 18:41:19 +0100
changeset 189280 5d244132884677d96f46a417533096dc74c3c8d1
parent 189279 ce0ac2767d138e2280490bcc910eb0c339864555
child 189281 05e5db54dd890c17473d9f5a276d26f5f6a95028
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs963086
milestone30.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 963086 - Detect filter rect XMost()/YMost() overflow and protect against out-of-range data acess. f=dholbert, r=Bas
gfx/2d/FilterNodeSoftware.cpp
layout/svg/crashtests/963086-1.svg
layout/svg/crashtests/crashtests.list
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -185,42 +185,81 @@ NS_lround(double x)
 void
 ClearDataSourceSurface(DataSourceSurface *aSurface)
 {
   size_t numBytes = aSurface->GetSize().height * aSurface->Stride();
   uint8_t* data = aSurface->GetData();
   PodZero(data, numBytes);
 }
 
-static ptrdiff_t
-DataOffset(DataSourceSurface* aSurface, IntPoint aPoint)
+// This check is safe against integer overflow.
+static bool
+SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
+{
+  IntSize size = aSurface->GetSize();
+  return aPoint.x >= 0 && aPoint.x < size.width &&
+         aPoint.y >= 0 && aPoint.y < size.height;
+}
+
+static uint8_t*
+DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint)
 {
-  return aPoint.y * aSurface->Stride() +
-         aPoint.x * BytesPerPixel(aSurface->GetFormat());
+  if (!SurfaceContainsPoint(aSurface, aPoint)) {
+    MOZ_CRASH("sample position needs to be inside surface!");
+  }
+
+  MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
+             "surface size overflows - this should have been prevented when the surface was created");
+
+  uint8_t* data = aSurface->GetData() + aPoint.y * aSurface->Stride() +
+    aPoint.x * BytesPerPixel(aSurface->GetFormat());
+
+  if (data < aSurface->GetData()) {
+    MOZ_CRASH("out-of-range data access");
+  }
+
+  return data;
+}
+
+static bool
+IntRectOverflows(const IntRect& aRect)
+{
+  CheckedInt<int32_t> xMost = aRect.x;
+  xMost += aRect.width;
+  CheckedInt<int32_t> yMost = aRect.y;
+  yMost += aRect.height;
+  return !xMost.isValid() || !yMost.isValid();
 }
 
 /**
  * aSrcRect: Rect relative to the aSrc surface
  * aDestPoint: Point inside aDest surface
  */
 static void
 CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
          IntRect aSrcRect, IntPoint aDestPoint)
 {
+  if (IntRectOverflows(aSrcRect) ||
+      IntRectOverflows(IntRect(aDestPoint, aSrcRect.Size()))) {
+    MOZ_CRASH("we should never be getting invalid rects at this point");
+  }
+
   MOZ_ASSERT(aSrc->GetFormat() == aDest->GetFormat(), "different surface formats");
   MOZ_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect), "source rect too big for source surface");
   MOZ_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(aSrcRect - aSrcRect.TopLeft() + aDestPoint), "dest surface too small");
-  uint8_t* sourceData = aSrc->GetData();
+
+  if (aSrcRect.IsEmpty()) {
+    return;
+  }
+
+  uint8_t* sourceData = DataAtOffset(aSrc, aSrcRect.TopLeft());
   uint32_t sourceStride = aSrc->Stride();
-  uint8_t* destData = aDest->GetData();
+  uint8_t* destData = DataAtOffset(aDest, aDestPoint);
   uint32_t destStride = aDest->Stride();
 
-  sourceData += DataOffset(aSrc, aSrcRect.TopLeft());
-  destData += DataOffset(aDest, aDestPoint);
-
   if (BytesPerPixel(aSrc->GetFormat()) == 4) {
     for (int32_t y = 0; y < aSrcRect.height; y++) {
       PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width);
       sourceData += sourceStride;
       destData += destStride;
     }
   } else if (BytesPerPixel(aSrc->GetFormat()) == 1) {
     for (int32_t y = 0; y < aSrcRect.height; y++) {
@@ -240,20 +279,25 @@ CloneAligned(DataSourceSurface* aSource)
     CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint());
   }
   return copy;
 }
 
 static void
 FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos)
 {
-  uint8_t* data = aSurface->GetData();
-  uint8_t* sourcePixelData = data + DataOffset(aSurface, aPixelPos);
+  MOZ_ASSERT(!IntRectOverflows(aFillRect));
+  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+             "aFillRect needs to be completely inside the surface");
+  MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
+             "aPixelPos needs to be inside the surface");
+
   int32_t stride = aSurface->Stride();
-  data += DataOffset(aSurface, aFillRect.TopLeft());
+  uint8_t* sourcePixelData = DataAtOffset(aSurface, aPixelPos);
+  uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
   int bpp = BytesPerPixel(aSurface->GetFormat());
 
   // Fill the first row by hand.
   if (bpp == 4) {
     uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
     for (int32_t x = 0; x < aFillRect.width; x++) {
       *((uint32_t*)data + x) = sourcePixel;
     }
@@ -268,20 +312,26 @@ FillRectWithPixel(DataSourceSurface *aSu
   }
 }
 
 static void
 FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface,
                                                const IntRect &aFillRect,
                                                const IntRect &aSampleRect)
 {
-  uint8_t* data = aSurface->GetData();
+  MOZ_ASSERT(!IntRectOverflows(aFillRect));
+  MOZ_ASSERT(!IntRectOverflows(aSampleRect));
+  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+             "aFillRect needs to be completely inside the surface");
+  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
+             "aSampleRect needs to be completely inside the surface");
+
   int32_t stride = aSurface->Stride();
-  uint8_t* sampleData = data + DataOffset(aSurface, aSampleRect.TopLeft());
-  data += DataOffset(aSurface, aFillRect.TopLeft());
+  uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
+  uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
   if (BytesPerPixel(aSurface->GetFormat()) == 4) {
     for (int32_t y = 0; y < aFillRect.height; y++) {
       PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width);
       data += stride;
     }
   } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
     for (int32_t y = 0; y < aFillRect.height; y++) {
       PodCopy(data, sampleData, aFillRect.width);
@@ -290,20 +340,26 @@ FillRectWithVerticallyRepeatingHorizonta
   }
 }
 
 static void
 FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface,
                                                const IntRect &aFillRect,
                                                const IntRect &aSampleRect)
 {
-  uint8_t* data = aSurface->GetData();
+  MOZ_ASSERT(!IntRectOverflows(aFillRect));
+  MOZ_ASSERT(!IntRectOverflows(aSampleRect));
+  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+             "aFillRect needs to be completely inside the surface");
+  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
+             "aSampleRect needs to be completely inside the surface");
+
   int32_t stride = aSurface->Stride();
-  uint8_t* sampleData = data + DataOffset(aSurface, aSampleRect.TopLeft());
-  data += DataOffset(aSurface, aFillRect.TopLeft());
+  uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
+  uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
   if (BytesPerPixel(aSurface->GetFormat()) == 4) {
     for (int32_t y = 0; y < aFillRect.height; y++) {
       int32_t sampleColor = *((uint32_t*)sampleData);
       for (int32_t x = 0; x < aFillRect.width; x++) {
         *((uint32_t*)data + x) = sampleColor;
       }
       data += stride;
       sampleData += stride;
@@ -316,16 +372,20 @@ FillRectWithHorizontallyRepeatingVertica
       sampleData += stride;
     }
   }
 }
 
 static void
 DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect)
 {
+  MOZ_ASSERT(!IntRectOverflows(aFromRect));
+  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
+             "aFromRect needs to be completely inside the surface");
+
   IntSize size = aSurface->GetSize();
   IntRect fill;
   IntRect sampleRect;
   for (int32_t ix = 0; ix < 3; ix++) {
     switch (ix) {
       case 0:
         fill.x = 0;
         fill.width = aFromRect.x;
@@ -419,16 +479,23 @@ TileSurface(DataSourceSurface* aSource, 
 
 static TemporaryRef<DataSourceSurface>
 GetDataSurfaceInRect(SourceSurface *aSurface,
                      const IntRect &aSurfaceRect,
                      const IntRect &aDestRect,
                      ConvolveMatrixEdgeMode aEdgeMode)
 {
   MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty());
+
+  if (IntRectOverflows(aSurfaceRect) || IntRectOverflows(aDestRect)) {
+    // We can't rely on the intersection calculations below to make sense when
+    // XMost() or YMost() overflow. Bail out.
+    return nullptr;
+  }
+
   IntRect sourceRect = aSurfaceRect;
 
   if (sourceRect.IsEqualEdges(aDestRect)) {
     return aSurface ? aSurface->GetDataSurface() : nullptr;
   }
 
   IntRect intersect = sourceRect.Intersect(aDestRect);
   IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
@@ -562,19 +629,33 @@ FilterNodeSoftware::Draw(DrawTarget* aDr
                          const DrawOptions &aOptions)
 {
 #ifdef DEBUG_DUMP_SURFACES
   printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName());
 #endif
 
   Rect renderRect = aSourceRect;
   renderRect.RoundOut();
-  IntRect renderIntRect(int32_t(renderRect.x), int32_t(renderRect.y),
-                        int32_t(renderRect.width), int32_t(renderRect.height));
-  IntRect outputRect = renderIntRect.Intersect(GetOutputRectInRect(renderIntRect));
+  IntRect renderIntRect;
+  if (!renderRect.ToIntRect(&renderIntRect)) {
+#ifdef DEBUG_DUMP_SURFACES
+    printf("render rect overflowed, not painting anything\n");
+    printf("</pre>\n");
+#endif
+    return;
+  }
+
+  IntRect outputRect = GetOutputRectInRect(renderIntRect);
+  if (IntRectOverflows(outputRect)) {
+#ifdef DEBUG_DUMP_SURFACES
+    printf("output rect overflowed, not painting anything\n");
+    printf("</pre>\n");
+#endif
+    return;
+  }
 
   RefPtr<DataSourceSurface> result;
   if (!outputRect.IsEmpty()) {
     result = GetOutput(outputRect);
   }
 
   if (!result) {
     // Null results are allowed and treated as transparent. Don't draw anything.
@@ -608,16 +689,21 @@ FilterNodeSoftware::Draw(DrawTarget* aDr
                              DrawSurfaceOptions(), aOptions);
   }
 }
 
 TemporaryRef<DataSourceSurface>
 FilterNodeSoftware::GetOutput(const IntRect &aRect)
 {
   MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
+
+  if (IntRectOverflows(aRect)) {
+    return nullptr;
+  }
+
   if (!mCachedRect.Contains(aRect)) {
     RequestRect(aRect);
     mCachedOutput = Render(mRequestedRect);
     if (!mCachedOutput) {
       mCachedRect = IntRect();
       mRequestedRect = IntRect();
       return nullptr;
     }
@@ -634,16 +720,20 @@ FilterNodeSoftware::RequestRect(const In
 {
   mRequestedRect = mRequestedRect.Union(aRect);
   RequestFromInputsForRect(aRect);
 }
 
 void
 FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
 {
+  if (IntRectOverflows(aRect)) {
+    return;
+  }
+
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
     MOZ_CRASH();
   }
   if (mInputSurfaces[inputIndex]) {
     return;
   }
   RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
@@ -663,16 +753,20 @@ FilterNodeSoftware::DesiredFormat(Surfac
 
 TemporaryRef<DataSourceSurface>
 FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex,
                                               const IntRect& aRect,
                                               FormatHint aFormatHint,
                                               ConvolveMatrixEdgeMode aEdgeMode,
                                               const IntRect *aTransparencyPaddedSourceRect)
 {
+  if (IntRectOverflows(aRect)) {
+    return nullptr;
+  }
+
 #ifdef DEBUG_DUMP_SURFACES
   printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
          aRect.x, aRect.y, aRect.width, aRect.height);
 #endif
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
     MOZ_CRASH();
     return nullptr;
@@ -761,16 +855,20 @@ FilterNodeSoftware::GetInputDataSourceSu
 
   return result;
 }
 
 IntRect
 FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
                                        const IntRect &aInRect)
 {
+  if (IntRectOverflows(aInRect)) {
+    return IntRect();
+  }
+
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
     MOZ_CRASH();
     return IntRect();
   }
   if (mInputSurfaces[inputIndex]) {
     return aInRect.Intersect(IntRect(IntPoint(0, 0),
                                      mInputSurfaces[inputIndex]->GetSize()));
@@ -975,17 +1073,21 @@ FilterNodeTransformSoftware::SourceRectF
 
   Matrix inverted(mMatrix);
   if (!inverted.Invert()) {
     return IntRect();
   }
 
   Rect neededRect = inverted.TransformBounds(Rect(aRect));
   neededRect.RoundOut();
-  return GetInputRectInRect(IN_TRANSFORM_IN, RoundedToInt(neededRect));
+  IntRect neededIntRect;
+  if (!neededRect.ToIntRect(&neededIntRect)) {
+    return IntRect();
+  }
+  return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
 }
 
 TemporaryRef<DataSourceSurface>
 FilterNodeTransformSoftware::Render(const IntRect& aRect)
 {
   IntRect srcRect = SourceRectForOutputRect(aRect);
 
   RefPtr<DataSourceSurface> input =
@@ -1027,17 +1129,21 @@ FilterNodeTransformSoftware::GetOutputRe
 {
   IntRect srcRect = SourceRectForOutputRect(aRect);
   if (srcRect.IsEmpty()) {
     return IntRect();
   }
 
   Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
   outRect.RoundOut();
-  return RoundedToInt(outRect).Intersect(aRect);
+  IntRect outIntRect;
+  if (!outRect.ToIntRect(&outIntRect)) {
+    return IntRect();
+  }
+  return outIntRect.Intersect(aRect);
 }
 
 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
  : mOperator(MORPHOLOGY_OPERATOR_ERODE)
 {}
 
 int32_t
 FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex)
@@ -1086,39 +1192,36 @@ ApplyMorphology(const IntRect& aSourceRe
     tmp = aInput;
   } else {
     tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8);
     if (!tmp) {
       return nullptr;
     }
 
     int32_t sourceStride = aInput->Stride();
-    uint8_t* sourceData = aInput->GetData();
-    sourceData += DataOffset(aInput, destRect.TopLeft() - srcRect.TopLeft());
+    uint8_t* sourceData = DataAtOffset(aInput, destRect.TopLeft() - srcRect.TopLeft());
 
     int32_t tmpStride = tmp->Stride();
-    uint8_t* tmpData = tmp->GetData();
-    tmpData += DataOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
+    uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
 
     FilterProcessing::ApplyMorphologyHorizontal(
       sourceData, sourceStride, tmpData, tmpStride, tmpRect, rx, aOperator);
   }
 
   RefPtr<DataSourceSurface> dest;
   if (ry == 0) {
     dest = tmp;
   } else {
     dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8);
     if (!dest) {
       return nullptr;
     }
 
     int32_t tmpStride = tmp->Stride();
-    uint8_t* tmpData = tmp->GetData();
-    tmpData += DataOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
+    uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
 
     int32_t destStride = dest->Stride();
     uint8_t* destData = dest->GetData();
 
     FilterProcessing::ApplyMorphologyVertical(
       tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
   }
 
@@ -2276,24 +2379,23 @@ FilterNodeConvolveMatrixSoftware::DoRend
 
   RefPtr<DataSourceSurface> target =
     Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
   if (!target) {
     return nullptr;
   }
   ClearDataSourceSurface(target);
 
-  uint8_t* sourceData = input->GetData();
+  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+  uint8_t* sourceData = DataAtOffset(input, offset);
   int32_t sourceStride = input->Stride();
   uint8_t* targetData = target->GetData();
   int32_t targetStride = target->Stride();
 
-  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
-  sourceData += DataOffset(input, offset);
-
   // Why exactly are we reversing the kernel?
   std::vector<Float> kernel = ReversedVector(mKernelMatrix);
   kernel = ScaledVector(kernel, mDivisor);
   Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
                                 MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
   maxResultAbs = std::max(maxResultAbs, 1.0f);
 
   double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
@@ -2423,26 +2525,25 @@ FilterNodeDisplacementMapSoftware::Rende
   RefPtr<DataSourceSurface> map =
     GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
   RefPtr<DataSourceSurface> target =
     Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
   if (!input || !map || !target) {
     return nullptr;
   }
 
-  uint8_t* sourceData = input->GetData();
+  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+  uint8_t* sourceData = DataAtOffset(input, offset);
   int32_t sourceStride = input->Stride();
   uint8_t* mapData = map->GetData();
   int32_t mapStride = map->Stride();
   uint8_t* targetData = target->GetData();
   int32_t targetStride = target->Stride();
 
-  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
-  sourceData += DataOffset(input, offset);
-
   static const ptrdiff_t channelMap[4] = {
                              B8G8R8A8_COMPONENT_BYTEOFFSET_R,
                              B8G8R8A8_COMPONENT_BYTEOFFSET_G,
                              B8G8R8A8_COMPONENT_BYTEOFFSET_B,
                              B8G8R8A8_COMPONENT_BYTEOFFSET_A };
   uint16_t xChannel = channelMap[mChannelX];
   uint16_t yChannel = channelMap[mChannelY];
 
@@ -2892,18 +2993,19 @@ FilterNodeCropSoftware::InputIndex(uint3
 
 void
 FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
                                      const Rect &aSourceRect)
 {
   MOZ_ASSERT(aIndex == ATT_CROP_RECT);
   Rect srcRect = aSourceRect;
   srcRect.Round();
-  mCropRect = IntRect(int32_t(srcRect.x), int32_t(srcRect.y),
-                      int32_t(srcRect.width), int32_t(srcRect.height));
+  if (!srcRect.ToIntRect(&mCropRect)) {
+    mCropRect = IntRect();
+  }
   Invalidate();
 }
 
 TemporaryRef<DataSourceSurface>
 FilterNodeCropSoftware::Render(const IntRect& aRect)
 {
   return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
 }
@@ -3288,24 +3390,23 @@ FilterNodeLightingSoftware<LightType, Li
   DebugOnlyAutoColorSamplingAccessControl accessControl(input);
 
   RefPtr<DataSourceSurface> target =
     Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
   if (!target) {
     return nullptr;
   }
 
-  uint8_t* sourceData = input->GetData();
+  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+  uint8_t* sourceData = DataAtOffset(input, offset);
   int32_t sourceStride = input->Stride();
   uint8_t* targetData = target->GetData();
   int32_t targetStride = target->Stride();
 
-  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
-  sourceData += DataOffset(input, offset);
-
   uint32_t lightColor = ColorToBGRA(mColor);
   mLight.Prepare();
   mLighting.Prepare();
 
   for (int32_t y = 0; y < size.height; y++) {
     for (int32_t x = 0; x < size.width; x++) {
       int32_t sourceIndex = y * sourceStride + x;
       int32_t targetIndex = y * targetStride + 4 * x;
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/963086-1.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+   viewBox="0 0 64 64">
+  <defs>
+    <filter id="dropShadow">
+      <feGaussianBlur stdDeviation="2" />
+      <feOffset
+         result="offsetBlur"
+         dy="1073741824"/>
+      <feMerge>
+        <feMergeNode
+           in="offsetBlur" />
+        <feMergeNode
+           in="SourceGraphic" />
+      </feMerge>
+    </filter>
+  </defs>
+  <rect height="64" width="64" style="filter:url(#dropShadow)" />
+</svg>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -173,10 +173,11 @@ load 890782-1.svg
 load 890783-1.svg
 load 893510-1.svg
 load 895311-1.svg
 load 897342-1.svg
 load 898909-1.svg
 load 898951-1.svg
 load 919371-1.xhtml
 load 952270-1.svg
+load 963086-1.svg
 load 975773-1.svg
 load 974746-1.svg