Add a way to undo a previous FPDF_SetSystemFontInfo() call

Modify FPDF_SetSystemFontInfo() to be able to take a nullptr without
crashing. Handle the nullptr case by releasing PDFium's hold on the
previously passed in pointer. Since no existing embedder could have
passed in a nullptr before without crashing, this is a safe enhancement
to an existing stable API.

This gives FPDF_SetSystemFontInfo() callers that need to also call
FPDF_FreeDefaultSystemFontInfo() a way to safely release PDFium's
pointer to the default system font info, before freeing the font info.
Use this where appropriate to balance out prior FPDF_SetSystemFontInfo()
calls in existing code.

Update pdfium_test.cc to make the call at the appropriate time, before
the FPDF_DestroyLibrary() call, by adjusting std::unique_ptr lifetimes.

In a follow-up CL, PartitionAllocator class instances will stop living
forever. Thus after FPDF_DestroyLibrary(), it is too late to call
FPDF_FreeDefaultSystemFontInfo(), as the font info was allocated inside
a destroyed partition.

Bug: pdfium:1876
Change-Id: Ic3835bae5f95604f89afb35bfaf8c0d568c6bc4b
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/116771
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_sysfontinfo.cpp b/fpdfsdk/fpdf_sysfontinfo.cpp
index afb873e..57fdbc0 100644
--- a/fpdfsdk/fpdf_sysfontinfo.cpp
+++ b/fpdfsdk/fpdf_sysfontinfo.cpp
@@ -155,10 +155,17 @@
 
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_SetSystemFontInfo(FPDF_SYSFONTINFO* pFontInfoExt) {
+  auto* mapper = CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper();
+  if (!pFontInfoExt) {
+    std::unique_ptr<SystemFontInfoIface> info = mapper->TakeSystemFontInfo();
+    // Delete `info` when it goes out of scope here.
+    return;
+  }
+
   if (pFontInfoExt->version != 1)
     return;
 
-  CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper()->SetSystemFontInfo(
+  mapper->SetSystemFontInfo(
       std::make_unique<CFX_ExternalFontInfo>(pFontInfoExt));
 
 #ifdef PDF_ENABLE_XFA
diff --git a/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
index 24df525..589cf71 100644
--- a/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
+++ b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
@@ -77,9 +77,10 @@
   }
 
   void TearDown() override {
+    FPDF_SetSystemFontInfo(nullptr);
     EmbedderTest::TearDown();
 
-    // Bouncing the library is the only reliable way to undo the
+    // Bouncing the library is the only reliable way to fully undo the initial
     // FPDF_SetSystemFontInfo() call at the moment.
     EmbedderTestEnvironment::GetInstance()->TearDown();
     EmbedderTestEnvironment::GetInstance()->SetUp();
@@ -103,13 +104,14 @@
   void TearDown() override {
     EmbedderTest::TearDown();
 
-    // Bouncing the library is the only reliable way to undo the
+    // After releasing `font_info_` from PDFium, it is safe to free it.
+    FPDF_SetSystemFontInfo(nullptr);
+    FPDF_FreeDefaultSystemFontInfo(font_info_);
+
+    // Bouncing the library is the only reliable way to fully undo the initial
     // FPDF_SetSystemFontInfo() call at the moment.
     EmbedderTestEnvironment::GetInstance()->TearDown();
 
-    // After shutdown, it is safe to release the font info.
-    FPDF_FreeDefaultSystemFontInfo(font_info_);
-
     EmbedderTestEnvironment::GetInstance()->SetUp();
   }
 
diff --git a/public/fpdf_sysfontinfo.h b/public/fpdf_sysfontinfo.h
index e30220c..0ba711d 100644
--- a/public/fpdf_sysfontinfo.h
+++ b/public/fpdf_sysfontinfo.h
@@ -275,6 +275,9 @@
  *          Platform support implementation should implement required methods of
  *          FFDF_SYSFONTINFO interface, then call this function during PDFium
  *          initialization process.
+ *
+ *          Call this with NULL to tell PDFium to stop using a previously set
+ *          |FPDF_SYSFONTINFO|.
  */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_SetSystemFontInfo(FPDF_SYSFONTINFO* pFontInfo);
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index 94fd6a8..27b0d72 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -1944,63 +1944,69 @@
 
   FPDF_InitLibraryWithConfig(&config);
 
-  std::unique_ptr<FontRenamer> font_renamer;
-  if (options.croscore_font_names)
-    font_renamer = std::make_unique<FontRenamer>();
-
-  UNSUPPORT_INFO unsupported_info = {};
-  unsupported_info.version = 1;
-  unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
-
-  FSDK_SetUnSpObjProcessHandler(&unsupported_info);
-
-  if (options.time > -1) {
-    // This must be a static var to avoid explicit capture, so the lambda can be
-    // converted to a function ptr.
-    static time_t time_ret = options.time;
-    FSDK_SetTimeFunction([]() { return time_ret; });
-    FSDK_SetLocaltimeFunction([](const time_t* tp) { return gmtime(tp); });
-  }
-
-  Processor processor(&options, &idler);
-  for (const std::string& filename : files) {
-    std::vector<uint8_t> file_contents = GetFileContents(filename.c_str());
-    if (file_contents.empty()) {
-      continue;
+  {
+    std::unique_ptr<FontRenamer> font_renamer;
+    if (options.croscore_font_names) {
+      // Must be destroyed before FPDF_DestroyLibrary().
+      font_renamer = std::make_unique<FontRenamer>();
     }
-    fprintf(stderr, "Processing PDF file %s.\n", filename.c_str());
+
+    UNSUPPORT_INFO unsupported_info = {};
+    unsupported_info.version = 1;
+    unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
+
+    FSDK_SetUnSpObjProcessHandler(&unsupported_info);
+
+    if (options.time > -1) {
+      // This must be a static var to avoid explicit capture, so the lambda can
+      // be converted to a function ptr.
+      static time_t time_ret = options.time;
+      FSDK_SetTimeFunction([]() { return time_ret; });
+      FSDK_SetLocaltimeFunction([](const time_t* tp) { return gmtime(tp); });
+    }
+
+    Processor processor(&options, &idler);
+    for (const std::string& filename : files) {
+      std::vector<uint8_t> file_contents = GetFileContents(filename.c_str());
+      if (file_contents.empty()) {
+        continue;
+      }
+      fprintf(stderr, "Processing PDF file %s.\n", filename.c_str());
 
 #ifdef ENABLE_CALLGRIND
-    if (options.callgrind_delimiters)
-      CALLGRIND_START_INSTRUMENTATION;
+      if (options.callgrind_delimiters) {
+        CALLGRIND_START_INSTRUMENTATION;
+      }
 #endif  // ENABLE_CALLGRIND
 
-    std::string events;
-    if (options.send_events) {
-      std::string event_filename = filename;
-      size_t extension_pos = event_filename.find(".pdf");
-      if (extension_pos != std::string::npos) {
-        event_filename.replace(extension_pos, 4, ".evt");
-        if (access(event_filename.c_str(), R_OK) == 0) {
-          fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
-          std::vector<uint8_t> event_contents =
-              GetFileContents(event_filename.c_str());
-          if (!event_contents.empty()) {
-            fprintf(stderr, "Sending events from: %s\n",
-                    event_filename.c_str());
-            std::copy(event_contents.begin(), event_contents.end(),
-                      std::back_inserter(events));
+      std::string events;
+      if (options.send_events) {
+        std::string event_filename = filename;
+        size_t extension_pos = event_filename.find(".pdf");
+        if (extension_pos != std::string::npos) {
+          event_filename.replace(extension_pos, 4, ".evt");
+          if (access(event_filename.c_str(), R_OK) == 0) {
+            fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
+            std::vector<uint8_t> event_contents =
+                GetFileContents(event_filename.c_str());
+            if (!event_contents.empty()) {
+              fprintf(stderr, "Sending events from: %s\n",
+                      event_filename.c_str());
+              std::copy(event_contents.begin(), event_contents.end(),
+                        std::back_inserter(events));
+            }
           }
         }
       }
-    }
 
-    processor.ProcessPdf(filename, file_contents, events);
+      processor.ProcessPdf(filename, file_contents, events);
 
 #ifdef ENABLE_CALLGRIND
-    if (options.callgrind_delimiters)
-      CALLGRIND_STOP_INSTRUMENTATION;
+      if (options.callgrind_delimiters) {
+        CALLGRIND_STOP_INSTRUMENTATION;
+      }
 #endif  // ENABLE_CALLGRIND
+    }
   }
 
   FPDF_DestroyLibrary();
diff --git a/testing/font_renamer.cpp b/testing/font_renamer.cpp
index 6a3a81c..1f3dd49 100644
--- a/testing/font_renamer.cpp
+++ b/testing/font_renamer.cpp
@@ -87,5 +87,6 @@
 }
 
 FontRenamer::~FontRenamer() {
+  FPDF_SetSystemFontInfo(nullptr);
   FPDF_FreeDefaultSystemFontInfo(impl_.ExtractAsDangling());
 }