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 "core/fxcrt/check.h" 13 #include "core/fxcrt/containers/contains.h" 14 #include "core/fxcrt/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::kCrossRefTableItemCheck: 49 check_result = CheckCrossRefTableItem(); 50 break; 51 case State::kCrossRefTableTrailerCheck: 52 check_result = CheckCrossRefTableTrailer(); 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) ? CheckCrossRefTable() 87 : CheckCrossRefStream(); 88 89 if (result) 90 cross_refs_for_check_.pop(); 91 92 return result; 93 } 94 CheckCrossRefTable()95bool CPDF_CrossRefAvail::CheckCrossRefTable() { 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::kCrossRefTableItemCheck; 106 offset_ = parser_->GetPos(); 107 return true; 108 } 109 CheckCrossRefTableItem()110bool CPDF_CrossRefAvail::CheckCrossRefTableItem() { 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::kCrossRefTableTrailerCheck; 123 124 // Go to next item. 125 offset_ = parser_->GetPos(); 126 return true; 127 } 128 CheckCrossRefTableTrailer()129bool CPDF_CrossRefAvail::CheckCrossRefTableTrailer() { 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::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos)) { 150 AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos)); 151 } 152 153 const int32_t stream_xref_offset = 154 trailer->GetDirectIntegerFor(kPrevCrossRefStreamOffsetFieldKey); 155 if (stream_xref_offset > 0 && 156 pdfium::IsValueInRangeForNumericType<FX_FILESIZE>(stream_xref_offset)) { 157 AddCrossRefForCheck(static_cast<FX_FILESIZE>(stream_xref_offset)); 158 } 159 160 // Goto check next crossref 161 state_ = State::kCrossRefCheck; 162 return true; 163 } 164 CheckCrossRefStream()165bool CPDF_CrossRefAvail::CheckCrossRefStream() { 166 auto cross_ref = 167 parser_->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose); 168 if (CheckReadProblems()) 169 return false; 170 171 RetainPtr<const CPDF_Dictionary> trailer = 172 cross_ref && cross_ref->IsStream() ? cross_ref->GetDict() : nullptr; 173 if (!trailer) { 174 status_ = CPDF_DataAvail::kDataError; 175 return false; 176 } 177 178 if (ToReference(trailer->GetObjectFor(kEncryptKey))) { 179 status_ = CPDF_DataAvail::kDataError; 180 return false; 181 } 182 183 if (trailer->GetNameFor(kTypeFieldKey) == kXRefKeyword) { 184 const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey); 185 if (xrefpos > 0 && 186 pdfium::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos)) { 187 AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos)); 188 } 189 } 190 // Goto check next crossref 191 state_ = State::kCrossRefCheck; 192 return true; 193 } 194 AddCrossRefForCheck(FX_FILESIZE crossref_offset)195void CPDF_CrossRefAvail::AddCrossRefForCheck(FX_FILESIZE crossref_offset) { 196 if (pdfium::Contains(registered_crossrefs_, crossref_offset)) 197 return; 198 199 cross_refs_for_check_.push(crossref_offset); 200 registered_crossrefs_.insert(crossref_offset); 201 } 202 GetValidator()203RetainPtr<CPDF_ReadValidator> CPDF_CrossRefAvail::GetValidator() { 204 return parser_->GetValidator(); 205 } 206