Introduce PDFFuzzerPublic::MaybeForceGCAndPump().

Since fuzzers don't have their own message loops, those that
make cppgc heap allocations will need to periodically invoke an
explicit GC and pump, lest they grow without bound. Only garbage
collect every Nth call to keep performance reasonable (N=1000 for
the moment for no particular reason).

In turn, we need to have a reference to an actual isolate in order
to pump the loop, so explicitly create one.

We don't actually have any such fuzzers at the moment, but this
prepares for their introduction in future CLs.

-- reset fuzzer state on teardown while we're at it.

Bug: pdfium:1563
Change-Id: Ia854c8ea0a4e9b7168b192c82c8aba27b2aa876b
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/73650
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/testing/fuzzers/pdf_formcalc_fuzzer.cc b/testing/fuzzers/pdf_formcalc_fuzzer.cc
index ce502b4..ad35e67 100644
--- a/testing/fuzzers/pdf_formcalc_fuzzer.cc
+++ b/testing/fuzzers/pdf_formcalc_fuzzer.cc
@@ -11,10 +11,9 @@
 
 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();
+  state->MaybeForceGCAndPump();
   return 0;
 }
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.cc b/testing/fuzzers/pdf_fuzzer_init_public.cc
index ca4bb39..002ffa0 100644
--- a/testing/fuzzers/pdf_fuzzer_init_public.cc
+++ b/testing/fuzzers/pdf_fuzzer_init_public.cc
@@ -66,7 +66,11 @@
   platform_ = InitializeV8ForPDFium(ProgramPath(), std::string());
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 #ifdef PDF_ENABLE_XFA
-  FXGC_Initialize(platform_.get(), nullptr);
+  allocator_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
+  v8::Isolate::CreateParams create_params;
+  create_params.array_buffer_allocator = allocator_.get();
+  isolate_.reset(v8::Isolate::New(create_params));
+  FXGC_Initialize(platform_.get(), isolate_.get());
   heap_ = FXGC_CreateHeap();
 #endif  // PDF_ENABLE_XFA
 #endif  // PDF_ENABLE_V8
@@ -74,10 +78,12 @@
   memset(&config_, '\0', sizeof(config_));
   config_.version = 3;
   config_.m_pUserFontPaths = nullptr;
+  config_.m_pPlatform = nullptr;
   config_.m_pIsolate = nullptr;
   config_.m_v8EmbedderSlot = 0;
 #ifdef PDF_ENABLE_V8
   config_.m_pPlatform = platform_.get();
+  config_.m_pIsolate = isolate_.get();
 #endif  // PDF_ENABLE_V8
   FPDF_InitLibraryWithConfig(&config_);
 
@@ -87,6 +93,23 @@
   FSDK_SetUnSpObjProcessHandler(&unsupport_info_);
 }
 
+#ifdef PDF_ENABLE_V8
+#ifdef PDF_ENABLE_XFA
+cppgc::Heap* PDFFuzzerPublic::GetHeap() const {
+  return heap_.get();
+}
+
+void PDFFuzzerPublic::MaybeForceGCAndPump() {
+  if (++iterations_ > 1000) {
+    FXGC_ForceGarbageCollection(heap_.get());
+    iterations_ = 0;
+  }
+  while (v8::platform::PumpMessageLoop(platform_.get(), isolate_.get()))
+    continue;
+}
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
+
 PDFFuzzerPublic::~PDFFuzzerPublic() = default;
 
 PDFFuzzerInitPublic::PDFFuzzerInitPublic()
@@ -94,4 +117,6 @@
   FPDF_SetFuzzerPerProcessState(context_.get());
 }
 
-PDFFuzzerInitPublic::~PDFFuzzerInitPublic() = default;
+PDFFuzzerInitPublic::~PDFFuzzerInitPublic() {
+  FPDF_SetFuzzerPerProcessState(nullptr);
+}
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.h b/testing/fuzzers/pdf_fuzzer_init_public.h
index a66c2ff..7711548 100644
--- a/testing/fuzzers/pdf_fuzzer_init_public.h
+++ b/testing/fuzzers/pdf_fuzzer_init_public.h
@@ -11,6 +11,7 @@
 #include "public/fpdfview.h"
 
 #ifdef PDF_ENABLE_V8
+#include "fxjs/cfx_v8.h"
 #include "v8/include/v8-platform.h"
 #include "v8/include/v8.h"
 #ifdef PDF_ENABLE_XFA
@@ -22,11 +23,14 @@
 class PDFFuzzerPublic {
  public:
   PDFFuzzerPublic();
-  ~PDFFuzzerPublic();
+  virtual ~PDFFuzzerPublic();
 
 #ifdef PDF_ENABLE_V8
 #ifdef PDF_ENABLE_XFA
-  cppgc::Heap* GetHeap() { return heap_.get(); }
+  // Virtualize to avoid linker issues in component builds. This results
+  // in an indirect function callback to code in a higher layer.
+  virtual cppgc::Heap* GetHeap() const;
+  virtual void MaybeForceGCAndPump();
 #endif  // PDF_ENABLE_XFA
 #endif  // PDF_ENABLE_V8
 
@@ -34,9 +38,12 @@
   FPDF_LIBRARY_CONFIG config_;
   UNSUPPORT_INFO unsupport_info_;
 #ifdef PDF_ENABLE_V8
-  std::unique_ptr<v8::Platform> platform_;
   v8::StartupData snapshot_blob_;
+  std::unique_ptr<v8::Platform> platform_;
+  std::unique_ptr<v8::ArrayBuffer::Allocator> allocator_;
+  std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> isolate_;
 #ifdef PDF_ENABLE_XFA
+  uint32_t iterations_ = 0;
   FXGCScopedHeap heap_;
 #endif  // PDF_ENABLE_XFA
 #endif  // PDF_ENABLE_V8