1 // Copyright 2017 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 #include "core/fpdfapi/parser/cpdf_read_validator.h" 6 7 #include <algorithm> 8 9 #include "core/fpdfapi/cpdf_modulemgr.h" 10 #include "third_party/base/logging.h" 11 12 namespace { 13 14 constexpr FX_FILESIZE kAlignBlockValue = CPDF_ModuleMgr::kFileBufSize; 15 16 FX_FILESIZE AlignDown(FX_FILESIZE offset) { 17 return offset > 0 ? (offset - offset % kAlignBlockValue) : 0; 18 } 19 20 FX_FILESIZE AlignUp(FX_FILESIZE offset) { 21 FX_SAFE_FILESIZE safe_result = AlignDown(offset); 22 safe_result += kAlignBlockValue; 23 if (safe_result.IsValid()) 24 return safe_result.ValueOrDie(); 25 return offset; 26 } 27 28 } // namespace 29 30 CPDF_ReadValidator::Session::Session(CPDF_ReadValidator* validator) 31 : validator_(validator) { 32 ASSERT(validator_); 33 saved_read_error_ = validator_->read_error_; 34 saved_has_unavailable_data_ = validator_->has_unavailable_data_; 35 validator_->ResetErrors(); 36 } 37 38 CPDF_ReadValidator::Session::~Session() { 39 validator_->read_error_ |= saved_read_error_; 40 validator_->has_unavailable_data_ |= saved_has_unavailable_data_; 41 } 42 43 CPDF_ReadValidator::CPDF_ReadValidator( 44 const RetainPtr<IFX_SeekableReadStream>& file_read, 45 CPDF_DataAvail::FileAvail* file_avail) 46 : file_read_(file_read), 47 file_avail_(file_avail), 48 read_error_(false), 49 has_unavailable_data_(false), 50 whole_file_already_available_(false), 51 file_size_(file_read->GetSize()) {} 52 53 CPDF_ReadValidator::~CPDF_ReadValidator() {} 54 55 void CPDF_ReadValidator::ResetErrors() { 56 read_error_ = false; 57 has_unavailable_data_ = false; 58 } 59 60 bool CPDF_ReadValidator::ReadBlock(void* buffer, 61 FX_FILESIZE offset, 62 size_t size) { 63 FX_SAFE_FILESIZE end_offset = offset; 64 end_offset += size; 65 if (!end_offset.IsValid() || end_offset.ValueOrDie() > file_size_) 66 return false; 67 68 if (!IsDataRangeAvailable(offset, size)) { 69 ScheduleDownload(offset, size); 70 return false; 71 } 72 73 if (file_read_->ReadBlock(buffer, offset, size)) 74 return true; 75 76 read_error_ = true; 77 ScheduleDownload(offset, size); 78 return false; 79 } 80 81 FX_FILESIZE CPDF_ReadValidator::GetSize() { 82 return file_size_; 83 } 84 85 void CPDF_ReadValidator::ScheduleDownload(FX_FILESIZE offset, size_t size) { 86 has_unavailable_data_ = true; 87 if (!hints_ || size == 0) 88 return; 89 90 const FX_FILESIZE start_segment_offset = AlignDown(offset); 91 FX_SAFE_FILESIZE end_segment_offset = offset; 92 end_segment_offset += size; 93 if (!end_segment_offset.IsValid()) { 94 NOTREACHED(); 95 return; 96 } 97 end_segment_offset = 98 std::min(file_size_, AlignUp(end_segment_offset.ValueOrDie())); 99 100 FX_SAFE_SIZE_T segment_size = end_segment_offset; 101 segment_size -= start_segment_offset; 102 if (!segment_size.IsValid()) { 103 NOTREACHED(); 104 return; 105 } 106 hints_->AddSegment(start_segment_offset, segment_size.ValueOrDie()); 107 } 108 109 bool CPDF_ReadValidator::IsDataRangeAvailable(FX_FILESIZE offset, 110 size_t size) const { 111 return whole_file_already_available_ || !file_avail_ || 112 file_avail_->IsDataAvail(offset, size); 113 } 114 115 bool CPDF_ReadValidator::IsWholeFileAvailable() { 116 const FX_SAFE_SIZE_T safe_size = file_size_; 117 whole_file_already_available_ = 118 whole_file_already_available_ || 119 (safe_size.IsValid() ? IsDataRangeAvailable(0, safe_size.ValueOrDie()) 120 : false); 121 122 return whole_file_already_available_; 123 } 124 125 bool CPDF_ReadValidator::CheckDataRangeAndRequestIfUnavailable( 126 FX_FILESIZE offset, 127 size_t size) { 128 if (offset > file_size_) 129 return true; 130 131 FX_SAFE_FILESIZE end_segment_offset = offset; 132 end_segment_offset += size; 133 // Increase checked range to allow CPDF_SyntaxParser read whole buffer. 134 end_segment_offset += CPDF_ModuleMgr::kFileBufSize; 135 if (!end_segment_offset.IsValid()) { 136 NOTREACHED(); 137 return false; 138 } 139 end_segment_offset = std::min( 140 file_size_, static_cast<FX_FILESIZE>(end_segment_offset.ValueOrDie())); 141 FX_SAFE_SIZE_T segment_size = end_segment_offset; 142 segment_size -= offset; 143 if (!segment_size.IsValid()) { 144 NOTREACHED(); 145 return false; 146 } 147 148 if (IsDataRangeAvailable(offset, segment_size.ValueOrDie())) 149 return true; 150 151 ScheduleDownload(offset, segment_size.ValueOrDie()); 152 return false; 153 } 154 155 bool CPDF_ReadValidator::CheckWholeFileAndRequestIfUnavailable() { 156 if (IsWholeFileAvailable()) 157 return true; 158 159 const FX_SAFE_SIZE_T safe_size = file_size_; 160 if (safe_size.IsValid()) 161 ScheduleDownload(0, safe_size.ValueOrDie()); 162 163 return false; 164 } 165