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