1 // Copyright 2014 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxcodec/tiff/tiff_decoder.h"
8
9 #include <memory>
10 #include <utility>
11
12 #include "core/fxcodec/cfx_codec_memory.h"
13 #include "core/fxcodec/fx_codec.h"
14 #include "core/fxcodec/fx_codec_def.h"
15 #include "core/fxcrt/check.h"
16 #include "core/fxcrt/compiler_specific.h"
17 #include "core/fxcrt/fx_memcpy_wrappers.h"
18 #include "core/fxcrt/fx_safe_types.h"
19 #include "core/fxcrt/fx_stream.h"
20 #include "core/fxcrt/fx_system.h"
21 #include "core/fxcrt/notreached.h"
22 #include "core/fxcrt/numerics/safe_conversions.h"
23 #include "core/fxcrt/retain_ptr.h"
24 #include "core/fxcrt/span.h"
25 #include "core/fxcrt/span_util.h"
26 #include "core/fxge/dib/cfx_dibitmap.h"
27 #include "core/fxge/dib/fx_dib.h"
28
29 extern "C" {
30 #if defined(USE_SYSTEM_LIBTIFF)
31 #include <tiffio.h>
32 #else
33 #include "third_party/libtiff/tiffio.h"
34 #endif
35 } // extern C
36
37 namespace {
38
39 // For use with std::unique_ptr<TIFF>.
40 struct TiffDeleter {
operator ()__anoneb7d6ba50111::TiffDeleter41 inline void operator()(TIFF* context) { TIFFClose(context); }
42 };
43
44 // For use with std::unique_ptr<TIFFOpenOptions>.
45 struct TIFFOpenOptionsDeleter {
operator ()__anoneb7d6ba50111::TIFFOpenOptionsDeleter46 inline void operator()(TIFFOpenOptions* options) {
47 TIFFOpenOptionsFree(options);
48 }
49 };
50
51 } // namespace
52
53 class CTiffContext final : public ProgressiveDecoderIface::Context {
54 public:
55 CTiffContext() = default;
56 ~CTiffContext() override = default;
57
58 bool InitDecoder(const RetainPtr<IFX_SeekableReadStream>& file_ptr);
59 bool LoadFrameInfo(int32_t frame,
60 int32_t* width,
61 int32_t* height,
62 int32_t* comps,
63 int32_t* bpc,
64 CFX_DIBAttribute* pAttribute);
65 bool Decode(RetainPtr<CFX_DIBitmap> bitmap);
66
io_in() const67 RetainPtr<IFX_SeekableReadStream> io_in() const { return m_io_in; }
offset() const68 uint32_t offset() const { return m_offset; }
set_offset(uint32_t offset)69 void set_offset(uint32_t offset) { m_offset = offset; }
70
71 private:
72 RetainPtr<IFX_SeekableReadStream> m_io_in;
73 uint32_t m_offset = 0;
74 std::unique_ptr<TIFF, TiffDeleter> m_tif_ctx;
75 };
76
_TIFFcalloc(tmsize_t nmemb,tmsize_t siz)77 void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz) {
78 return FXMEM_DefaultCalloc(nmemb, siz);
79 }
80
_TIFFmalloc(tmsize_t size)81 void* _TIFFmalloc(tmsize_t size) {
82 return FXMEM_DefaultAlloc(size);
83 }
84
_TIFFfree(void * ptr)85 void _TIFFfree(void* ptr) {
86 if (ptr)
87 FXMEM_DefaultFree(ptr);
88 }
89
_TIFFrealloc(void * ptr,tmsize_t size)90 void* _TIFFrealloc(void* ptr, tmsize_t size) {
91 return FXMEM_DefaultRealloc(ptr, size);
92 }
93
_TIFFmemset(void * ptr,int val,tmsize_t size)94 void _TIFFmemset(void* ptr, int val, tmsize_t size) {
95 UNSAFE_TODO(FXSYS_memset(ptr, val, static_cast<size_t>(size)));
96 }
97
_TIFFmemcpy(void * des,const void * src,tmsize_t size)98 void _TIFFmemcpy(void* des, const void* src, tmsize_t size) {
99 UNSAFE_TODO(FXSYS_memcpy(des, src, static_cast<size_t>(size)));
100 }
101
_TIFFmemcmp(const void * ptr1,const void * ptr2,tmsize_t size)102 int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) {
103 return UNSAFE_TODO(memcmp(ptr1, ptr2, static_cast<size_t>(size)));
104 }
105
106 namespace {
107
tiff_read(thandle_t context,tdata_t buf,tsize_t length)108 tsize_t tiff_read(thandle_t context, tdata_t buf, tsize_t length) {
109 CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
110 FX_SAFE_UINT32 increment = pTiffContext->offset();
111 increment += length;
112 if (!increment.IsValid())
113 return 0;
114
115 FX_FILESIZE offset = pTiffContext->offset();
116 // SAFETY: required from caller.
117 if (!pTiffContext->io_in()->ReadBlockAtOffset(
118 UNSAFE_BUFFERS(pdfium::make_span(static_cast<uint8_t*>(buf),
119 static_cast<size_t>(length))),
120 offset)) {
121 return 0;
122 }
123 pTiffContext->set_offset(increment.ValueOrDie());
124 if (offset + length > pTiffContext->io_in()->GetSize()) {
125 return pdfium::checked_cast<tsize_t>(pTiffContext->io_in()->GetSize() -
126 offset);
127 }
128 return length;
129 }
130
tiff_write(thandle_t context,tdata_t buf,tsize_t length)131 tsize_t tiff_write(thandle_t context, tdata_t buf, tsize_t length) {
132 NOTREACHED_NORETURN();
133 }
134
tiff_seek(thandle_t context,toff_t offset,int whence)135 toff_t tiff_seek(thandle_t context, toff_t offset, int whence) {
136 CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
137 FX_SAFE_FILESIZE safe_offset = offset;
138 if (!safe_offset.IsValid())
139 return static_cast<toff_t>(-1);
140 FX_FILESIZE file_offset = safe_offset.ValueOrDie();
141
142 switch (whence) {
143 case 0: {
144 if (file_offset > pTiffContext->io_in()->GetSize())
145 return static_cast<toff_t>(-1);
146 pTiffContext->set_offset(pdfium::checked_cast<uint32_t>(file_offset));
147 return pTiffContext->offset();
148 }
149 case 1: {
150 FX_SAFE_UINT32 new_increment = pTiffContext->offset();
151 new_increment += file_offset;
152 if (!new_increment.IsValid())
153 return static_cast<toff_t>(-1);
154 pTiffContext->set_offset(new_increment.ValueOrDie());
155 return pTiffContext->offset();
156 }
157 case 2: {
158 if (pTiffContext->io_in()->GetSize() < file_offset)
159 return static_cast<toff_t>(-1);
160 pTiffContext->set_offset(pdfium::checked_cast<uint32_t>(
161 pTiffContext->io_in()->GetSize() - file_offset));
162 return pTiffContext->offset();
163 }
164 default:
165 return static_cast<toff_t>(-1);
166 }
167 }
168
tiff_close(thandle_t context)169 int tiff_close(thandle_t context) {
170 return 0;
171 }
172
tiff_get_size(thandle_t context)173 toff_t tiff_get_size(thandle_t context) {
174 CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
175 return static_cast<toff_t>(pTiffContext->io_in()->GetSize());
176 }
177
tiff_map(thandle_t context,tdata_t *,toff_t *)178 int tiff_map(thandle_t context, tdata_t*, toff_t*) {
179 return 0;
180 }
181
tiff_unmap(thandle_t context,tdata_t,toff_t)182 void tiff_unmap(thandle_t context, tdata_t, toff_t) {}
183
184 } // namespace
185
InitDecoder(const RetainPtr<IFX_SeekableReadStream> & file_ptr)186 bool CTiffContext::InitDecoder(
187 const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
188 // Limit set to make fuzzers happy. If this causes problems in the real world,
189 // then adjust as needed.
190 constexpr tmsize_t kMaxTiffAllocBytes = 1536 * 1024 * 1024; // 1.5 GB
191 std::unique_ptr<TIFFOpenOptions, TIFFOpenOptionsDeleter> options(
192 TIFFOpenOptionsAlloc());
193 CHECK(options);
194 TIFFOpenOptionsSetMaxCumulatedMemAlloc(options.get(), kMaxTiffAllocBytes);
195
196 m_io_in = file_ptr;
197 m_tif_ctx.reset(TIFFClientOpenExt(
198 /*name=*/"Tiff Image", /*mode=*/"r", /*clientdata=*/this, tiff_read,
199 tiff_write, tiff_seek, tiff_close, tiff_get_size, tiff_map, tiff_unmap,
200 options.get()));
201 return !!m_tif_ctx;
202 }
203
LoadFrameInfo(int32_t frame,int32_t * width,int32_t * height,int32_t * comps,int32_t * bpc,CFX_DIBAttribute * pAttribute)204 bool CTiffContext::LoadFrameInfo(int32_t frame,
205 int32_t* width,
206 int32_t* height,
207 int32_t* comps,
208 int32_t* bpc,
209 CFX_DIBAttribute* pAttribute) {
210 if (!TIFFSetDirectory(m_tif_ctx.get(), (uint16_t)frame))
211 return false;
212
213 uint32_t tif_width = 0;
214 uint32_t tif_height = 0;
215 uint16_t tif_comps = 0;
216 uint16_t tif_bpc = 0;
217 uint32_t tif_rps = 0;
218 TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &tif_width);
219 TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &tif_height);
220 TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &tif_comps);
221 TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &tif_bpc);
222 TIFFGetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, &tif_rps);
223
224 uint16_t tif_resunit = 0;
225 if (TIFFGetField(m_tif_ctx.get(), TIFFTAG_RESOLUTIONUNIT, &tif_resunit)) {
226 pAttribute->m_wDPIUnit =
227 static_cast<CFX_DIBAttribute::ResUnit>(tif_resunit - 1);
228 } else {
229 pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitInch;
230 }
231
232 float tif_xdpi = 0.0f;
233 TIFFGetField(m_tif_ctx.get(), TIFFTAG_XRESOLUTION, &tif_xdpi);
234 if (tif_xdpi)
235 pAttribute->m_nXDPI = static_cast<int32_t>(tif_xdpi + 0.5f);
236
237 float tif_ydpi = 0.0f;
238 TIFFGetField(m_tif_ctx.get(), TIFFTAG_YRESOLUTION, &tif_ydpi);
239 if (tif_ydpi)
240 pAttribute->m_nYDPI = static_cast<int32_t>(tif_ydpi + 0.5f);
241
242 FX_SAFE_INT32 checked_width = tif_width;
243 FX_SAFE_INT32 checked_height = tif_height;
244 if (!checked_width.IsValid() || !checked_height.IsValid())
245 return false;
246
247 *width = checked_width.ValueOrDie();
248 *height = checked_height.ValueOrDie();
249 *comps = tif_comps;
250 *bpc = tif_bpc;
251 if (tif_rps > tif_height) {
252 tif_rps = tif_height;
253 TIFFSetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, tif_rps);
254 }
255 return true;
256 }
257
Decode(RetainPtr<CFX_DIBitmap> bitmap)258 bool CTiffContext::Decode(RetainPtr<CFX_DIBitmap> bitmap) {
259 // TODO(crbug.com/355630556): Consider adding support for
260 // `FXDIB_Format::kBgraPremul`
261 CHECK_EQ(bitmap->GetFormat(), FXDIB_Format::kBgra);
262 const uint32_t img_width = bitmap->GetWidth();
263 const uint32_t img_height = bitmap->GetHeight();
264 uint32_t width = 0;
265 uint32_t height = 0;
266 TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &width);
267 TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &height);
268 if (img_width != width || img_height != height) {
269 return false;
270 }
271
272 uint16_t rotation = ORIENTATION_TOPLEFT;
273 TIFFGetField(m_tif_ctx.get(), TIFFTAG_ORIENTATION, &rotation);
274 uint32_t* data =
275 fxcrt::reinterpret_span<uint32_t>(bitmap->GetWritableBuffer()).data();
276 if (!TIFFReadRGBAImageOriented(m_tif_ctx.get(), img_width, img_height, data,
277 rotation, 1)) {
278 return false;
279 }
280
281 for (uint32_t row = 0; row < img_height; row++) {
282 auto row_span = bitmap->GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row);
283 for (auto& pixel : row_span) {
284 std::swap(pixel.blue, pixel.red);
285 }
286 }
287 return true;
288 }
289
290 namespace fxcodec {
291
292 // static
CreateDecoder(const RetainPtr<IFX_SeekableReadStream> & file_ptr)293 std::unique_ptr<ProgressiveDecoderIface::Context> TiffDecoder::CreateDecoder(
294 const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
295 auto pDecoder = std::make_unique<CTiffContext>();
296 if (!pDecoder->InitDecoder(file_ptr))
297 return nullptr;
298
299 return pDecoder;
300 }
301
302 // static
LoadFrameInfo(ProgressiveDecoderIface::Context * pContext,int32_t frame,int32_t * width,int32_t * height,int32_t * comps,int32_t * bpc,CFX_DIBAttribute * pAttribute)303 bool TiffDecoder::LoadFrameInfo(ProgressiveDecoderIface::Context* pContext,
304 int32_t frame,
305 int32_t* width,
306 int32_t* height,
307 int32_t* comps,
308 int32_t* bpc,
309 CFX_DIBAttribute* pAttribute) {
310 DCHECK(pAttribute);
311
312 auto* ctx = static_cast<CTiffContext*>(pContext);
313 return ctx->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute);
314 }
315
316 // static
Decode(ProgressiveDecoderIface::Context * pContext,RetainPtr<CFX_DIBitmap> bitmap)317 bool TiffDecoder::Decode(ProgressiveDecoderIface::Context* pContext,
318 RetainPtr<CFX_DIBitmap> bitmap) {
319 auto* ctx = static_cast<CTiffContext*>(pContext);
320 return ctx->Decode(std::move(bitmap));
321 }
322
323 } // namespace fxcodec
324