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