xref: /aosp_15_r20/external/pdfium/core/fpdfapi/parser/cpdf_cross_ref_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_cross_ref_avail.h"
6 
7 #include "core/fpdfapi/parser/cpdf_dictionary.h"
8 #include "core/fpdfapi/parser/cpdf_read_validator.h"
9 #include "core/fpdfapi/parser/cpdf_reference.h"
10 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
11 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
12 #include "third_party/base/check.h"
13 #include "third_party/base/containers/contains.h"
14 #include "third_party/base/numerics/safe_conversions.h"
15 
16 namespace {
17 
18 constexpr char kCrossRefKeyword[] = "xref";
19 constexpr char kTrailerKeyword[] = "trailer";
20 constexpr char kPrevCrossRefFieldKey[] = "Prev";
21 constexpr char kTypeFieldKey[] = "Type";
22 constexpr char kPrevCrossRefStreamOffsetFieldKey[] = "XRefStm";
23 constexpr char kXRefKeyword[] = "XRef";
24 constexpr char kEncryptKey[] = "Encrypt";
25 
26 }  // namespace
27 
CPDF_CrossRefAvail(CPDF_SyntaxParser * parser,FX_FILESIZE last_crossref_offset)28 CPDF_CrossRefAvail::CPDF_CrossRefAvail(CPDF_SyntaxParser* parser,
29                                        FX_FILESIZE last_crossref_offset)
30     : parser_(parser), last_crossref_offset_(last_crossref_offset) {
31   DCHECK(parser_);
32   AddCrossRefForCheck(last_crossref_offset);
33 }
34 
35 CPDF_CrossRefAvail::~CPDF_CrossRefAvail() = default;
36 
CheckAvail()37 CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() {
38   if (status_ == CPDF_DataAvail::kDataAvailable)
39     return CPDF_DataAvail::kDataAvailable;
40 
41   CPDF_ReadValidator::ScopedSession read_session(GetValidator());
42   while (true) {
43     bool check_result = false;
44     switch (state_) {
45       case State::kCrossRefCheck:
46         check_result = CheckCrossRef();
47         break;
48       case State::kCrossRefV4ItemCheck:
49         check_result = CheckCrossRefV4Item();
50         break;
51       case State::kCrossRefV4TrailerCheck:
52         check_result = CheckCrossRefV4Trailer();
53         break;
54       case State::kDone:
55         break;
56     }
57     if (!check_result)
58       break;
59 
60     DCHECK(!GetValidator()->has_read_problems());
61   }
62   return status_;
63 }
64 
CheckReadProblems()65 bool CPDF_CrossRefAvail::CheckReadProblems() {
66   if (GetValidator()->read_error()) {
67     status_ = CPDF_DataAvail::kDataError;
68     return true;
69   }
70   return GetValidator()->has_unavailable_data();
71 }
72 
CheckCrossRef()73 bool CPDF_CrossRefAvail::CheckCrossRef() {
74   if (cross_refs_for_check_.empty()) {
75     // All cross refs were checked.
76     state_ = State::kDone;
77     status_ = CPDF_DataAvail::kDataAvailable;
78     return true;
79   }
80   parser_->SetPos(cross_refs_for_check_.front());
81 
82   const ByteString first_word = parser_->PeekNextWord();
83   if (CheckReadProblems())
84     return false;
85 
86   const bool result = (first_word == kCrossRefKeyword) ? CheckCrossRefV4()
87                                                        : CheckCrossRefStream();
88 
89   if (result)
90     cross_refs_for_check_.pop();
91 
92   return result;
93 }
94 
CheckCrossRefV4()95 bool CPDF_CrossRefAvail::CheckCrossRefV4() {
96   const ByteString keyword = parser_->GetKeyword();
97   if (CheckReadProblems())
98     return false;
99 
100   if (keyword != kCrossRefKeyword) {
101     status_ = CPDF_DataAvail::kDataError;
102     return false;
103   }
104 
105   state_ = State::kCrossRefV4ItemCheck;
106   offset_ = parser_->GetPos();
107   return true;
108 }
109 
CheckCrossRefV4Item()110 bool CPDF_CrossRefAvail::CheckCrossRefV4Item() {
111   parser_->SetPos(offset_);
112   const ByteString keyword = parser_->GetKeyword();
113   if (CheckReadProblems())
114     return false;
115 
116   if (keyword.IsEmpty()) {
117     status_ = CPDF_DataAvail::kDataError;
118     return false;
119   }
120 
121   if (keyword == kTrailerKeyword)
122     state_ = State::kCrossRefV4TrailerCheck;
123 
124   // Go to next item.
125   offset_ = parser_->GetPos();
126   return true;
127 }
128 
CheckCrossRefV4Trailer()129 bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() {
130   parser_->SetPos(offset_);
131 
132   RetainPtr<CPDF_Dictionary> trailer =
133       ToDictionary(parser_->GetObjectBody(nullptr));
134   if (CheckReadProblems())
135     return false;
136 
137   if (!trailer) {
138     status_ = CPDF_DataAvail::kDataError;
139     return false;
140   }
141 
142   if (ToReference(trailer->GetObjectFor(kEncryptKey))) {
143     status_ = CPDF_DataAvail::kDataError;
144     return false;
145   }
146 
147   const int32_t xrefpos = trailer->GetDirectIntegerFor(kPrevCrossRefFieldKey);
148   if (xrefpos > 0 &&
149       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos))
150     AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
151 
152   const int32_t stream_xref_offset =
153       trailer->GetDirectIntegerFor(kPrevCrossRefStreamOffsetFieldKey);
154   if (stream_xref_offset > 0 &&
155       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(
156           stream_xref_offset))
157     AddCrossRefForCheck(static_cast<FX_FILESIZE>(stream_xref_offset));
158 
159   // Goto check next crossref
160   state_ = State::kCrossRefCheck;
161   return true;
162 }
163 
CheckCrossRefStream()164 bool CPDF_CrossRefAvail::CheckCrossRefStream() {
165   auto cross_ref =
166       parser_->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose);
167   if (CheckReadProblems())
168     return false;
169 
170   RetainPtr<const CPDF_Dictionary> trailer =
171       cross_ref && cross_ref->IsStream() ? cross_ref->GetDict() : nullptr;
172   if (!trailer) {
173     status_ = CPDF_DataAvail::kDataError;
174     return false;
175   }
176 
177   if (ToReference(trailer->GetObjectFor(kEncryptKey))) {
178     status_ = CPDF_DataAvail::kDataError;
179     return false;
180   }
181 
182   if (trailer->GetNameFor(kTypeFieldKey) == kXRefKeyword) {
183     const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey);
184     if (xrefpos > 0 &&
185         pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos)) {
186       AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
187     }
188   }
189   // Goto check next crossref
190   state_ = State::kCrossRefCheck;
191   return true;
192 }
193 
AddCrossRefForCheck(FX_FILESIZE crossref_offset)194 void CPDF_CrossRefAvail::AddCrossRefForCheck(FX_FILESIZE crossref_offset) {
195   if (pdfium::Contains(registered_crossrefs_, crossref_offset))
196     return;
197 
198   cross_refs_for_check_.push(crossref_offset);
199   registered_crossrefs_.insert(crossref_offset);
200 }
201 
GetValidator()202 RetainPtr<CPDF_ReadValidator> CPDF_CrossRefAvail::GetValidator() {
203   return parser_->GetValidator();
204 }
205