Merge to M46: Enforce input and output dimensionalities for CPDF_StitchFunc.

Also cleans up some places in the relevant functions since we're here.

BUG=551460
R=thestig@chromium.org

Review URL: https://codereview.chromium.org/1421783004 .

(cherry picked from commit 4f85605cbc652a17bc833f883186e0a68af6006d)

Review URL: https://codereview.chromium.org/1432833002 .
diff --git a/BUILD.gn b/BUILD.gn
index 4ba901e..015241f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -756,6 +756,7 @@
 
 test("pdfium_embeddertests") {
   sources = [
+    "core/src/fpdfapi/fpdf_page/fpdf_page_func_embeddertest.cpp",
     "core/src/fpdfapi/fpdf_parser/fpdf_parser_decode_embeddertest.cpp",
     "core/src/fpdfapi/fpdf_parser/fpdf_parser_parser_embeddertest.cpp",
     "fpdfsdk/src/fpdf_dataavail_embeddertest.cpp",
diff --git a/core/src/fpdfapi/fpdf_page/fpdf_page_func.cpp b/core/src/fpdfapi/fpdf_page/fpdf_page_func.cpp
index 8c909a9..4deb4d3 100644
--- a/core/src/fpdfapi/fpdf_page/fpdf_page_func.cpp
+++ b/core/src/fpdfapi/fpdf_page/fpdf_page_func.cpp
@@ -5,11 +5,13 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include <limits.h>
+#include <vector>
 
+#include "../../../../third_party/base/nonstd_unique_ptr.h"
+#include "../../../../third_party/base/numerics/safe_conversions_impl.h"
 #include "../../../include/fpdfapi/fpdf_module.h"
 #include "../../../include/fpdfapi/fpdf_page.h"
 #include "../../../include/fxcrt/fx_safe_types.h"
-#include "../../../third_party/base/numerics/safe_conversions_impl.h"
 #include "pageint.h"
 
 class CPDF_PSEngine;
@@ -724,78 +726,88 @@
   FX_BOOL v_Init(CPDF_Object* pObj) override;
   FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const override;
 
-  int m_nSubs;
-  CPDF_Function** m_pSubFunctions;
+  std::vector<CPDF_Function*> m_pSubFunctions;
   FX_FLOAT* m_pBounds;
   FX_FLOAT* m_pEncode;
+
+  static const int kRequiredNumInputs = 1;
 };
 
 CPDF_StitchFunc::CPDF_StitchFunc() {
-  m_nSubs = 0;
-  m_pSubFunctions = NULL;
   m_pBounds = NULL;
   m_pEncode = NULL;
 }
 CPDF_StitchFunc::~CPDF_StitchFunc() {
-  for (int i = 0; i < m_nSubs; i++)
-    delete m_pSubFunctions[i];
-  FX_Free(m_pSubFunctions);
+  for (auto& sub : m_pSubFunctions) {
+    delete sub;
+  }
   FX_Free(m_pBounds);
   FX_Free(m_pEncode);
 }
 FX_BOOL CPDF_StitchFunc::v_Init(CPDF_Object* pObj) {
   CPDF_Dictionary* pDict = pObj->GetDict();
-  if (pDict == NULL) {
+  if (!pDict) {
+    return FALSE;
+  }
+  if (m_nInputs != kRequiredNumInputs) {
     return FALSE;
   }
   CPDF_Array* pArray = pDict->GetArray(FX_BSTRC("Functions"));
-  if (pArray == NULL) {
+  if (!pArray) {
     return FALSE;
   }
-  m_nSubs = pArray->GetCount();
-  if (m_nSubs == 0) {
+  FX_DWORD nSubs = pArray->GetCount();
+  if (nSubs == 0) {
     return FALSE;
   }
-  m_pSubFunctions = FX_Alloc(CPDF_Function*, m_nSubs);
   m_nOutputs = 0;
-  int i;
-  for (i = 0; i < m_nSubs; i++) {
+  for (FX_DWORD i = 0; i < nSubs; i++) {
     CPDF_Object* pSub = pArray->GetElementValue(i);
     if (pSub == pObj) {
       return FALSE;
     }
-    m_pSubFunctions[i] = CPDF_Function::Load(pSub);
-    if (m_pSubFunctions[i] == NULL) {
+    nonstd::unique_ptr<CPDF_Function> pFunc(CPDF_Function::Load(pSub));
+    if (!pFunc) {
       return FALSE;
     }
-    if (m_pSubFunctions[i]->CountOutputs() > m_nOutputs) {
-      m_nOutputs = m_pSubFunctions[i]->CountOutputs();
+    // Check that the input dimensionality is 1, and that all output
+    // dimensionalities are the same.
+    if (pFunc->CountInputs() != kRequiredNumInputs) {
+      return FALSE;
     }
+    if (pFunc->CountOutputs() != m_nOutputs) {
+      if (m_nOutputs)
+        return FALSE;
+
+      m_nOutputs = pFunc->CountOutputs();
+    }
+
+    m_pSubFunctions.push_back(pFunc.release());
   }
-  m_pBounds = FX_Alloc(FX_FLOAT, m_nSubs + 1);
+  m_pBounds = FX_Alloc(FX_FLOAT, nSubs + 1);
   m_pBounds[0] = m_pDomains[0];
   pArray = pDict->GetArray(FX_BSTRC("Bounds"));
-  if (pArray == NULL) {
+  if (!pArray) {
     return FALSE;
   }
-  for (i = 0; i < m_nSubs - 1; i++) {
+  for (FX_DWORD i = 0; i < nSubs - 1; i++) {
     m_pBounds[i + 1] = pArray->GetFloat(i);
   }
-  m_pBounds[m_nSubs] = m_pDomains[1];
-  m_pEncode = FX_Alloc2D(FX_FLOAT, m_nSubs, 2);
+  m_pBounds[nSubs] = m_pDomains[1];
+  m_pEncode = FX_Alloc2D(FX_FLOAT, nSubs, 2);
   pArray = pDict->GetArray(FX_BSTRC("Encode"));
-  if (pArray == NULL) {
+  if (!pArray) {
     return FALSE;
   }
-  for (i = 0; i < m_nSubs * 2; i++) {
+  for (FX_DWORD i = 0; i < nSubs * 2; i++) {
     m_pEncode[i] = pArray->GetFloat(i);
   }
   return TRUE;
 }
 FX_BOOL CPDF_StitchFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* outputs) const {
   FX_FLOAT input = inputs[0];
-  int i;
-  for (i = 0; i < m_nSubs - 1; i++)
+  size_t i;
+  for (i = 0; i < m_pSubFunctions.size() - 1; i++)
     if (input < m_pBounds[i + 1]) {
       break;
     }
@@ -805,7 +817,7 @@
   input = PDF_Interpolate(input, m_pBounds[i], m_pBounds[i + 1],
                           m_pEncode[i * 2], m_pEncode[i * 2 + 1]);
   int nresults;
-  m_pSubFunctions[i]->Call(&input, m_nInputs, outputs, nresults);
+  m_pSubFunctions[i]->Call(&input, kRequiredNumInputs, outputs, nresults);
   return TRUE;
 }
 CPDF_Function* CPDF_Function::Load(CPDF_Object* pFuncObj) {
diff --git a/core/src/fpdfapi/fpdf_page/fpdf_page_func_embeddertest.cpp b/core/src/fpdfapi/fpdf_page/fpdf_page_func_embeddertest.cpp
new file mode 100644
index 0000000..af04b4a
--- /dev/null
+++ b/core/src/fpdfapi/fpdf_page/fpdf_page_func_embeddertest.cpp
@@ -0,0 +1,20 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "../../../testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFPageFuncEmbeddertest : public EmbedderTest {};
+
+TEST_F(FPDFPageFuncEmbeddertest, Bug_551460) {
+  // Should not crash under ASan.
+  // Tests that the number of inputs is not simply calculated from the domain
+  // and trusted. The number of inputs has to be 1.
+  EXPECT_TRUE(OpenDocument("testing/resources/bug_551460.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+  FPDF_BITMAP bitmap = RenderPage(page);
+  FPDFBitmap_Destroy(bitmap);
+  UnloadPage(page);
+}
diff --git a/pdfium.gyp b/pdfium.gyp
index 6f1e6d7..08cb075 100644
--- a/pdfium.gyp
+++ b/pdfium.gyp
@@ -740,6 +740,7 @@
         '<(DEPTH)/v8',
       ],
       'sources': [
+        'core/src/fpdfapi/fpdf_page/fpdf_page_func_embeddertest.cpp',
         'core/src/fpdfapi/fpdf_parser/fpdf_parser_decode_embeddertest.cpp',
         'core/src/fpdfapi/fpdf_parser/fpdf_parser_parser_embeddertest.cpp',
         'fpdfsdk/src/fpdf_dataavail_embeddertest.cpp',
diff --git a/testing/resources/bug_551460.in b/testing/resources/bug_551460.in
new file mode 100644
index 0000000..5ed2f3f
--- /dev/null
+++ b/testing/resources/bug_551460.in
@@ -0,0 +1,75 @@
+{{header}}
+
+{{object 1 0}}
+<<
+  /Pages 2 0 R
+>>
+endobj
+
+{{object 2 0}}
+<<
+  /Kids [ 3 0 R ]
+>>
+endobj
+
+{{object 3 0}}
+<<
+  /Contents 6 0 R
+  /Resources 5 0 R
+>>
+endobj
+
+{{object 6 0}}
+<<>>
+stream
+0.0 G
+0.0 1.0 0.0 rg
+25 175 175 -150 re
+/Sh sh
+endstream
+endobj
+
+{{object 5 0}}
+<<
+  /Shading <<
+    /Sh 7 0 R
+  >>
+>>
+endobj
+
+{{object 7 0}}
+<<
+  /ShadingType 1
+  /ColorSpace /DeviceCMYK
+  /Coords [0.0 0.0 1.0 1.0]
+  /Function 9 0 R
+  /Extend [true true]
+>>
+
+{{object 9 0}}
+<<
+  /FunctionType 3
+  /Domain [0.0 1.0 0.0 1.0]
+  /Functions [10 0 R]
+  /Bounds [2.0]
+  /Encode [0.0 1.0]
+>>
+endobj
+
+{{object 10 0}}
+<<
+  /FunctionType 2
+  /Domain [0.0 1.0 0.0 1.0]
+  /C0 [0.0 0.0]
+  /C1 [1.0 1.0]
+  /N 1
+>>
+endobj
+
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Size 11
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_551460.pdf b/testing/resources/bug_551460.pdf
new file mode 100644
index 0000000..926e3fa
--- /dev/null
+++ b/testing/resources/bug_551460.pdf
@@ -0,0 +1,89 @@
+%PDF-1.7
+% ò¤ô
+
+1 0 obj
+<<
+  /Pages 2 0 R
+>>
+endobj
+
+2 0 obj
+<<
+  /Kids [ 3 0 R ]
+>>
+endobj
+
+3 0 obj
+<<
+  /Contents 6 0 R
+  /Resources 5 0 R
+>>
+endobj
+
+6 0 obj
+<<>>
+stream
+0.0 G
+0.0 1.0 0.0 rg
+25 175 175 -150 re
+/Sh sh
+endstream
+endobj
+
+5 0 obj
+<<
+  /Shading <<
+    /Sh 7 0 R
+  >>
+>>
+endobj
+
+7 0 obj
+<<
+  /ShadingType 1
+  /ColorSpace /DeviceCMYK
+  /Coords [0.0 0.0 1.0 1.0]
+  /Function 9 0 R
+  /Extend [true true]
+>>
+
+9 0 obj
+<<
+  /FunctionType 3
+  /Domain [0.0 1.0 0.0 1.0]
+  /Functions [10 0 R]
+  /Bounds [2.0]
+  /Encode [0.0 1.0]
+>>
+endobj
+
+10 0 obj
+<<
+  /FunctionType 2
+  /Domain [0.0 1.0 0.0 1.0]
+  /C0 [0.0 0.0]
+  /C1 [1.0 1.0]
+  /N 1
+>>
+endobj
+
+xref
+0 11
+0000000000 65535 f 
+0000000016 00000 n 
+0000000053 00000 n 
+0000000093 00000 n 
+0000000000 65535 f 
+0000000237 00000 n 
+0000000152 00000 n 
+0000000292 00000 n 
+0000000000 65535 f 
+0000000418 00000 n 
+0000000544 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 11
+>>
+startxref
+652
+%%EOF