Limit image positions/sizes in CPDF_ImageRenderer.

Do the checks once in CPDF_ImageRenderer instead of in all the
RenderDeviceDriverIface implementations.

BUG=chromium:919640

Change-Id: I834aed748bf02067cd4bee840dc1c122ae8c6de1
Reviewed-on: https://pdfium-review.googlesource.com/c/50150
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.cpp b/core/fpdfapi/render/cpdf_imagerenderer.cpp
index 6149cca..d2d6384 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.cpp
+++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp
@@ -40,14 +40,25 @@
 #include "core/fxge/skia/fx_skia_device.h"
 #endif
 
+namespace {
+
+bool IsImageValueTooBig(int val) {
+  // Likely large enough for any real rendering need, but sufficiently small
+  // that operations like val1 + val2 or -val will not overflow.
+  constexpr int kLimit = 256 * 1024 * 1024;
+  FX_SAFE_INT32 safe_val = val;
+  safe_val = safe_val.Abs();
+  return safe_val.ValueOrDefault(kLimit) >= kLimit;
+}
+
+}  // namespace
+
 CPDF_ImageRenderer::CPDF_ImageRenderer() = default;
 
 CPDF_ImageRenderer::~CPDF_ImageRenderer() = default;
 
 bool CPDF_ImageRenderer::StartLoadDIBBase() {
-  CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
-  FX_RECT image_rect = image_rect_f.GetOuterRect();
-  if (!image_rect.Valid())
+  if (!GetUnitRect().has_value())
     return false;
 
   if (m_Loader.Start(m_pImageObject.Get(),
@@ -400,10 +411,7 @@
     return false;
   }
 #endif
-  CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
-  FX_RECT image_rect = image_rect_f.GetOuterRect();
-  int dest_width = image_rect.Width();
-  int dest_height = image_rect.Height();
+
   if ((fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) ||
       (fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) {
     if (NotDrawing()) {
@@ -411,21 +419,31 @@
       return false;
     }
 
+    Optional<FX_RECT> image_rect = GetUnitRect();
+    if (!image_rect.has_value())
+      return false;
+
     FX_RECT clip_box = m_pRenderStatus->GetRenderDevice()->GetClipBox();
-    clip_box.Intersect(image_rect);
+    clip_box.Intersect(image_rect.value());
     m_Status = 2;
     m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
         m_pDIBBase, m_ImageMatrix, m_ResampleOptions, &clip_box);
     return true;
   }
-  if (m_ImageMatrix.a < 0)
-    dest_width = -dest_width;
 
-  if (m_ImageMatrix.d > 0)
-    dest_height = -dest_height;
+  Optional<FX_RECT> image_rect = GetUnitRect();
+  if (!image_rect.has_value())
+    return false;
 
-  int dest_left = dest_width > 0 ? image_rect.left : image_rect.right;
-  int dest_top = dest_height > 0 ? image_rect.top : image_rect.bottom;
+  int dest_left;
+  int dest_top;
+  int dest_width;
+  int dest_height;
+  if (!GetDimensionsFromUnitRect(image_rect.value(), &dest_left, &dest_top,
+                                 &dest_width, &dest_height)) {
+    return false;
+  }
+
   if (m_pDIBBase->IsOpaqueImage() && m_BitmapAlpha == 255) {
     if (m_pRenderStatus->GetRenderDevice()->StretchDIBitsWithFlagsAndBlend(
             m_pDIBBase, dest_left, dest_top, dest_width, dest_height,
@@ -449,10 +467,10 @@
 
   FX_RECT clip_box = m_pRenderStatus->GetRenderDevice()->GetClipBox();
   FX_RECT dest_rect = clip_box;
-  dest_rect.Intersect(image_rect);
+  dest_rect.Intersect(image_rect.value());
   FX_RECT dest_clip(
-      dest_rect.left - image_rect.left, dest_rect.top - image_rect.top,
-      dest_rect.right - image_rect.left, dest_rect.bottom - image_rect.top);
+      dest_rect.left - image_rect->left, dest_rect.top - image_rect->top,
+      dest_rect.right - image_rect->left, dest_rect.bottom - image_rect->top);
   RetainPtr<CFX_DIBitmap> pStretched = m_pDIBBase->StretchTo(
       dest_width, dest_height, m_ResampleOptions, &dest_clip);
   if (pStretched) {
@@ -493,14 +511,20 @@
         ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha));
     return false;
   }
-  CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
-  FX_RECT image_rect = image_rect_f.GetOuterRect();
-  int dest_width =
-      m_ImageMatrix.a > 0 ? image_rect.Width() : -image_rect.Width();
-  int dest_height =
-      m_ImageMatrix.d > 0 ? -image_rect.Height() : image_rect.Height();
-  int left = dest_width > 0 ? image_rect.left : image_rect.right;
-  int top = dest_height > 0 ? image_rect.top : image_rect.bottom;
+
+  Optional<FX_RECT> image_rect = GetUnitRect();
+  if (!image_rect.has_value())
+    return false;
+
+  int left;
+  int top;
+  int dest_width;
+  int dest_height;
+  if (!GetDimensionsFromUnitRect(image_rect.value(), &left, &top, &dest_width,
+                                 &dest_height)) {
+    return false;
+  }
+
   m_pRenderStatus->GetRenderDevice()->StretchBitMask(
       pAlphaMask, left, top, dest_width, dest_height,
       ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha));
@@ -572,3 +596,41 @@
     }
   }
 }
+
+Optional<FX_RECT> CPDF_ImageRenderer::GetUnitRect() const {
+  CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
+  FX_RECT image_rect = image_rect_f.GetOuterRect();
+  if (!image_rect.Valid())
+    return {};
+  return image_rect;
+}
+
+bool CPDF_ImageRenderer::GetDimensionsFromUnitRect(const FX_RECT& rect,
+                                                   int* left,
+                                                   int* top,
+                                                   int* width,
+                                                   int* height) const {
+  ASSERT(rect.Valid());
+
+  int dest_width = rect.Width();
+  int dest_height = rect.Height();
+  if (IsImageValueTooBig(dest_width) || IsImageValueTooBig(dest_height))
+    return false;
+
+  if (m_ImageMatrix.a < 0)
+    dest_width = -dest_width;
+
+  if (m_ImageMatrix.d > 0)
+    dest_height = -dest_height;
+
+  int dest_left = dest_width > 0 ? rect.left : rect.right;
+  int dest_top = dest_height > 0 ? rect.top : rect.bottom;
+  if (IsImageValueTooBig(dest_left) || IsImageValueTooBig(dest_top))
+    return false;
+
+  *left = dest_left;
+  *top = dest_top;
+  *width = dest_width;
+  *height = dest_height;
+  return true;
+}
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.h b/core/fpdfapi/render/cpdf_imagerenderer.h
index ed2cf7b..bf3db2d 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.h
+++ b/core/fpdfapi/render/cpdf_imagerenderer.h
@@ -14,6 +14,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/fx_dib.h"
+#include "third_party/base/optional.h"
 
 class CFX_DIBitmap;
 class CFX_DIBBase;
@@ -65,6 +66,12 @@
                           const FX_RECT& rect) const;
   const CPDF_RenderOptions& GetRenderOptions() const;
   void HandleFilters();
+  Optional<FX_RECT> GetUnitRect() const;
+  bool GetDimensionsFromUnitRect(const FX_RECT& rect,
+                                 int* left,
+                                 int* top,
+                                 int* width,
+                                 int* height) const;
 
   UnownedPtr<CPDF_RenderStatus> m_pRenderStatus;
   UnownedPtr<CPDF_ImageObject> m_pImageObject;