• 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 <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