Copy some fuzzer code from Chromium into PDFium.

These came from Chromium's pdf/pdfium/fuzzer directory. The code has
been modified to be buildable in standalone PDFium and modernized.

Change-Id: Ia4ddad4a164f798689af4b9f92d41d635901dccb
Reviewed-on: https://pdfium-review.googlesource.com/c/43530
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index 59211b1..5a6af69 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -35,6 +35,7 @@
     ":pdf_psengine_fuzzer_src",
     ":pdf_streamparser_fuzzer_src",
     ":pdf_xml_fuzzer_src",
+    ":pdfium_fuzzer_src",
   ]
   if (pdf_enable_xfa) {
     deps += [
@@ -44,6 +45,7 @@
       ":pdf_css_fuzzer_src",
       ":pdf_fm2js_fuzzer_src",
       ":pdf_formcalc_fuzzer_src",
+      ":pdfium_xfa_fuzzer_src",
     ]
     if (pdf_enable_xfa_bmp) {
       deps += [ ":pdf_codec_bmp_fuzzer_src" ]
@@ -78,6 +80,9 @@
       "//build/config/compiler:no_chromium_code",
       ":fuzzer_config",
     ]
+    if (pdf_enable_v8) {
+      configs += [ "//v8:external_startup_data" ]
+    }
   }
 }
 
@@ -175,6 +180,19 @@
       "pdf_formcalc_fuzzer.cc",
     ]
   }
+
+  pdfium_fuzzer("pdfium_xfa_fuzzer_src") {
+    sources = [
+      "pdfium_fuzzer_helper.cc",
+      "pdfium_fuzzer_helper.h",
+      "pdfium_xfa_fuzzer.cc",
+    ]
+    deps = [
+      "../../:test_support",
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+  }
 }
 
 pdfium_fuzzer("pdf_cmap_fuzzer_src") {
@@ -251,3 +269,22 @@
     "pdf_xml_fuzzer.cc",
   ]
 }
+
+pdfium_fuzzer("pdfium_fuzzer_src") {
+  sources = [
+    "pdfium_fuzzer.cc",
+    "pdfium_fuzzer_helper.cc",
+    "pdfium_fuzzer_helper.h",
+  ]
+
+  deps = [
+    "../../:test_support",
+  ]
+
+  if (pdf_enable_v8) {
+    deps += [
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+  }
+}
diff --git a/testing/fuzzers/pdfium_fuzzer.cc b/testing/fuzzers/pdfium_fuzzer.cc
new file mode 100644
index 0000000..dc15378
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium 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 <stdint.h>
+
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumFuzzer() = default;
+  ~PDFiumFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 1; }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  PDFiumFuzzer fuzzer;
+  fuzzer.RenderPdf(reinterpret_cast<const char*>(data), size);
+  return 0;
+}
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.cc b/testing/fuzzers/pdfium_fuzzer_helper.cc
new file mode 100644
index 0000000..ed000d2
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer_helper.cc
@@ -0,0 +1,264 @@
+// Copyright 2017 The Chromium 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/fuzzers/pdfium_fuzzer_helper.h"
+
+#include <assert.h>
+#include <limits.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <Windows.h>
+#elif defined(__APPLE__)
+#include <mach-o/dyld.h>
+#else  // Linux
+#include <unistd.h>
+#endif  // _WIN32
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_dataavail.h"
+#include "public/fpdf_ext.h"
+#include "public/fpdf_text.h"
+#include "testing/test_support.h"
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8.h"
+
+namespace {
+
+int ExampleAppAlert(IPDF_JSPLATFORM*,
+                    FPDF_WIDESTRING,
+                    FPDF_WIDESTRING,
+                    int,
+                    int) {
+  return 0;
+}
+
+int ExampleAppResponse(IPDF_JSPLATFORM*,
+                       FPDF_WIDESTRING question,
+                       FPDF_WIDESTRING title,
+                       FPDF_WIDESTRING default_value,
+                       FPDF_WIDESTRING label,
+                       FPDF_BOOL is_password,
+                       void* response,
+                       int length) {
+  // UTF-16, always LE regardless of platform.
+  uint8_t* ptr = static_cast<uint8_t*>(response);
+  ptr[0] = 'N';
+  ptr[1] = 0;
+  ptr[2] = 'o';
+  ptr[3] = 0;
+  return 4;
+}
+
+void ExampleDocGotoPage(IPDF_JSPLATFORM*, int pageNumber) {}
+
+void ExampleDocMail(IPDF_JSPLATFORM*,
+                    void* mailData,
+                    int length,
+                    FPDF_BOOL UI,
+                    FPDF_WIDESTRING To,
+                    FPDF_WIDESTRING Subject,
+                    FPDF_WIDESTRING CC,
+                    FPDF_WIDESTRING BCC,
+                    FPDF_WIDESTRING Msg) {}
+
+void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {}
+
+FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
+  return true;
+}
+
+void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {}
+
+#ifdef PDF_ENABLE_V8
+std::string ProgramPath() {
+  std::string result;
+
+#ifdef _WIN32
+  char path[MAX_PATH];
+  DWORD len = GetModuleFileNameA(NULL, path, MAX_PATH);
+  if (len != 0)
+    result = std::string(path, len);
+#elif defined(__APPLE__)
+  char path[PATH_MAX];
+  unsigned int len = PATH_MAX;
+  if (!_NSGetExecutablePath(path, &len)) {
+    std::unique_ptr<char, pdfium::FreeDeleter> resolved_path(
+        realpath(path, nullptr));
+    if (resolved_path.get())
+      result = std::string(resolved_path.get());
+  }
+#else  // Linux
+  char path[PATH_MAX];
+  ssize_t len = readlink("/proc/self/exe", path, PATH_MAX);
+  if (len > 0)
+    result = std::string(path, len);
+#endif
+  return result;
+}
+#endif  // PDF_ENABLE_V8
+
+}  // namespace
+
+PDFiumFuzzerHelper::PDFiumFuzzerHelper() = default;
+
+PDFiumFuzzerHelper::~PDFiumFuzzerHelper() = default;
+
+bool PDFiumFuzzerHelper::OnFormFillEnvLoaded(FPDF_DOCUMENT doc) {
+  return true;
+}
+
+void PDFiumFuzzerHelper::RenderPdf(const char* pBuf, size_t len) {
+  IPDF_JSPLATFORM platform_callbacks;
+  memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
+  platform_callbacks.version = 3;
+  platform_callbacks.app_alert = ExampleAppAlert;
+  platform_callbacks.app_response = ExampleAppResponse;
+  platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
+  platform_callbacks.Doc_mail = ExampleDocMail;
+
+  FPDF_FORMFILLINFO form_callbacks;
+  memset(&form_callbacks, '\0', sizeof(form_callbacks));
+  form_callbacks.version = GetFormCallbackVersion();
+  form_callbacks.m_pJsPlatform = &platform_callbacks;
+
+  TestLoader loader(pBuf, len);
+  FPDF_FILEACCESS file_access;
+  memset(&file_access, '\0', sizeof(file_access));
+  file_access.m_FileLen = static_cast<unsigned long>(len);
+  file_access.m_GetBlock = TestLoader::GetBlock;
+  file_access.m_Param = &loader;
+
+  FX_FILEAVAIL file_avail;
+  memset(&file_avail, '\0', sizeof(file_avail));
+  file_avail.version = 1;
+  file_avail.IsDataAvail = Is_Data_Avail;
+
+  FX_DOWNLOADHINTS hints;
+  memset(&hints, '\0', sizeof(hints));
+  hints.version = 1;
+  hints.AddSegment = Add_Segment;
+
+  ScopedFPDFAvail pdf_avail(FPDFAvail_Create(&file_avail, &file_access));
+
+  int nRet = PDF_DATA_NOTAVAIL;
+  bool bIsLinearized = false;
+  ScopedFPDFDocument doc;
+  if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
+    doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
+    if (doc) {
+      while (nRet == PDF_DATA_NOTAVAIL)
+        nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
+
+      if (nRet == PDF_DATA_ERROR)
+        return;
+
+      nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
+      if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL)
+        return;
+
+      bIsLinearized = true;
+    }
+  } else {
+    doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
+  }
+
+  if (!doc)
+    return;
+
+  (void)FPDF_GetDocPermissions(doc.get());
+
+  ScopedFPDFFormHandle form(
+      FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
+  if (!OnFormFillEnvLoaded(doc.get()))
+    return;
+
+  FPDF_SetFormFieldHighlightColor(form.get(), FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD);
+  FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
+  FORM_DoDocumentJSAction(form.get());
+  FORM_DoDocumentOpenAction(form.get());
+
+  int page_count = FPDF_GetPageCount(doc.get());
+  for (int i = 0; i < page_count; ++i) {
+    if (bIsLinearized) {
+      nRet = PDF_DATA_NOTAVAIL;
+      while (nRet == PDF_DATA_NOTAVAIL)
+        nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
+
+      if (nRet == PDF_DATA_ERROR)
+        return;
+    }
+    RenderPage(doc.get(), form.get(), i);
+  }
+  FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
+}
+
+bool PDFiumFuzzerHelper::RenderPage(FPDF_DOCUMENT doc,
+                                    FPDF_FORMHANDLE form,
+                                    const int page_index) {
+  ScopedFPDFPage page(FPDF_LoadPage(doc, page_index));
+  if (!page)
+    return false;
+
+  ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get()));
+  FORM_OnAfterLoadPage(page.get(), form);
+  FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_OPEN);
+
+  const double scale = 1.0;
+  int width = static_cast<int>(FPDF_GetPageWidth(page.get()) * scale);
+  int height = static_cast<int>(FPDF_GetPageHeight(page.get()) * scale);
+  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, 0));
+  if (bitmap) {
+    FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, 0xFFFFFFFF);
+    FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0, 0);
+    FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0, 0);
+  }
+  FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page.get(), form);
+  return !!bitmap;
+}
+
+// Initialize the library once for all runs of the fuzzer.
+struct TestCase {
+  TestCase() {
+#ifdef PDF_ENABLE_V8
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+    platform = InitializeV8ForPDFiumWithStartupData(
+        ProgramPath(), "", &natives_blob, &snapshot_blob);
+#else
+    platform = InitializeV8ForPDFium(ProgramPath());
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+#endif  // PDF_ENABLE_V8
+
+    memset(&config, '\0', sizeof(config));
+    config.version = 2;
+    config.m_pUserFontPaths = nullptr;
+    config.m_pIsolate = nullptr;
+    config.m_v8EmbedderSlot = 0;
+    FPDF_InitLibraryWithConfig(&config);
+
+    memset(&unsupport_info, '\0', sizeof(unsupport_info));
+    unsupport_info.version = 1;
+    unsupport_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
+    FSDK_SetUnSpObjProcessHandler(&unsupport_info);
+  }
+
+  std::unique_ptr<v8::Platform> platform;
+  v8::StartupData natives_blob;
+  v8::StartupData snapshot_blob;
+  FPDF_LIBRARY_CONFIG config;
+  UNSUPPORT_INFO unsupport_info;
+};
+
+static TestCase* test_case = new TestCase();
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.h b/testing/fuzzers/pdfium_fuzzer_helper.h
new file mode 100644
index 0000000..f9db00f
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer_helper.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_
+#define TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_
+
+#include "public/fpdfview.h"
+
+class PDFiumFuzzerHelper {
+ public:
+  void RenderPdf(const char* pBuf, size_t len);
+
+  virtual int GetFormCallbackVersion() const = 0;
+  virtual bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc);
+
+ protected:
+  PDFiumFuzzerHelper();
+  virtual ~PDFiumFuzzerHelper();
+
+ private:
+  bool RenderPage(FPDF_DOCUMENT doc, FPDF_FORMHANDLE form, int page_index);
+};
+
+#endif  // TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_
diff --git a/testing/fuzzers/pdfium_xfa_fuzzer.cc b/testing/fuzzers/pdfium_xfa_fuzzer.cc
new file mode 100644
index 0000000..f9a69d4
--- /dev/null
+++ b/testing/fuzzers/pdfium_xfa_fuzzer.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium 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 <stdint.h>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumXFAFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumXFAFuzzer() = default;
+  ~PDFiumXFAFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 2; }
+
+  // Return false if XFA doesn't load as otherwise we're duplicating the work
+  // done by the non-xfa fuzzer.
+  bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+    int form_type = FPDF_GetFormType(doc);
+    if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+      return false;
+    return FPDF_LoadXFA(doc);
+  }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  PDFiumXFAFuzzer fuzzer;
+  fuzzer.RenderPdf(reinterpret_cast<const char*>(data), size);
+  return 0;
+}