Trim excess /Limits array entries in name trees.
Add a new CPDF_NameTree test case for /Limits arrays with excess
elements. Check for this condition inside cpdf_nametree.cpp, where the
GetNodeLimitsMaybeSwap() helper function becomes
GetNodeLimitsAndSanitize().
Change-Id: Iaa06eeb65381cfad646560e0409c43b4d88765dc
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/86910
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
index 546a573..c8cf0b4 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -22,7 +22,8 @@
constexpr int kNameTreeMaxRecursion = 32;
-std::pair<WideString, WideString> GetNodeLimitsMaybeSwap(CPDF_Array* pLimits) {
+std::pair<WideString, WideString> GetNodeLimitsAndSanitize(
+ CPDF_Array* pLimits) {
DCHECK(pLimits);
WideString csLeft = pLimits->GetUnicodeTextAt(0);
WideString csRight = pLimits->GetUnicodeTextAt(1);
@@ -33,6 +34,8 @@
csLeft = pLimits->GetUnicodeTextAt(0);
csRight = pLimits->GetUnicodeTextAt(1);
}
+ while (pLimits->size() > 2)
+ pLimits->RemoveAt(pLimits->size() - 1);
return {csLeft, csRight};
}
@@ -82,7 +85,7 @@
WideString csLeft;
WideString csRight;
if (pLimits)
- std::tie(csLeft, csRight) = GetNodeLimitsMaybeSwap(pLimits);
+ std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits);
CPDF_Array* pNames = pNode->GetArrayFor("Names");
if (pNames) {
@@ -170,7 +173,7 @@
if (pLimits) {
WideString csLeft;
WideString csRight;
- std::tie(csLeft, csRight) = GetNodeLimitsMaybeSwap(pLimits);
+ std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits);
// Skip this node if the name to look for is smaller than its lower limit.
if (csName.Compare(csLeft) < 0)
return nullptr;
diff --git a/core/fpdfdoc/cpdf_nametree_unittest.cpp b/core/fpdfdoc/cpdf_nametree_unittest.cpp
index eb41d97..36617e7 100644
--- a/core/fpdfdoc/cpdf_nametree_unittest.cpp
+++ b/core/fpdfdoc/cpdf_nametree_unittest.cpp
@@ -39,6 +39,7 @@
ASSERT_TRUE(node);
const CPDF_Array* limits = node->GetArrayFor("Limits");
ASSERT_TRUE(limits);
+ EXPECT_EQ(2u, limits->size());
const CPDF_String* left = ToString(limits->GetObjectAt(0));
ASSERT_TRUE(left);
const CPDF_String* right = ToString(limits->GetObjectAt(1));
@@ -124,6 +125,28 @@
EXPECT_EQ(100, pNumber->GetInteger());
}
+TEST(cpdf_nametree, GetFromTreeWithLimitsArrayWith4Items) {
+ // After creating a name tree, mutate a /Limits array so it has excess
+ // elements.
+ auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
+ FillNameTreeDict(pRootDict.Get());
+ CPDF_Dictionary* pKid1 = pRootDict->GetArrayFor("Kids")->GetDictAt(0);
+ CPDF_Dictionary* pGrandKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1);
+ CPDF_Array* pLimits = pGrandKid3->GetArrayFor("Limits");
+ ASSERT_EQ(2u, pLimits->size());
+ pLimits->AppendNew<CPDF_Number>(5);
+ pLimits->AppendNew<CPDF_Number>(6);
+ ASSERT_EQ(4u, pLimits->size());
+ std::unique_ptr<CPDF_NameTree> name_tree =
+ CPDF_NameTree::CreateForTesting(pRootDict.Get());
+
+ const CPDF_Number* pNumber = ToNumber(name_tree->LookupValue(L"9.txt"));
+ ASSERT_TRUE(pNumber);
+ EXPECT_EQ(999, pNumber->GetInteger());
+ CheckLimitsArray(pKid1, "1.txt", "9.txt");
+ CheckLimitsArray(pGrandKid3, "9.txt", "9.txt");
+}
+
TEST(cpdf_nametree, AddIntoNames) {
// Set up a name tree with a single Names array.
auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();