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