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