M71: Clone dict before iteration in CJS_Document::get_info

Bug: chromium:895152
TBR=tsepez@chromium.org
Change-Id: I678350841892f88a5d580b58a33a639a1b6ec305
Reviewed-on: https://pdfium-review.googlesource.com/c/44050
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
(cherry picked from commit d2e27d660a96080882e43825fb4b5d03e8a4d05a)
Reviewed-on: https://pdfium-review.googlesource.com/c/47333
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp
index 6e2f2da..ff9cfc3 100644
--- a/fxjs/cjs_document.cpp
+++ b/fxjs/cjs_document.cpp
@@ -744,8 +744,9 @@
   pRuntime->PutObjectProperty(pObj, L"Trapped",
                               pRuntime->NewString(cwTrapped.AsStringView()));
 
-  // It's to be compatible to non-standard info dictionary.
-  for (const auto& it : *pDictionary) {
+  // PutObjectProperty() calls below may re-enter JS and change info dict.
+  auto pCopy = pDictionary->Clone();
+  for (const auto& it : *ToDictionary(pCopy.get())) {
     const ByteString& bsKey = it.first;
     CPDF_Object* pValueObj = it.second.get();
     WideString wsKey = WideString::FromUTF8(bsKey.AsStringView());
diff --git a/testing/resources/javascript/bug_895152.in b/testing/resources/javascript/bug_895152.in
new file mode 100644
index 0000000..3640274
--- /dev/null
+++ b/testing/resources/javascript/bug_895152.in
@@ -0,0 +1,74 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 4 0 R
+  /OpenAction 7 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources <<>>
+  /Annots [5 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Fields [5 0 R]
+>>
+endobj
+{{object 5 0}} <<
+  /V /TestV
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /F 4
+  /AP <</N 6 0 R>>
+  /Rect [200 200 400 400]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 8 0 R
+>>
+endobj
+{{object 8 0}} <<>>
+stream
+function run() {
+  var doc = this;
+  Object.prototype.__defineSetter__('V', function() {
+    doc.resetForm();
+  });
+  var other_info = this.info;
+}
+try {
+  run();
+  app.alert('*** PASS ***');
+} catch (e) {
+  app.alert('Caught: ' + e);
+}
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Info 5 0 R
+>>
+{{startxref}}
+%%EOF
+
diff --git a/testing/resources/javascript/bug_895152_expected.txt b/testing/resources/javascript/bug_895152_expected.txt
new file mode 100644
index 0000000..a7c97be
--- /dev/null
+++ b/testing/resources/javascript/bug_895152_expected.txt
@@ -0,0 +1 @@
+Alert: *** PASS ***