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