From 626c2a528fdbb53ddc6fede8ce879f56bfe87716 Mon Sep 17 00:00:00 2001 From: Artem Strygin Date: Thu, 2 Nov 2017 19:59:38 +0000 Subject: Refactoring of cross refs availability check. Use CPDF_CrossRefAvail to check crossrefs. Change-Id: Ia333cff4e86eaab5bad17424c1bb8ef9bdbca8ff Reviewed-on: https://pdfium-review.googlesource.com/15510 Commit-Queue: Art Snake Reviewed-by: dsinclair --- core/fpdfapi/parser/cpdf_cross_ref_avail.cpp | 211 +++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 core/fpdfapi/parser/cpdf_cross_ref_avail.cpp (limited to 'core/fpdfapi/parser/cpdf_cross_ref_avail.cpp') diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp new file mode 100644 index 0000000000..be9818ae21 --- /dev/null +++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp @@ -0,0 +1,211 @@ +// Copyright 2017 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/fpdfapi/parser/cpdf_cross_ref_avail.h" + +#include +#include + +#include "core/fpdfapi/parser/cpdf_dictionary.h" +#include "core/fpdfapi/parser/cpdf_name.h" +#include "core/fpdfapi/parser/cpdf_read_validator.h" +#include "core/fpdfapi/parser/cpdf_reference.h" +#include "core/fpdfapi/parser/cpdf_syntax_parser.h" +#include "core/fpdfapi/parser/fpdf_parser_utility.h" + +namespace { + +constexpr char kCrossRefKeyword[] = "xref"; +constexpr char kTrailerKeyword[] = "trailer"; +constexpr char kPrevCrossRefFieldKey[] = "Prev"; +constexpr char kTypeFieldKey[] = "Type"; +constexpr char kPrevCrossRefStreamOffsetFieldKey[] = "XRefStm"; +constexpr char kXRefKeyword[] = "XRef"; +constexpr char kEncryptKey[] = "Encrypt"; + +} // namespace + +CPDF_CrossRefAvail::CPDF_CrossRefAvail(CPDF_SyntaxParser* parser, + FX_FILESIZE last_crossref_offset) + : parser_(parser), last_crossref_offset_(last_crossref_offset) { + ASSERT(parser_); + AddCrossRefForCheck(last_crossref_offset); +} + +CPDF_CrossRefAvail::~CPDF_CrossRefAvail() {} + +CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() { + if (current_status_ == CPDF_DataAvail::DataAvailable) + return CPDF_DataAvail::DataAvailable; + + const CPDF_ReadValidator::Session read_session(GetValidator().Get()); + while (true) { + bool check_result = false; + switch (current_state_) { + case State::kCrossRefCheck: + check_result = CheckCrossRef(); + break; + case State::kCrossRefV4ItemCheck: + check_result = CheckCrossRefV4Item(); + break; + case State::kCrossRefV4TrailerCheck: + check_result = CheckCrossRefV4Trailer(); + break; + case State::kDone: + break; + default: { + current_status_ = CPDF_DataAvail::DataError; + NOTREACHED(); + break; + } + } + if (!check_result) + break; + + ASSERT(!GetValidator()->has_read_problems()); + } + return current_status_; +} + +bool CPDF_CrossRefAvail::CheckReadProblems() { + if (GetValidator()->read_error()) { + current_status_ = CPDF_DataAvail::DataError; + return true; + } + return GetValidator()->has_unavailable_data(); +} + +bool CPDF_CrossRefAvail::CheckCrossRef() { + if (cross_refs_for_check_.empty()) { + // All cross refs were checked. + current_state_ = State::kDone; + current_status_ = CPDF_DataAvail::DataAvailable; + return true; + } + parser_->SetPos(cross_refs_for_check_.front()); + + const ByteString first_word = parser_->PeekNextWord(nullptr); + if (CheckReadProblems()) + return false; + + const bool result = (first_word == kCrossRefKeyword) ? CheckCrossRefV4() + : CheckCrossRefStream(); + + if (result) + cross_refs_for_check_.pop(); + + return result; +} + +bool CPDF_CrossRefAvail::CheckCrossRefV4() { + const ByteString keyword = parser_->GetKeyword(); + if (CheckReadProblems()) + return false; + + if (keyword != kCrossRefKeyword) { + current_status_ = CPDF_DataAvail::DataError; + return false; + } + + current_state_ = State::kCrossRefV4ItemCheck; + current_offset_ = parser_->GetPos(); + return true; +} + +bool CPDF_CrossRefAvail::CheckCrossRefV4Item() { + parser_->SetPos(current_offset_); + const ByteString keyword = parser_->GetKeyword(); + if (CheckReadProblems()) + return false; + + if (keyword.IsEmpty()) { + current_status_ = CPDF_DataAvail::DataError; + return false; + } + + if (keyword == kTrailerKeyword) + current_state_ = State::kCrossRefV4TrailerCheck; + + // Go to next item. + current_offset_ = parser_->GetPos(); + return true; +} + +bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() { + parser_->SetPos(current_offset_); + + std::unique_ptr trailer = + ToDictionary(parser_->GetObjectBody(nullptr)); + if (CheckReadProblems()) + return false; + + if (!trailer) { + current_status_ = CPDF_DataAvail::DataError; + return false; + } + + if (ToReference(trailer->GetObjectFor(kEncryptKey))) { + current_status_ = CPDF_DataAvail::DataError; + return false; + } + + const int32_t xrefpos = + GetDirectInteger(trailer.get(), kPrevCrossRefFieldKey); + if (xrefpos && + pdfium::base::IsValueInRangeForNumericType(xrefpos)) + AddCrossRefForCheck(static_cast(xrefpos)); + + const int32_t stream_xref_offset = + GetDirectInteger(trailer.get(), kPrevCrossRefStreamOffsetFieldKey); + if (stream_xref_offset && + pdfium::base::IsValueInRangeForNumericType( + stream_xref_offset)) + AddCrossRefForCheck(static_cast(stream_xref_offset)); + + // Goto check next crossref + current_state_ = State::kCrossRefCheck; + return true; +} + +bool CPDF_CrossRefAvail::CheckCrossRefStream() { + auto cross_ref = + parser_->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose); + if (CheckReadProblems()) + return false; + + const CPDF_Dictionary* trailer = + cross_ref && cross_ref->IsStream() ? cross_ref->GetDict() : nullptr; + if (!trailer) { + current_status_ = CPDF_DataAvail::DataError; + return false; + } + + if (ToReference(trailer->GetObjectFor(kEncryptKey))) { + current_status_ = CPDF_DataAvail::DataError; + return false; + } + + CPDF_Name* type_name = ToName(trailer->GetObjectFor(kTypeFieldKey)); + if (type_name && type_name->GetString() == kXRefKeyword) { + const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey); + if (xrefpos && + pdfium::base::IsValueInRangeForNumericType(xrefpos)) + AddCrossRefForCheck(static_cast(xrefpos)); + } + // Goto check next crossref + current_state_ = State::kCrossRefCheck; + return true; +} + +void CPDF_CrossRefAvail::AddCrossRefForCheck(FX_FILESIZE crossref_offset) { + if (registered_crossrefs_.count(crossref_offset)) + return; + + cross_refs_for_check_.push(crossref_offset); + registered_crossrefs_.insert(crossref_offset); +} + +fxcrt::RetainPtr CPDF_CrossRefAvail::GetValidator() { + return parser_->GetValidator(); +} -- cgit v1.2.3