1 // Copyright 2016 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "core/fpdfapi/parser/cpdf_linearized_header.h" 8 9 #include <algorithm> 10 #include <limits> 11 #include <utility> 12 13 #include "core/fpdfapi/parser/cpdf_array.h" 14 #include "core/fpdfapi/parser/cpdf_dictionary.h" 15 #include "core/fpdfapi/parser/cpdf_number.h" 16 #include "core/fpdfapi/parser/cpdf_syntax_parser.h" 17 #include "third_party/base/ptr_util.h" 18 19 namespace { 20 21 constexpr FX_FILESIZE kLinearizedHeaderOffset = 9; 22 constexpr size_t kMaxInt = static_cast<size_t>(std::numeric_limits<int>::max()); 23 24 template <class T> 25 bool IsValidNumericDictionaryValue(const CPDF_Dictionary* pDict, 26 const char* key, 27 T min_value, 28 bool must_exist = true) { 29 if (!pDict->KeyExist(key)) 30 return !must_exist; 31 const CPDF_Number* pNum = ToNumber(pDict->GetObjectFor(key)); 32 if (!pNum || !pNum->IsInteger()) 33 return false; 34 const int raw_value = pNum->GetInteger(); 35 if (!pdfium::base::IsValueInRangeForNumericType<T>(raw_value)) 36 return false; 37 return static_cast<T>(raw_value) >= min_value; 38 } 39 40 bool IsLinearizedHeaderValid(const CPDF_LinearizedHeader* header, 41 FX_FILESIZE file_size) { 42 ASSERT(header); 43 return header->GetFileSize() == file_size && 44 static_cast<int>(header->GetFirstPageNo()) >= 0 && 45 header->GetFirstPageNo() < kMaxInt && 46 header->GetMainXRefTableFirstEntryOffset() < file_size && 47 header->GetPageCount() > 0 && 48 header->GetFirstPageEndOffset() < file_size && 49 header->GetLastXRefOffset() < file_size && 50 header->GetHintStart() < file_size; 51 } 52 53 } // namespace 54 55 // static 56 std::unique_ptr<CPDF_LinearizedHeader> CPDF_LinearizedHeader::Parse( 57 CPDF_SyntaxParser* parser) { 58 parser->SetPos(kLinearizedHeaderOffset); 59 60 const auto pDict = ToDictionary( 61 parser->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose)); 62 63 if (!pDict || !pDict->KeyExist("Linearized") || 64 !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.get(), "L", 1) || 65 !IsValidNumericDictionaryValue<uint32_t>(pDict.get(), "P", 0, false) || 66 !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.get(), "T", 1) || 67 !IsValidNumericDictionaryValue<uint32_t>(pDict.get(), "N", 0) || 68 !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.get(), "E", 1) || 69 !IsValidNumericDictionaryValue<uint32_t>(pDict.get(), "O", 1)) { 70 return nullptr; 71 } 72 // Move parser to the start of the xref table for the documents first page. 73 // (skpping endobj keyword) 74 if (parser->GetNextWord(nullptr) != "endobj") 75 return nullptr; 76 77 auto result = pdfium::WrapUnique( 78 new CPDF_LinearizedHeader(pDict.get(), parser->GetPos())); 79 80 if (!IsLinearizedHeaderValid(result.get(), 81 parser->GetFileAccess()->GetSize())) { 82 return nullptr; 83 } 84 return result; 85 } 86 87 CPDF_LinearizedHeader::CPDF_LinearizedHeader(const CPDF_Dictionary* pDict, 88 FX_FILESIZE szLastXRefOffset) 89 : m_szFileSize(pDict->GetIntegerFor("L")), 90 m_dwFirstPageNo(pDict->GetIntegerFor("P")), 91 m_szMainXRefTableFirstEntryOffset(pDict->GetIntegerFor("T")), 92 m_PageCount(pDict->GetIntegerFor("N")), 93 m_szFirstPageEndOffset(pDict->GetIntegerFor("E")), 94 m_FirstPageObjNum(pDict->GetIntegerFor("O")), 95 m_szLastXRefOffset(szLastXRefOffset) { 96 const CPDF_Array* pHintStreamRange = pDict->GetArrayFor("H"); 97 const size_t nHintStreamSize = 98 pHintStreamRange ? pHintStreamRange->GetCount() : 0; 99 if (nHintStreamSize == 2 || nHintStreamSize == 4) { 100 m_szHintStart = std::max(pHintStreamRange->GetIntegerAt(0), 0); 101 const FX_SAFE_UINT32 safe_hint_length = pHintStreamRange->GetIntegerAt(1); 102 if (safe_hint_length.IsValid()) 103 m_HintLength = safe_hint_length.ValueOrDie(); 104 } 105 } 106 107 CPDF_LinearizedHeader::~CPDF_LinearizedHeader() {} 108 109 bool CPDF_LinearizedHeader::HasHintTable() const { 110 return GetPageCount() > 1 && GetHintStart() > 0 && GetHintLength() > 0; 111 } 112