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