• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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