xref: /aosp_15_r20/external/pdfium/core/fpdfapi/parser/cpdf_object_avail.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 <utility>
8 
9 #include "core/fpdfapi/parser/cpdf_dictionary.h"
10 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
11 #include "core/fpdfapi/parser/cpdf_object_walker.h"
12 #include "core/fpdfapi/parser/cpdf_read_validator.h"
13 #include "core/fpdfapi/parser/cpdf_reference.h"
14 #include "third_party/base/check.h"
15 #include "third_party/base/containers/contains.h"
16 
CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,CPDF_IndirectObjectHolder * holder,RetainPtr<const CPDF_Object> root)17 CPDF_ObjectAvail::CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,
18                                    CPDF_IndirectObjectHolder* holder,
19                                    RetainPtr<const CPDF_Object> root)
20     : validator_(std::move(validator)),
21       holder_(holder),
22       root_(std::move(root)) {
23   DCHECK(validator_);
24   DCHECK(holder);
25   DCHECK(root_);
26   if (!root_->IsInline())
27     parsed_objnums_.insert(root_->GetObjNum());
28 }
29 
CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,CPDF_IndirectObjectHolder * holder,uint32_t obj_num)30 CPDF_ObjectAvail::CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,
31                                    CPDF_IndirectObjectHolder* holder,
32                                    uint32_t obj_num)
33     : validator_(std::move(validator)),
34       holder_(holder),
35       root_(pdfium::MakeRetain<CPDF_Reference>(holder, obj_num)) {
36   DCHECK(validator_);
37   DCHECK(holder);
38 }
39 
40 CPDF_ObjectAvail::~CPDF_ObjectAvail() = default;
41 
CheckAvail()42 CPDF_DataAvail::DocAvailStatus CPDF_ObjectAvail::CheckAvail() {
43   if (!LoadRootObject())
44     return CPDF_DataAvail::kDataNotAvailable;
45 
46   if (CheckObjects()) {
47     CleanMemory();
48     return CPDF_DataAvail::kDataAvailable;
49   }
50   return CPDF_DataAvail::kDataNotAvailable;
51 }
52 
LoadRootObject()53 bool CPDF_ObjectAvail::LoadRootObject() {
54   if (!non_parsed_objects_.empty())
55     return true;
56 
57   while (root_ && root_->IsReference()) {
58     const uint32_t ref_obj_num = root_->AsReference()->GetRefObjNum();
59     if (HasObjectParsed(ref_obj_num)) {
60       root_ = nullptr;
61       return true;
62     }
63 
64     CPDF_ReadValidator::ScopedSession parse_session(validator_);
65     RetainPtr<CPDF_Object> direct =
66         holder_->GetOrParseIndirectObject(ref_obj_num);
67     if (validator_->has_read_problems())
68       return false;
69 
70     parsed_objnums_.insert(ref_obj_num);
71     root_ = std::move(direct);
72   }
73   std::stack<uint32_t> non_parsed_objects_in_root;
74   if (AppendObjectSubRefs(root_, &non_parsed_objects_in_root)) {
75     non_parsed_objects_ = std::move(non_parsed_objects_in_root);
76     return true;
77   }
78   return false;
79 }
80 
CheckObjects()81 bool CPDF_ObjectAvail::CheckObjects() {
82   std::set<uint32_t> checked_objects;
83   std::stack<uint32_t> objects_to_check = std::move(non_parsed_objects_);
84   non_parsed_objects_ = std::stack<uint32_t>();
85   while (!objects_to_check.empty()) {
86     const uint32_t obj_num = objects_to_check.top();
87     objects_to_check.pop();
88 
89     if (HasObjectParsed(obj_num))
90       continue;
91 
92     if (!checked_objects.insert(obj_num).second)
93       continue;
94 
95     CPDF_ReadValidator::ScopedSession parse_session(validator_);
96     RetainPtr<const CPDF_Object> direct =
97         holder_->GetOrParseIndirectObject(obj_num);
98     if (direct == root_)
99       continue;
100 
101     if (validator_->has_read_problems() ||
102         !AppendObjectSubRefs(std::move(direct), &objects_to_check)) {
103       non_parsed_objects_.push(obj_num);
104       continue;
105     }
106     parsed_objnums_.insert(obj_num);
107   }
108   return non_parsed_objects_.empty();
109 }
110 
AppendObjectSubRefs(RetainPtr<const CPDF_Object> object,std::stack<uint32_t> * refs) const111 bool CPDF_ObjectAvail::AppendObjectSubRefs(RetainPtr<const CPDF_Object> object,
112                                            std::stack<uint32_t>* refs) const {
113   DCHECK(refs);
114   if (!object)
115     return true;
116 
117   CPDF_ObjectWalker walker(std::move(object));
118   while (RetainPtr<const CPDF_Object> obj = walker.GetNext()) {
119     CPDF_ReadValidator::ScopedSession parse_session(validator_);
120 
121     // Skip if this object if it's an inlined root, the parent object or
122     // explicitily excluded.
123     const bool skip = (walker.GetParent() && obj == root_) ||
124                       walker.dictionary_key() == "Parent" ||
125                       (obj != root_ && ExcludeObject(obj.Get()));
126 
127     // We need to parse the object before we can do the exclusion check.
128     // This is because the exclusion check may check against a referenced
129     // field of the object which we need to make sure is loaded.
130     if (validator_->has_read_problems())
131       return false;
132 
133     if (skip) {
134       walker.SkipWalkIntoCurrentObject();
135       continue;
136     }
137 
138     if (obj->IsReference())
139       refs->push(obj->AsReference()->GetRefObjNum());
140   }
141   return true;
142 }
143 
CleanMemory()144 void CPDF_ObjectAvail::CleanMemory() {
145   root_.Reset();
146   parsed_objnums_.clear();
147 }
148 
ExcludeObject(const CPDF_Object * object) const149 bool CPDF_ObjectAvail::ExcludeObject(const CPDF_Object* object) const {
150   return false;
151 }
152 
HasObjectParsed(uint32_t obj_num) const153 bool CPDF_ObjectAvail::HasObjectParsed(uint32_t obj_num) const {
154   return pdfium::Contains(parsed_objnums_, obj_num);
155 }
156