Use CheckedNumeric in BiInterpolImpl

This CL splits up BiInterpolImpl into the three interpolations its made of. In
the interpolation, safe math is used, and a boolean keeps track of whether an
overflow has occurred. If it does, we consider the patch to be invalid and
immediately terminate the Draw method.

Bug: chromium:724885
Change-Id: I4944ee1d821d8dd46c04e6b145eabe19d2ad8a5a
Reviewed-on: https://pdfium-review.googlesource.com/5851
Commit-Queue: Nicolás Peña <npm@chromium.org>
Reviewed-by: dsinclair <dsinclair@chromium.org>
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 9f4bda1..9170ae9 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -673,6 +673,17 @@
   float Distance() { return x.Distance() + y.Distance(); }
 };
 
+int Interpolate(int p1, int p2, int delta1, int delta2, bool* overflow) {
+  pdfium::base::CheckedNumeric<int> p = p2;
+  p -= p1;
+  p *= delta1;
+  p /= delta2;
+  p += p1;
+  if (!p.IsValid())
+    *overflow = true;
+  return p.ValueOrDefault(0);
+}
+
 int BiInterpolImpl(int c0,
                    int c1,
                    int c2,
@@ -680,26 +691,30 @@
                    int x,
                    int y,
                    int x_scale,
-                   int y_scale) {
-  int x1 = c0 + (c3 - c0) * x / x_scale;
-  int x2 = c1 + (c2 - c1) * x / x_scale;
-  return x1 + (x2 - x1) * y / y_scale;
+                   int y_scale,
+                   bool* overflow) {
+  int x1 = Interpolate(c0, c3, x, x_scale, overflow);
+  int x2 = Interpolate(c1, c2, x, x_scale, overflow);
+  return Interpolate(x1, x2, y, y_scale, overflow);
 }
 
 struct Coon_Color {
   Coon_Color() { memset(comp, 0, sizeof(int) * 3); }
   int comp[3];
 
-  void BiInterpol(Coon_Color colors[4],
+  // Returns true if successful, false if overflow detected.
+  bool BiInterpol(Coon_Color colors[4],
                   int x,
                   int y,
                   int x_scale,
                   int y_scale) {
+    bool overflow = false;
     for (int i = 0; i < 3; i++) {
       comp[i] = BiInterpolImpl(colors[0].comp[i], colors[1].comp[i],
                                colors[2].comp[i], colors[3].comp[i], x, y,
-                               x_scale, y_scale);
+                               x_scale, y_scale, &overflow);
     }
+    return !overflow;
   }
 
   int Distance(Coon_Color& o) {
@@ -731,14 +746,23 @@
     int d_left = 0;
     int d_top = 0;
     int d_right = 0;
-    div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale, y_scale);
+    if (!div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale,
+                                  y_scale)) {
+      return;
+    }
     if (!bSmall) {
-      div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale,
-                               y_scale);
-      div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale,
-                               y_scale);
-      div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale,
-                               y_scale);
+      if (!div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale,
+                                    y_scale)) {
+        return;
+      }
+      if (!div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale,
+                                    y_scale)) {
+        return;
+      }
+      if (!div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale,
+                                    y_scale)) {
+        return;
+      }
       d_bottom = div_colors[3].Distance(div_colors[0]);
       d_left = div_colors[1].Distance(div_colors[0]);
       d_top = div_colors[1].Distance(div_colors[2]);