• 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 <algorithm>
8 #include <utility>
9 
10 #include "core/fpdfapi/parser/cpdf_stream.h"
11 #include "core/fxcrt/fx_safe_types.h"
12 #include "third_party/base/notreached.h"
13 
14 namespace {
15 
16 constexpr FX_FILESIZE kAlignBlockValue = CPDF_Stream::kFileBufSize;
17 
AlignDown(FX_FILESIZE offset)18 FX_FILESIZE AlignDown(FX_FILESIZE offset) {
19   return offset > 0 ? (offset - offset % kAlignBlockValue) : 0;
20 }
21 
AlignUp(FX_FILESIZE offset)22 FX_FILESIZE AlignUp(FX_FILESIZE offset) {
23   FX_SAFE_FILESIZE safe_result = AlignDown(offset);
24   safe_result += kAlignBlockValue;
25   return safe_result.ValueOrDefault(offset);
26 }
27 
28 }  // namespace
29 
ScopedSession(RetainPtr<CPDF_ReadValidator> validator)30 CPDF_ReadValidator::ScopedSession::ScopedSession(
31     RetainPtr<CPDF_ReadValidator> validator)
32     : validator_(std::move(validator)),
33       saved_read_error_(validator_->read_error_),
34       saved_has_unavailable_data_(validator_->has_unavailable_data_) {
35   validator_->ResetErrors();
36 }
37 
~ScopedSession()38 CPDF_ReadValidator::ScopedSession::~ScopedSession() {
39   validator_->read_error_ |= saved_read_error_;
40   validator_->has_unavailable_data_ |= saved_has_unavailable_data_;
41 }
42 
CPDF_ReadValidator(RetainPtr<IFX_SeekableReadStream> file_read,CPDF_DataAvail::FileAvail * file_avail)43 CPDF_ReadValidator::CPDF_ReadValidator(
44     RetainPtr<IFX_SeekableReadStream> file_read,
45     CPDF_DataAvail::FileAvail* file_avail)
46     : file_read_(std::move(file_read)),
47       file_avail_(file_avail),
48       file_size_(file_read_->GetSize()) {}
49 
50 CPDF_ReadValidator::~CPDF_ReadValidator() = default;
51 
ResetErrors()52 void CPDF_ReadValidator::ResetErrors() {
53   read_error_ = false;
54   has_unavailable_data_ = false;
55 }
56 
ReadBlockAtOffset(pdfium::span<uint8_t> buffer,FX_FILESIZE offset)57 bool CPDF_ReadValidator::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
58                                            FX_FILESIZE offset) {
59   if (offset < 0) {
60     NOTREACHED();
61     return false;
62   }
63 
64   FX_SAFE_FILESIZE end_offset = offset;
65   end_offset += buffer.size();
66   if (!end_offset.IsValid() || end_offset.ValueOrDie() > file_size_)
67     return false;
68 
69   if (!IsDataRangeAvailable(offset, buffer.size())) {
70     ScheduleDownload(offset, buffer.size());
71     return false;
72   }
73 
74   if (file_read_->ReadBlockAtOffset(buffer, offset))
75     return true;
76 
77   read_error_ = true;
78   ScheduleDownload(offset, buffer.size());
79   return false;
80 }
81 
GetSize()82 FX_FILESIZE CPDF_ReadValidator::GetSize() {
83   return file_size_;
84 }
85 
ScheduleDownload(FX_FILESIZE offset,size_t size)86 void CPDF_ReadValidator::ScheduleDownload(FX_FILESIZE offset, size_t size) {
87   has_unavailable_data_ = true;
88   if (!hints_ || size == 0)
89     return;
90 
91   const FX_FILESIZE start_segment_offset = AlignDown(offset);
92   FX_SAFE_FILESIZE end_segment_offset = offset;
93   end_segment_offset += size;
94   if (!end_segment_offset.IsValid()) {
95     NOTREACHED();
96     return;
97   }
98   end_segment_offset =
99       std::min(file_size_, AlignUp(end_segment_offset.ValueOrDie()));
100 
101   FX_SAFE_SIZE_T segment_size = end_segment_offset;
102   segment_size -= start_segment_offset;
103   if (!segment_size.IsValid()) {
104     NOTREACHED();
105     return;
106   }
107   hints_->AddSegment(start_segment_offset, segment_size.ValueOrDie());
108 }
109 
IsDataRangeAvailable(FX_FILESIZE offset,size_t size) const110 bool CPDF_ReadValidator::IsDataRangeAvailable(FX_FILESIZE offset,
111                                               size_t size) const {
112   return whole_file_already_available_ || !file_avail_ ||
113          file_avail_->IsDataAvail(offset, size);
114 }
115 
IsWholeFileAvailable()116 bool CPDF_ReadValidator::IsWholeFileAvailable() {
117   const FX_SAFE_SIZE_T safe_size = file_size_;
118   whole_file_already_available_ =
119       whole_file_already_available_ ||
120       (safe_size.IsValid() && IsDataRangeAvailable(0, safe_size.ValueOrDie()));
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_Stream::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