M110: Validate the page count.

In CountPages(), which recursively calls itself, validate the page
count. When any part of the pages tree contains bad data, bail out.

Bug: chromium:1404864
Change-Id: Ifdbc14213ec3f963b4b2cb5793b83c15d03336e8
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/103078
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
(cherry picked from commit 5688d711a1d7104debb5651d71b14ee7dd3c8bcf)
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/103570
diff --git a/core/fpdfapi/parser/cpdf_document.cpp b/core/fpdfapi/parser/cpdf_document.cpp
index 4dcdcc8..ee9dfcd 100644
--- a/core/fpdfapi/parser/cpdf_document.cpp
+++ b/core/fpdfapi/parser/cpdf_document.cpp
@@ -23,6 +23,7 @@
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/scoped_set_insertion.h"
 #include "core/fxcrt/stl_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
 
@@ -30,8 +31,11 @@
 
 const int kMaxPageLevel = 1024;
 
-int CountPages(RetainPtr<CPDF_Dictionary> pPages,
-               std::set<RetainPtr<CPDF_Dictionary>>* visited_pages) {
+// Returns a value in the range [0, `CPDF_Document::kPageMaxNum`), or nullopt on
+// error.
+absl::optional<int> CountPages(
+    RetainPtr<CPDF_Dictionary> pPages,
+    std::set<RetainPtr<CPDF_Dictionary>>* visited_pages) {
   int count = pPages->GetIntegerFor("Count");
   if (count > 0 && count < CPDF_Document::kPageMaxNum)
     return count;
@@ -47,11 +51,19 @@
       // Use |visited_pages| to help detect circular references of pages.
       ScopedSetInsertion<RetainPtr<CPDF_Dictionary>> local_add(visited_pages,
                                                                pKid);
-      count += CountPages(std::move(pKid), visited_pages);
+      absl::optional<int> local_count =
+          CountPages(std::move(pKid), visited_pages);
+      if (!local_count.has_value()) {
+        return absl::nullopt;  // Propagate error.
+      }
+      count += local_count.value();
     } else {
       // This page is a leaf node.
       count++;
     }
+    if (count >= CPDF_Document::kPageMaxNum) {
+      return absl::nullopt;  // Error: too many pages.
+    }
   }
   pPages->SetNewFor<CPDF_Number>("Count", count);
   return count;
@@ -381,7 +393,7 @@
     return 1;
 
   std::set<RetainPtr<CPDF_Dictionary>> visited_pages = {pPages};
-  return CountPages(std::move(pPages), &visited_pages);
+  return CountPages(std::move(pPages), &visited_pages).value_or(0);
 }
 
 uint32_t CPDF_Document::GetUserPermissions() const {