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 <limits>
8 #include <utility>
9 #include <vector>
10
11 #include "core/fxcrt/cfx_readonlymemorystream.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "testing/invalid_seekable_read_stream.h"
14
15 namespace {
16
17 constexpr uint32_t kTestDataSize = 64 * 1024 - 467;
18
MakeRange(uint32_t start,uint32_t end)19 std::pair<FX_FILESIZE, FX_FILESIZE> MakeRange(uint32_t start, uint32_t end) {
20 return std::pair<FX_FILESIZE, FX_FILESIZE>(start, end);
21 }
22
23 class MockFileAvail final : public CPDF_DataAvail::FileAvail {
24 public:
MockFileAvail()25 MockFileAvail() : available_range_(0, 0) {}
~MockFileAvail()26 ~MockFileAvail() override {}
27
IsDataAvail(FX_FILESIZE offset,size_t size)28 bool IsDataAvail(FX_FILESIZE offset, size_t size) override {
29 return available_range_.first <= offset &&
30 available_range_.second >= static_cast<FX_FILESIZE>(offset + size);
31 }
32
SetAvailableRange(const std::pair<FX_FILESIZE,FX_FILESIZE> & range)33 void SetAvailableRange(const std::pair<FX_FILESIZE, FX_FILESIZE>& range) {
34 available_range_ = range;
35 }
36
SetAvailableRange(uint32_t start,uint32_t end)37 void SetAvailableRange(uint32_t start, uint32_t end) {
38 SetAvailableRange(MakeRange(start, end));
39 }
40
41 private:
42 std::pair<FX_FILESIZE, FX_FILESIZE> available_range_;
43 };
44
45 class MockDownloadHints final : public CPDF_DataAvail::DownloadHints {
46 public:
MockDownloadHints()47 MockDownloadHints() : last_requested_range_(0, 0) {}
~MockDownloadHints()48 ~MockDownloadHints() override {}
49
AddSegment(FX_FILESIZE offset,size_t size)50 void AddSegment(FX_FILESIZE offset, size_t size) override {
51 last_requested_range_.first = offset;
52 last_requested_range_.second = offset + size;
53 }
54
GetLastRequstedRange() const55 const std::pair<FX_FILESIZE, FX_FILESIZE>& GetLastRequstedRange() const {
56 return last_requested_range_;
57 }
58
Reset()59 void Reset() { last_requested_range_ = MakeRange(0, 0); }
60
61 private:
62 std::pair<FX_FILESIZE, FX_FILESIZE> last_requested_range_;
63 };
64
65 } // namespace
66
TEST(CPDF_ReadValidatorTest,UnavailableData)67 TEST(CPDF_ReadValidatorTest, UnavailableData) {
68 std::vector<uint8_t> test_data(kTestDataSize);
69 auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
70 MockFileAvail file_avail;
71 auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
72
73 std::vector<uint8_t> read_buffer(100);
74 EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
75 read_buffer.size()));
76
77 EXPECT_FALSE(validator->read_error());
78 EXPECT_TRUE(validator->has_unavailable_data());
79
80 validator->ResetErrors();
81
82 file_avail.SetAvailableRange(5000, 5000 + read_buffer.size());
83
84 EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
85 read_buffer.size()));
86 EXPECT_FALSE(validator->read_error());
87 EXPECT_FALSE(validator->has_unavailable_data());
88 }
89
TEST(CPDF_ReadValidatorTest,UnavailableDataWithHints)90 TEST(CPDF_ReadValidatorTest, UnavailableDataWithHints) {
91 std::vector<uint8_t> test_data(kTestDataSize);
92 auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
93 MockFileAvail file_avail;
94 auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
95
96 MockDownloadHints hints;
97 validator->SetDownloadHints(&hints);
98
99 std::vector<uint8_t> read_buffer(100);
100
101 EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
102 read_buffer.size()));
103 EXPECT_FALSE(validator->read_error());
104 EXPECT_TRUE(validator->has_unavailable_data());
105
106 // Requested range should be enlarged and aligned.
107 EXPECT_EQ(MakeRange(4608, 5120), hints.GetLastRequstedRange());
108
109 file_avail.SetAvailableRange(hints.GetLastRequstedRange());
110 hints.Reset();
111
112 validator->ResetErrors();
113 EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
114 read_buffer.size()));
115 // No new request on already available data.
116 EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange());
117 EXPECT_FALSE(validator->read_error());
118 EXPECT_FALSE(validator->has_unavailable_data());
119
120 validator->ResetErrors();
121 // Try read unavailable data at file end.
122 EXPECT_FALSE(validator->ReadBlockAtOffset(
123 read_buffer.data(), validator->GetSize() - read_buffer.size(),
124 read_buffer.size()));
125 // Should not enlarge request at file end.
126 EXPECT_EQ(validator->GetSize(), hints.GetLastRequstedRange().second);
127 EXPECT_FALSE(validator->read_error());
128 EXPECT_TRUE(validator->has_unavailable_data());
129
130 validator->SetDownloadHints(nullptr);
131 }
132
TEST(CPDF_ReadValidatorTest,ReadError)133 TEST(CPDF_ReadValidatorTest, ReadError) {
134 auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
135 auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, nullptr);
136
137 static const uint32_t kBufferSize = 3 * 1000;
138 std::vector<uint8_t> buffer(kBufferSize);
139
140 EXPECT_FALSE(validator->ReadBlockAtOffset(buffer.data(), 5000, 100));
141 EXPECT_TRUE(validator->read_error());
142 EXPECT_TRUE(validator->has_unavailable_data());
143 }
144
TEST(CPDF_ReadValidatorTest,IntOverflow)145 TEST(CPDF_ReadValidatorTest, IntOverflow) {
146 std::vector<uint8_t> test_data(kTestDataSize);
147 auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
148 MockFileAvail file_avail;
149 auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
150
151 std::vector<uint8_t> read_buffer(100);
152
153 // If we have int overflow, this is equal reading after file end. This is not
154 // read_error, and in this case we have not unavailable data. It is just error
155 // of input params.
156 EXPECT_FALSE(validator->ReadBlockAtOffset(
157 read_buffer.data(), std::numeric_limits<FX_FILESIZE>::max() - 1,
158 read_buffer.size()));
159 EXPECT_FALSE(validator->read_error());
160 EXPECT_FALSE(validator->has_unavailable_data());
161 }
162
TEST(CPDF_ReadValidatorTest,Session)163 TEST(CPDF_ReadValidatorTest, Session) {
164 std::vector<uint8_t> test_data(kTestDataSize);
165
166 auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
167 MockFileAvail file_avail;
168 MockDownloadHints hints;
169 auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
170 validator->SetDownloadHints(&hints);
171
172 const CPDF_ReadValidator::Session read_session(validator);
173 ASSERT_FALSE(validator->has_read_problems());
174
175 // Data is unavailable
176 validator->ReadBlockAtOffset(test_data.data(), 0, 100);
177
178 EXPECT_TRUE(validator->has_read_problems());
179 EXPECT_TRUE(validator->has_unavailable_data());
180 EXPECT_FALSE(validator->read_error());
181
182 {
183 const CPDF_ReadValidator::Session read_subsession(validator);
184 // The read problems should be hidden.
185 EXPECT_FALSE(validator->has_read_problems());
186
187 file_avail.SetAvailableRange(0, 100);
188 // Read fail.
189 validator->ReadBlockAtOffset(test_data.data(), 0, 100);
190 EXPECT_TRUE(validator->has_read_problems());
191 EXPECT_TRUE(validator->has_unavailable_data());
192 EXPECT_TRUE(validator->read_error());
193 }
194
195 // The problems should be merged
196 EXPECT_TRUE(validator->has_read_problems());
197 EXPECT_TRUE(validator->has_unavailable_data());
198 EXPECT_TRUE(validator->read_error());
199 }
200
TEST(CPDF_ReadValidatorTest,SessionReset)201 TEST(CPDF_ReadValidatorTest, SessionReset) {
202 std::vector<uint8_t> test_data(kTestDataSize);
203
204 auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
205 MockFileAvail file_avail;
206 MockDownloadHints hints;
207 auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
208 validator->SetDownloadHints(&hints);
209
210 const CPDF_ReadValidator::Session read_session(validator);
211 ASSERT_FALSE(validator->has_read_problems());
212
213 // Data is unavailable
214 validator->ReadBlockAtOffset(test_data.data(), 0, 100);
215
216 EXPECT_TRUE(validator->has_read_problems());
217 EXPECT_TRUE(validator->has_unavailable_data());
218 EXPECT_FALSE(validator->read_error());
219
220 {
221 const CPDF_ReadValidator::Session read_subsession(validator);
222 // The read problems should be hidden.
223 EXPECT_FALSE(validator->has_read_problems());
224
225 file_avail.SetAvailableRange(0, 100);
226 // Read fail.
227 validator->ReadBlockAtOffset(test_data.data(), 0, 100);
228 EXPECT_TRUE(validator->has_read_problems());
229 EXPECT_TRUE(validator->has_unavailable_data());
230 EXPECT_TRUE(validator->read_error());
231
232 // Reset session.
233 validator->ResetErrors();
234 EXPECT_FALSE(validator->has_read_problems());
235 }
236
237 // The problems should be restored.
238 EXPECT_TRUE(validator->has_read_problems());
239 EXPECT_TRUE(validator->has_unavailable_data());
240 EXPECT_FALSE(validator->read_error());
241 }
242
TEST(CPDF_ReadValidatorTest,CheckDataRangeAndRequestIfUnavailable)243 TEST(CPDF_ReadValidatorTest, CheckDataRangeAndRequestIfUnavailable) {
244 std::vector<uint8_t> test_data(kTestDataSize);
245 auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
246 MockFileAvail file_avail;
247 auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
248
249 MockDownloadHints hints;
250 validator->SetDownloadHints(&hints);
251
252 EXPECT_FALSE(validator->CheckDataRangeAndRequestIfUnavailable(5000, 100));
253 EXPECT_FALSE(validator->read_error());
254 EXPECT_TRUE(validator->has_unavailable_data());
255
256 // Requested range should be enlarged and aligned.
257 EXPECT_EQ(MakeRange(4608, 5632), hints.GetLastRequstedRange());
258
259 file_avail.SetAvailableRange(hints.GetLastRequstedRange());
260 hints.Reset();
261
262 validator->ResetErrors();
263 EXPECT_TRUE(validator->CheckDataRangeAndRequestIfUnavailable(5000, 100));
264 // No new request on already available data.
265 EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange());
266 EXPECT_FALSE(validator->read_error());
267 EXPECT_FALSE(validator->has_unavailable_data());
268
269 std::vector<uint8_t> read_buffer(100);
270 EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
271 read_buffer.size()));
272 // No new request on already available data.
273 EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange());
274 EXPECT_FALSE(validator->read_error());
275 EXPECT_FALSE(validator->has_unavailable_data());
276
277 validator->ResetErrors();
278 // Try request unavailable data at file end.
279 EXPECT_FALSE(validator->CheckDataRangeAndRequestIfUnavailable(
280 validator->GetSize() - 100, 100));
281
282 // Should not enlarge request at file end.
283 EXPECT_EQ(validator->GetSize(), hints.GetLastRequstedRange().second);
284 EXPECT_FALSE(validator->read_error());
285 EXPECT_TRUE(validator->has_unavailable_data());
286
287 validator->ResetErrors();
288 // Offset > file size should yield |true| and not cause a fetch.
289 EXPECT_TRUE(
290 validator->CheckDataRangeAndRequestIfUnavailable(kTestDataSize + 1, 1));
291 // No new request on already available data.
292 EXPECT_FALSE(validator->read_error());
293 EXPECT_FALSE(validator->has_unavailable_data());
294
295 validator->SetDownloadHints(nullptr);
296 }
297