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