Rework pdf_fuzzer_init_public.cc

Allow fuzzers to access globally-created resources, specifically
the cppgc garbage collector heap, for future fuzzing of GC'd
objects. This is complicated by the component build, where the
fuzzers's LLVMFuzzerTestOneInput() entry points live in the pdfium.so
library under different names, with the public init occurring in a
small shim above that.

-- Introduce a .h file for use by the fuzzers.
-- Rename class to match file (actual name unimportant)
-- Create an external heap when XFA is present.
-- Remove wrong comment about static variables being the same.

Change-Id: I97ba63dd19f5317f1635cbf3711c3551c3794852
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/73590
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index b97c08f..0fe1021 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -90,7 +90,10 @@
   testonly = true
   sources = [ "pdf_fuzzer_init_public.cc" ]
   include_dirs = [ "../.." ]
-  deps = [ "../../:pdfium_public_headers" ]
+  deps = [
+    ":fuzzer_utils",
+    "../../:pdfium_public_headers",
+  ]
   if (pdf_enable_v8) {
     configs += [ "//v8:external_startup_data" ]
     deps += [
@@ -98,6 +101,9 @@
       "//v8",
       "//v8:v8_libplatform",
     ]
+    if (pdf_enable_xfa) {
+      deps += [ "../../fxjs:gc" ]
+    }
   }
 }
 
@@ -356,9 +362,12 @@
     pdfium_fuzzer("pdf_formcalc_fuzzer") {
       sources = [ "pdf_formcalc_fuzzer.cc" ]
       deps = [
+        ":fuzzer_utils",
         "../../core/fxcrt",
+        "../../fxjs:gc",
         "../../xfa/fxfa/fm2js",
       ]
+      public_fuzzer = true
     }
 
     pdfium_fuzzer("pdfium_xfa_fuzzer") {
diff --git a/testing/fuzzers/pdf_formcalc_fuzzer.cc b/testing/fuzzers/pdf_formcalc_fuzzer.cc
index 08e22bb..ce502b4 100644
--- a/testing/fuzzers/pdf_formcalc_fuzzer.cc
+++ b/testing/fuzzers/pdf_formcalc_fuzzer.cc
@@ -4,13 +4,17 @@
 
 #include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_string.h"
+#include "testing/fuzzers/pdf_fuzzer_init_public.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "third_party/base/logging.h"
 #include "xfa/fxfa/fm2js/cxfa_fmparser.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  auto* state = static_cast<PDFFuzzerPublic*>(FPDF_GetFuzzerPerProcessState());
+  CHECK(state);
+  CHECK(state->GetHeap());
   WideString input = WideString::FromUTF8(ByteStringView(data, size));
-
   CXFA_FMParser parser(input.AsStringView());
   parser.Parse();
-
   return 0;
 }
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.cc b/testing/fuzzers/pdf_fuzzer_init_public.cc
index ad39e36..ca4bb39 100644
--- a/testing/fuzzers/pdf_fuzzer_init_public.cc
+++ b/testing/fuzzers/pdf_fuzzer_init_public.cc
@@ -2,18 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "testing/fuzzers/pdf_fuzzer_init_public.h"
+
 #include <string.h>
 
-#include <memory>
-
-#include "public/fpdf_ext.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
 
 #ifdef PDF_ENABLE_V8
 #include "testing/free_deleter.h"
 #include "testing/v8_initializer.h"
-#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif
+#endif  // PDF_ENABLE_V8
 
 #ifdef _WIN32
 #include <windows.h>
@@ -26,10 +24,13 @@
 
 namespace {
 
+// pdf_fuzzer_init.cc and pdf_fuzzer_init_public.cc are mutually exclusive
+// and should not be built together.
+PDFFuzzerInitPublic* g_instance = new PDFFuzzerInitPublic();
+
 #ifdef PDF_ENABLE_V8
 std::string ProgramPath() {
   std::string result;
-
 #ifdef _WIN32
   char path[MAX_PATH];
   DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH);
@@ -56,44 +57,41 @@
 
 }  // namespace
 
-// Initialize the library once for all runs of the fuzzer.
-struct TestCase {
-  TestCase() {
+PDFFuzzerPublic::PDFFuzzerPublic() {
 #ifdef PDF_ENABLE_V8
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-    platform = InitializeV8ForPDFiumWithStartupData(
-        ProgramPath(), std::string(), std::string(), &snapshot_blob);
-#else
-    platform = InitializeV8ForPDFium(ProgramPath(), std::string());
+  platform_ = InitializeV8ForPDFiumWithStartupData(
+      ProgramPath(), std::string(), std::string(), &snapshot_blob_);
+#else   // V8_USE_EXTERNAL_STARTUP_DATA
+  platform_ = InitializeV8ForPDFium(ProgramPath(), std::string());
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
+#ifdef PDF_ENABLE_XFA
+  FXGC_Initialize(platform_.get(), nullptr);
+  heap_ = FXGC_CreateHeap();
+#endif  // PDF_ENABLE_XFA
 #endif  // PDF_ENABLE_V8
 
-    memset(&config, '\0', sizeof(config));
-    config.version = 3;
-    config.m_pUserFontPaths = nullptr;
-    config.m_pIsolate = nullptr;
-    config.m_v8EmbedderSlot = 0;
+  memset(&config_, '\0', sizeof(config_));
+  config_.version = 3;
+  config_.m_pUserFontPaths = nullptr;
+  config_.m_pIsolate = nullptr;
+  config_.m_v8EmbedderSlot = 0;
 #ifdef PDF_ENABLE_V8
-    config.m_pPlatform = platform.get();
+  config_.m_pPlatform = platform_.get();
 #endif  // PDF_ENABLE_V8
-    FPDF_InitLibraryWithConfig(&config);
+  FPDF_InitLibraryWithConfig(&config_);
 
-    memset(&unsupport_info, '\0', sizeof(unsupport_info));
-    unsupport_info.version = 1;
-    unsupport_info.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {};
-    FSDK_SetUnSpObjProcessHandler(&unsupport_info);
-  }
+  memset(&unsupport_info_, '\0', sizeof(unsupport_info_));
+  unsupport_info_.version = 1;
+  unsupport_info_.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {};
+  FSDK_SetUnSpObjProcessHandler(&unsupport_info_);
+}
 
-#ifdef PDF_ENABLE_V8
-  std::unique_ptr<v8::Platform> platform;
-  v8::StartupData snapshot_blob;
-#endif
+PDFFuzzerPublic::~PDFFuzzerPublic() = default;
 
-  FPDF_LIBRARY_CONFIG config;
-  UNSUPPORT_INFO unsupport_info;
-};
+PDFFuzzerInitPublic::PDFFuzzerInitPublic()
+    : context_(std::make_unique<PDFFuzzerPublic>()) {
+  FPDF_SetFuzzerPerProcessState(context_.get());
+}
 
-// pdf_fuzzer_init.cc and pdfium_fuzzer_init_public.cc are mutually exclusive
-// and should not be built together. They deliberately have the same global
-// variable.
-static TestCase* g_test_case = new TestCase();
+PDFFuzzerInitPublic::~PDFFuzzerInitPublic() = default;
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.h b/testing/fuzzers/pdf_fuzzer_init_public.h
new file mode 100644
index 0000000..a66c2ff
--- /dev/null
+++ b/testing/fuzzers/pdf_fuzzer_init_public.h
@@ -0,0 +1,55 @@
+// Copyright 2020 The 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.
+
+#ifndef TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
+#define TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
+
+#include <memory>
+
+#include "public/fpdf_ext.h"
+#include "public/fpdfview.h"
+
+#ifdef PDF_ENABLE_V8
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8.h"
+#ifdef PDF_ENABLE_XFA
+#include "fxjs/gc/heap.h"
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
+
+// Context for all runs of the fuzzer.
+class PDFFuzzerPublic {
+ public:
+  PDFFuzzerPublic();
+  ~PDFFuzzerPublic();
+
+#ifdef PDF_ENABLE_V8
+#ifdef PDF_ENABLE_XFA
+  cppgc::Heap* GetHeap() { return heap_.get(); }
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
+
+ private:
+  FPDF_LIBRARY_CONFIG config_;
+  UNSUPPORT_INFO unsupport_info_;
+#ifdef PDF_ENABLE_V8
+  std::unique_ptr<v8::Platform> platform_;
+  v8::StartupData snapshot_blob_;
+#ifdef PDF_ENABLE_XFA
+  FXGCScopedHeap heap_;
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
+};
+
+// Initializes the library once for all runs of the fuzzer.
+class PDFFuzzerInitPublic {
+ public:
+  PDFFuzzerInitPublic();
+  ~PDFFuzzerInitPublic();
+
+ private:
+  std::unique_ptr<PDFFuzzerPublic> context_;
+};
+
+#endif  // TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
diff --git a/testing/fuzzers/pdfium_fuzzer_util.cc b/testing/fuzzers/pdfium_fuzzer_util.cc
index 9238f0e..836244b 100644
--- a/testing/fuzzers/pdfium_fuzzer_util.cc
+++ b/testing/fuzzers/pdfium_fuzzer_util.cc
@@ -4,6 +4,18 @@
 
 #include "testing/fuzzers/pdfium_fuzzer_util.h"
 
+namespace {
+void* g_fuzzer_init_per_process_state = nullptr;
+}  // namespace
+
 int GetInteger(const uint8_t* data) {
   return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
 }
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state) {
+  g_fuzzer_init_per_process_state = state;
+}
+
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState() {
+  return g_fuzzer_init_per_process_state;
+}
diff --git a/testing/fuzzers/pdfium_fuzzer_util.h b/testing/fuzzers/pdfium_fuzzer_util.h
index d82f44b..a8ddeee 100644
--- a/testing/fuzzers/pdfium_fuzzer_util.h
+++ b/testing/fuzzers/pdfium_fuzzer_util.h
@@ -7,7 +7,19 @@
 
 #include <stdint.h>
 
+#include "public/fpdfview.h"
+
 // Returns an integer from the first 4 bytes of |data|.
 int GetInteger(const uint8_t* data);
 
+// Plumb access to any context created by fuzzer initialization into
+// the LLVMFuzzerTestOneInput() function, as that function does not
+// allow for additional parameters, nor can it reach back up to the
+// top-level fuzzer shim during a component build (see the comment
+// in BUILD.gn about splitting fuzzers into _impl and _src targets).
+extern "C" {
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state);
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState();
+}  // extern "C"
+
 #endif  // TESTING_FUZZERS_PDFIUM_FUZZER_UTIL_H_