Prevent import hang on circular page parents Track visited page parent nodes while copying inherited page attributes so invalid page trees with self-referential parents stop traversal instead of looping forever. Add an embedder regression test covering FPDF_ImportPagesByIndex() and FPDF_ImportPages(). Bug: 517126568 Change-Id: I103779059c32b4d9b9f6675ac621a3392851fe06 Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/148630 Commit-Queue: Tom Sepez <tsepez@chromium.org> Reviewed-by: Lei Zhang <thestig@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pageorganizer.cpp b/core/fpdfapi/edit/cpdf_pageorganizer.cpp index a1810cd..cb63f2b 100644 --- a/core/fpdfapi/edit/cpdf_pageorganizer.cpp +++ b/core/fpdfapi/edit/cpdf_pageorganizer.cpp
@@ -6,6 +6,7 @@ #include "core/fpdfapi/edit/cpdf_pageorganizer.h" +#include <set> #include <utility> #include <vector> @@ -208,7 +209,12 @@ return dict->GetObjectFor(src_tag); } + std::set<const CPDF_Dictionary*> visited_parent_dicts; while (pp) { + if (!visited_parent_dicts.insert(pp.Get()).second) { + return nullptr; + } + if (pp->KeyExist(src_tag)) { return pp->GetObjectFor(src_tag); }
diff --git a/fpdfsdk/fpdf_ppo_embeddertest.cpp b/fpdfsdk/fpdf_ppo_embeddertest.cpp index 0a8084d..6b385c0 100644 --- a/fpdfsdk/fpdf_ppo_embeddertest.cpp +++ b/fpdfsdk/fpdf_ppo_embeddertest.cpp
@@ -618,6 +618,25 @@ CompareBitmapWithExpectationSuffix(new_bitmap.get(), pdfium::kHelloWorldPng); } +TEST_F(FPDFPPOEmbedderTest, ImportWithSelfReferentialPageParent) { + ASSERT_TRUE(OpenDocument("bug_517126568.pdf")); + ASSERT_EQ(1, FPDF_GetPageCount(document())); + + ScopedPage page = LoadScopedPage(0); + ASSERT_TRUE(page); + + ScopedFPDFDocument new_doc(FPDF_CreateNewDocument()); + ASSERT_TRUE(new_doc); + + static constexpr int kIndices[] = {0}; + EXPECT_TRUE(FPDF_ImportPagesByIndex(new_doc.get(), document(), kIndices, + std::size(kIndices), 0)); + EXPECT_EQ(1, FPDF_GetPageCount(new_doc.get())); + + EXPECT_TRUE(FPDF_ImportPages(new_doc.get(), document(), "1", 1)); + EXPECT_EQ(2, FPDF_GetPageCount(new_doc.get())); +} + TEST_F(FPDFPPOEmbedderTest, ImportIntoDestDocWithoutInfo) { ASSERT_TRUE(OpenDocument("hello_world.pdf")); EXPECT_EQ(1, FPDF_GetPageCount(document()));
diff --git a/testing/resources/bug_517126568.in b/testing/resources/bug_517126568.in new file mode 100644 index 0000000..3522606 --- /dev/null +++ b/testing/resources/bug_517126568.in
@@ -0,0 +1,36 @@ +{{header}} +{{object 1 0}} << + /Type /Catalog + /Pages 2 0 R +>> +endobj +{{object 2 0}} << + /Type /Pages + /Parent 2 0 R % Deliberate self-reference for testing circular parent handling + /Count 1 + /Kids [3 0 R] +>> +endobj +{{object 3 0}} << + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 200 200] + /Resources << + >> + /Contents 4 0 R +>> +endobj +{{object 4 0}} << + {{streamlen}} +>> +stream +q +0 0 1 rg +50 60 10 10 re f +Q +endstream +endobj +{{xref}} +{{trailer}} +{{startxref}} +%%EOF
diff --git a/testing/resources/bug_517126568.pdf b/testing/resources/bug_517126568.pdf new file mode 100644 index 0000000..209e1c9 --- /dev/null +++ b/testing/resources/bug_517126568.pdf
@@ -0,0 +1,47 @@ +%PDF-1.7 +% ò¤ô +1 0 obj << + /Type /Catalog + /Pages 2 0 R +>> +endobj +2 0 obj << + /Type /Pages + /Parent 2 0 R % Deliberate self-reference for testing circular parent handling + /Count 1 + /Kids [3 0 R] +>> +endobj +3 0 obj << + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 200 200] + /Resources << + >> + /Contents 4 0 R +>> +endobj +4 0 obj << + /Length 29 +>> +stream +q +0 0 1 rg +50 60 10 10 re f +Q +endstream +endobj +xref +0 5 +0000000000 65535 f +0000000015 00000 n +0000000068 00000 n +0000000213 00000 n +0000000329 00000 n +trailer << + /Root 1 0 R + /Size 5 +>> +startxref +410 +%%EOF