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)30CPDF_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()37CPDF_CrossRefAvail::~CPDF_CrossRefAvail() {} 38 CheckAvail()39CPDF_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()72bool 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()80bool 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()102bool 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()117bool 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()136bool 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()172bool 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)202void 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()210RetainPtr<CPDF_ReadValidator> CPDF_CrossRefAvail::GetValidator() { 211 return parser_->GetValidator(); 212 } 213