Fix stack-overflow in CheckForSharedFormInternal()

CheckForSharedFormInternal() is called recursively to traverse the
nodes in a meta data tree. If the meta data contains corrupted data
and never closes XML tags correctly, it could trigger stack-overflow
in this function.

This CL adds `kMaxMetaDataDepth` as the arbitrary maximum meta data
tree depth to prevent too many recursive calls of
CheckForSharedFormInternal() and adds a unit test for it. Also
renames `pElement` to `xml_element` to fit the c++ style guide better.

Bug: chromium:1012377
Change-Id: Iaad7a07c6dc17d37130fdfd1e02441ba50b2066e
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/108910
Commit-Queue: Nigi <nigi@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfdoc/cpdf_metadata.cpp b/core/fpdfdoc/cpdf_metadata.cpp
index 228a0c1..31564f4 100644
--- a/core/fpdfdoc/cpdf_metadata.cpp
+++ b/core/fpdfdoc/cpdf_metadata.cpp
@@ -20,8 +20,15 @@
 
 namespace {
 
-void CheckForSharedFormInternal(CFX_XMLElement* element,
+constexpr int kMaxMetaDataDepth = 128;
+
+bool CheckForSharedFormInternal(int depth,
+                                CFX_XMLElement* element,
                                 std::vector<UnsupportedFeature>* unsupported) {
+  if (depth >= kMaxMetaDataDepth) {
+    return false;
+  }
+
   WideString attr =
       element->GetAttribute(WideString::FromASCII("xmlns:adhocwf"));
   if (attr.EqualsASCII("http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/")) {
@@ -54,10 +61,13 @@
 
   for (auto* child = element->GetFirstChild(); child;
        child = child->GetNextSibling()) {
-    CFX_XMLElement* pElement = ToXMLElement(child);
-    if (pElement)
-      CheckForSharedFormInternal(pElement, unsupported);
+    CFX_XMLElement* xml_element = ToXMLElement(child);
+    if (xml_element &&
+        !CheckForSharedFormInternal(depth + 1, xml_element, unsupported)) {
+      return false;
+    }
   }
+  return true;
 }
 
 }  // namespace
@@ -80,6 +90,6 @@
     return {};
 
   std::vector<UnsupportedFeature> unsupported;
-  CheckForSharedFormInternal(doc->GetRoot(), &unsupported);
+  CheckForSharedFormInternal(/*depth=*/0, doc->GetRoot(), &unsupported);
   return unsupported;
 }
diff --git a/core/fpdfdoc/cpdf_metadata_unittest.cpp b/core/fpdfdoc/cpdf_metadata_unittest.cpp
index cfcb8f3..4bd30ed 100644
--- a/core/fpdfdoc/cpdf_metadata_unittest.cpp
+++ b/core/fpdfdoc/cpdf_metadata_unittest.cpp
@@ -106,6 +106,39 @@
   EXPECT_EQ(0U, results.size());
 }
 
+TEST(CPDF_MetadataTest, CheckSharedFormExceedMaxDepth) {
+  // Node <parent> has the depth of 130, which exceeds the maximum node depth of
+  // `kMaxMetaDataDepth`.
+  static const char kData[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<parent>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>0</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>"
+      "</parent>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(kData).raw_span());
+  CPDF_Metadata metadata(stream);
+
+  auto results = metadata.CheckForSharedForm();
+  ASSERT_EQ(0U, results.size());
+}
+
 TEST(CPDF_MetadataTest, CheckSharedFormWrongNamespace) {
   static const char data[] =
       "<?xml charset=\"utf-8\"?>\n"