xref: /aosp_15_r20/external/pdfium/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2017 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "core/fpdfapi/parser/cpdf_object_avail.h"
6 
7 #include <map>
8 #include <utility>
9 
10 #include "core/fpdfapi/parser/cpdf_array.h"
11 #include "core/fpdfapi/parser/cpdf_dictionary.h"
12 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
13 #include "core/fpdfapi/parser/cpdf_read_validator.h"
14 #include "core/fpdfapi/parser/cpdf_reference.h"
15 #include "core/fpdfapi/parser/cpdf_string.h"
16 #include "core/fxcrt/fx_stream.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "testing/invalid_seekable_read_stream.h"
19 #include "third_party/base/check.h"
20 #include "third_party/base/notreached.h"
21 
22 namespace {
23 
24 class TestReadValidator final : public CPDF_ReadValidator {
25  public:
26   CONSTRUCT_VIA_MAKE_RETAIN;
27 
SimulateReadError()28   void SimulateReadError() { ReadBlockAtOffset({}, 0); }
29 
30  private:
TestReadValidator()31   TestReadValidator()
32       : CPDF_ReadValidator(pdfium::MakeRetain<InvalidSeekableReadStream>(100),
33                            nullptr) {}
34   ~TestReadValidator() override = default;
35 };
36 
37 class TestHolder final : public CPDF_IndirectObjectHolder {
38  public:
39   enum class ObjectState {
40     Unavailable,
41     Available,
42   };
TestHolder()43   TestHolder() : validator_(pdfium::MakeRetain<TestReadValidator>()) {}
44   ~TestHolder() override = default;
45 
46   // CPDF_IndirectObjectHolder overrides:
ParseIndirectObject(uint32_t objnum)47   RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override {
48     auto it = objects_data_.find(objnum);
49     if (it == objects_data_.end())
50       return nullptr;
51 
52     ObjectData& obj_data = it->second;
53     if (obj_data.state == ObjectState::Unavailable) {
54       validator_->SimulateReadError();
55       return nullptr;
56     }
57     return obj_data.object;
58   }
59 
GetValidator()60   RetainPtr<CPDF_ReadValidator> GetValidator() { return validator_; }
61 
AddObject(uint32_t objnum,RetainPtr<CPDF_Object> object,ObjectState state)62   void AddObject(uint32_t objnum,
63                  RetainPtr<CPDF_Object> object,
64                  ObjectState state) {
65     ObjectData object_data;
66     object_data.object = std::move(object);
67     object_data.state = state;
68     DCHECK(objects_data_.find(objnum) == objects_data_.end());
69     objects_data_[objnum] = std::move(object_data);
70   }
71 
SetObjectState(uint32_t objnum,ObjectState state)72   void SetObjectState(uint32_t objnum, ObjectState state) {
73     auto it = objects_data_.find(objnum);
74     DCHECK(it != objects_data_.end());
75     ObjectData& obj_data = it->second;
76     obj_data.state = state;
77   }
78 
GetTestObject(uint32_t objnum)79   CPDF_Object* GetTestObject(uint32_t objnum) {
80     auto it = objects_data_.find(objnum);
81     if (it == objects_data_.end())
82       return nullptr;
83     return it->second.object.Get();
84   }
85 
86  private:
87   struct ObjectData {
88     RetainPtr<CPDF_Object> object;
89     ObjectState state = ObjectState::Unavailable;
90   };
91   std::map<uint32_t, ObjectData> objects_data_;
92   RetainPtr<TestReadValidator> validator_;
93 };
94 
95 class CPDF_ObjectAvailFailOnExclude final : public CPDF_ObjectAvail {
96  public:
97   using CPDF_ObjectAvail::CPDF_ObjectAvail;
98   ~CPDF_ObjectAvailFailOnExclude() override = default;
ExcludeObject(const CPDF_Object * object) const99   bool ExcludeObject(const CPDF_Object* object) const override {
100     NOTREACHED();
101     return false;
102   }
103 };
104 
105 class CPDF_ObjectAvailExcludeArray final : public CPDF_ObjectAvail {
106  public:
107   using CPDF_ObjectAvail::CPDF_ObjectAvail;
108   ~CPDF_ObjectAvailExcludeArray() override = default;
ExcludeObject(const CPDF_Object * object) const109   bool ExcludeObject(const CPDF_Object* object) const override {
110     return object->IsArray();
111   }
112 };
113 
114 class CPDF_ObjectAvailExcludeTypeKey final : public CPDF_ObjectAvail {
115  public:
116   using CPDF_ObjectAvail::CPDF_ObjectAvail;
117   ~CPDF_ObjectAvailExcludeTypeKey() override = default;
ExcludeObject(const CPDF_Object * object) const118   bool ExcludeObject(const CPDF_Object* object) const override {
119     // The value of "Type" may be reference, and if it is not available, we can
120     // incorrect filter objects.
121     // In this case CPDF_ObjectAvail should wait availability of this item and
122     // call ExcludeObject again.
123     return object->IsDictionary() &&
124            object->GetDict()->GetByteStringFor("Type") == "Exclude me";
125   }
126 };
127 
128 }  // namespace
129 
TEST(ObjectAvailTest,OneObject)130 TEST(ObjectAvailTest, OneObject) {
131   TestHolder holder;
132   holder.AddObject(1, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
133                    TestHolder::ObjectState::Unavailable);
134   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
135   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
136   holder.SetObjectState(1, TestHolder::ObjectState::Available);
137   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
138 }
139 
TEST(ObjectAvailTest,OneReferencedObject)140 TEST(ObjectAvailTest, OneReferencedObject) {
141   TestHolder holder;
142   holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
143                    TestHolder::ObjectState::Unavailable);
144   holder.AddObject(2, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
145                    TestHolder::ObjectState::Unavailable);
146   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
147   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
148 
149   holder.SetObjectState(1, TestHolder::ObjectState::Available);
150   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
151 
152   holder.SetObjectState(2, TestHolder::ObjectState::Available);
153   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
154 }
155 
TEST(ObjectAvailTest,CycledReferences)156 TEST(ObjectAvailTest, CycledReferences) {
157   TestHolder holder;
158   holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
159                    TestHolder::ObjectState::Unavailable);
160   holder.AddObject(2, pdfium::MakeRetain<CPDF_Reference>(&holder, 3),
161                    TestHolder::ObjectState::Unavailable);
162   holder.AddObject(3, pdfium::MakeRetain<CPDF_Reference>(&holder, 1),
163                    TestHolder::ObjectState::Unavailable);
164 
165   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
166   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
167 
168   holder.SetObjectState(1, TestHolder::ObjectState::Available);
169   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
170 
171   holder.SetObjectState(2, TestHolder::ObjectState::Available);
172   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
173 
174   holder.SetObjectState(3, TestHolder::ObjectState::Available);
175   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
176 }
177 
TEST(ObjectAvailTest,DoNotCheckParent)178 TEST(ObjectAvailTest, DoNotCheckParent) {
179   TestHolder holder;
180   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
181                    TestHolder::ObjectState::Unavailable);
182   holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
183                    TestHolder::ObjectState::Unavailable);
184 
185   holder.GetTestObject(2)->GetMutableDict()->SetNewFor<CPDF_Reference>(
186       "Parent", &holder, 1);
187 
188   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 2);
189   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
190 
191   holder.SetObjectState(2, TestHolder::ObjectState::Available);
192   //  Object should be available in case when "Parent" object is unavailable.
193   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
194 }
195 
TEST(ObjectAvailTest,Generic)196 TEST(ObjectAvailTest, Generic) {
197   TestHolder holder;
198   const uint32_t kDepth = 100;
199   for (uint32_t i = 1; i < kDepth; ++i) {
200     holder.AddObject(i, pdfium::MakeRetain<CPDF_Dictionary>(),
201                      TestHolder::ObjectState::Unavailable);
202     // Add ref to next dictionary.
203     holder.GetTestObject(i)->GetMutableDict()->SetNewFor<CPDF_Reference>(
204         "Child", &holder, i + 1);
205   }
206   // Add final object
207   holder.AddObject(kDepth, pdfium::MakeRetain<CPDF_Dictionary>(),
208                    TestHolder::ObjectState::Unavailable);
209 
210   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
211   for (uint32_t i = 1; i <= kDepth; ++i) {
212     EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
213     holder.SetObjectState(i, TestHolder::ObjectState::Available);
214   }
215   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
216 }
217 
TEST(ObjectAvailTest,NotExcludeRoot)218 TEST(ObjectAvailTest, NotExcludeRoot) {
219   TestHolder holder;
220   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
221                    TestHolder::ObjectState::Available);
222   CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1);
223   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
224 }
225 
TEST(ObjectAvailTest,NotExcludeReferedRoot)226 TEST(ObjectAvailTest, NotExcludeReferedRoot) {
227   TestHolder holder;
228   holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
229                    TestHolder::ObjectState::Available);
230   holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
231                    TestHolder::ObjectState::Available);
232   CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1);
233   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
234 }
235 
TEST(ObjectAvailTest,Exclude)236 TEST(ObjectAvailTest, Exclude) {
237   TestHolder holder;
238   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
239                    TestHolder::ObjectState::Available);
240   holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
241       "ArrayRef", &holder, 2);
242   holder.AddObject(2, pdfium::MakeRetain<CPDF_Array>(),
243                    TestHolder::ObjectState::Available);
244   holder.GetTestObject(2)->AsMutableArray()->AppendNew<CPDF_Reference>(&holder,
245                                                                        2);
246 
247   // Add string, which is refered by array item. It is should not be checked.
248   holder.AddObject(
249       3,
250       pdfium::MakeRetain<CPDF_String>(nullptr, "Not available string", false),
251       TestHolder::ObjectState::Unavailable);
252   CPDF_ObjectAvailExcludeArray avail(holder.GetValidator(), &holder, 1);
253   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
254 }
255 
TEST(ObjectAvailTest,ReadErrorOnExclude)256 TEST(ObjectAvailTest, ReadErrorOnExclude) {
257   TestHolder holder;
258   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
259                    TestHolder::ObjectState::Available);
260   holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
261       "DictRef", &holder, 2);
262   holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
263                    TestHolder::ObjectState::Available);
264 
265   holder.GetTestObject(2)->GetMutableDict()->SetNewFor<CPDF_Reference>(
266       "Type", &holder, 3);
267   // The value of "Type" key is not available at start
268   holder.AddObject(
269       3, pdfium::MakeRetain<CPDF_String>(nullptr, "Exclude me", false),
270       TestHolder::ObjectState::Unavailable);
271 
272   holder.GetTestObject(2)->GetMutableDict()->SetNewFor<CPDF_Reference>(
273       "OtherData", &holder, 4);
274   // Add string, which is referred by dictionary item. It is should not be
275   // checked, because the dictionary with it, should be skipped.
276   holder.AddObject(
277       4,
278       pdfium::MakeRetain<CPDF_String>(nullptr, "Not available string", false),
279       TestHolder::ObjectState::Unavailable);
280 
281   CPDF_ObjectAvailExcludeTypeKey avail(holder.GetValidator(), &holder, 1);
282 
283   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
284 
285   // Make "Type" value object available.
286   holder.SetObjectState(3, TestHolder::ObjectState::Available);
287 
288   // Now object should be available, although the object '4' is not available,
289   // because it is in skipped dictionary.
290   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
291 }
292 
TEST(ObjectAvailTest,IgnoreNotExistsObject)293 TEST(ObjectAvailTest, IgnoreNotExistsObject) {
294   TestHolder holder;
295   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
296                    TestHolder::ObjectState::Available);
297   holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
298       "NotExistsObjRef", &holder, 2);
299   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
300   // Now object should be available, although the object '2' is not exists. But
301   // all exists in file related data are checked.
302   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
303 }
304 
TEST(ObjectAvailTest,CheckTwice)305 TEST(ObjectAvailTest, CheckTwice) {
306   TestHolder holder;
307   holder.AddObject(1, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
308                    TestHolder::ObjectState::Unavailable);
309 
310   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
311   EXPECT_EQ(avail.CheckAvail(), avail.CheckAvail());
312 
313   holder.SetObjectState(1, TestHolder::ObjectState::Available);
314   EXPECT_EQ(avail.CheckAvail(), avail.CheckAvail());
315 }
316 
TEST(ObjectAvailTest,SelfReferedInlinedObject)317 TEST(ObjectAvailTest, SelfReferedInlinedObject) {
318   TestHolder holder;
319   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
320                    TestHolder::ObjectState::Available);
321 
322   holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
323       "Data", &holder, 2);
324   auto root =
325       holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Dictionary>(
326           "Dict");
327 
328   root->SetNewFor<CPDF_Reference>("Self", &holder, 1);
329   holder.AddObject(2, pdfium::MakeRetain<CPDF_String>(nullptr, "Data", false),
330                    TestHolder::ObjectState::Unavailable);
331 
332   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, root);
333   EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
334 
335   holder.SetObjectState(2, TestHolder::ObjectState::Available);
336   EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
337 }
338