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
AlignDown(FX_FILESIZE offset)16 FX_FILESIZE AlignDown(FX_FILESIZE offset) {
17 return offset > 0 ? (offset - offset % kAlignBlockValue) : 0;
18 }
19
AlignUp(FX_FILESIZE offset)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
Session(CPDF_ReadValidator * validator)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
~Session()38 CPDF_ReadValidator::Session::~Session() {
39 validator_->read_error_ |= saved_read_error_;
40 validator_->has_unavailable_data_ |= saved_has_unavailable_data_;
41 }
42
CPDF_ReadValidator(const RetainPtr<IFX_SeekableReadStream> & file_read,CPDF_DataAvail::FileAvail * file_avail)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
~CPDF_ReadValidator()53 CPDF_ReadValidator::~CPDF_ReadValidator() {}
54
ResetErrors()55 void CPDF_ReadValidator::ResetErrors() {
56 read_error_ = false;
57 has_unavailable_data_ = false;
58 }
59
ReadBlock(void * buffer,FX_FILESIZE offset,size_t size)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
GetSize()81 FX_FILESIZE CPDF_ReadValidator::GetSize() {
82 return file_size_;
83 }
84
ScheduleDownload(FX_FILESIZE offset,size_t size)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
IsDataRangeAvailable(FX_FILESIZE offset,size_t size) const109 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
IsWholeFileAvailable()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
CheckDataRangeAndRequestIfUnavailable(FX_FILESIZE offset,size_t size)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
CheckWholeFileAndRequestIfUnavailable()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