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)29CPDF_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()38CPDF_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()71bool 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()79bool 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()101bool 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()116bool 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()135bool 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()170bool 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)200void 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()208RetainPtr<CPDF_ReadValidator> CPDF_CrossRefAvail::GetValidator() { 209 return parser_->GetValidator(); 210 } 211