Coverage Report

Created: 2026-02-14 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libraw/src/decoders/unpack_thumb.cpp
Line
Count
Source
1
/* -*- C++ -*-
2
 * Copyright 2019-2025 LibRaw LLC (info@libraw.org)
3
 *
4
 LibRaw is free software; you can redistribute it and/or modify
5
 it under the terms of the one of two licenses as you choose:
6
7
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
8
   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
9
10
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
11
   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
12
13
 */
14
15
#include "../../internal/libraw_cxx_defs.h"
16
17
#ifndef NO_JPEG
18
struct jpegErrorManager
19
{
20
  struct jpeg_error_mgr pub;
21
  jmp_buf setjmp_buffer;
22
};
23
24
static void jpegErrorExit(j_common_ptr cinfo)
25
{
26
  jpegErrorManager *myerr = (jpegErrorManager *)cinfo->err;
27
  longjmp(myerr->setjmp_buffer, 1);
28
}
29
#endif
30
31
int LibRaw::unpack_thumb_ex(int idx)
32
0
{
33
0
  if (idx < 0 || idx >= imgdata.thumbs_list.thumbcount || idx >= LIBRAW_THUMBNAIL_MAXCOUNT)
34
0
    return LIBRAW_REQUEST_FOR_NONEXISTENT_THUMBNAIL;
35
36
  // Set from thumb-list
37
0
    libraw_internal_data.internal_data.toffset = imgdata.thumbs_list.thumblist[idx].toffset;
38
0
    imgdata.thumbnail.tlength = imgdata.thumbs_list.thumblist[idx].tlength;
39
0
    libraw_internal_data.unpacker_data.thumb_format = imgdata.thumbs_list.thumblist[idx].tformat; 
40
0
    imgdata.thumbnail.twidth = imgdata.thumbs_list.thumblist[idx].twidth;
41
0
    imgdata.thumbnail.theight = imgdata.thumbs_list.thumblist[idx].theight;
42
0
  libraw_internal_data.unpacker_data.thumb_misc = imgdata.thumbs_list.thumblist[idx].tmisc;
43
0
  int rc = unpack_thumb();
44
0
    imgdata.progress_flags &= ~LIBRAW_PROGRESS_THUMB_LOAD;
45
46
0
  return rc;
47
0
}
48
49
50
int LibRaw::unpack_thumb(void)
51
0
{
52
0
  CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
53
0
  CHECK_ORDER_BIT(LIBRAW_PROGRESS_THUMB_LOAD);
54
55
0
#define THUMB_SIZE_CHECKT(A) \
56
0
  do { \
57
0
    if (INT64(A) > 1024LL * 1024LL * LIBRAW_MAX_THUMBNAIL_MB) return LIBRAW_UNSUPPORTED_THUMBNAIL; \
58
0
    if (INT64(A) > 0 &&  INT64(A) < 64LL)        return LIBRAW_NO_THUMBNAIL; \
59
0
  } while (0)
60
61
0
#define THUMB_SIZE_CHECKTNZ(A) \
62
0
  do { \
63
0
    if (INT64(A) > 1024LL * 1024LL * LIBRAW_MAX_THUMBNAIL_MB) return LIBRAW_UNSUPPORTED_THUMBNAIL; \
64
0
    if (INT64(A) < 64LL)        return LIBRAW_NO_THUMBNAIL; \
65
0
  } while (0)
66
67
68
0
#define THUMB_SIZE_CHECKWH(W,H) \
69
0
  do { \
70
0
    if (INT64(W)*INT64(H) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB) return LIBRAW_UNSUPPORTED_THUMBNAIL; \
71
0
    if (INT64(W)*INT64(H) < 64ULL)        return LIBRAW_NO_THUMBNAIL; \
72
0
  } while (0)
73
74
0
#define Tformat libraw_internal_data.unpacker_data.thumb_format
75
76
0
  try
77
0
  {
78
0
    if (!libraw_internal_data.internal_data.input)
79
0
      return LIBRAW_INPUT_CLOSED;
80
81
0
    int t_colors = libraw_internal_data.unpacker_data.thumb_misc >> 5 & 7;
82
0
    int t_bytesps = (libraw_internal_data.unpacker_data.thumb_misc & 31) / 8;
83
84
0
    if (!ID.toffset && !(imgdata.thumbnail.tlength > 0 &&
85
0
                         load_raw == &LibRaw::broadcom_load_raw)  // RPi
86
#ifdef USE_6BY9RPI
87
        && !(imgdata.thumbnail.tlength > 0 && libraw_internal_data.unpacker_data.load_flags & 0x4000
88
            && (load_raw == &LibRaw::rpi_load_raw8 || load_raw == &LibRaw::nokia_load_raw ||
89
           load_raw == &LibRaw::rpi_load_raw12 || load_raw == &LibRaw::rpi_load_raw14))
90
#endif
91
0
    )
92
0
    {
93
0
      return LIBRAW_NO_THUMBNAIL;
94
0
    }
95
0
  else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_DNG_YCBCR)
96
0
  {
97
0
    try
98
0
    {
99
0
        dng_ycbcr_thumb_loader();
100
0
        T.tformat = LIBRAW_THUMBNAIL_BITMAP;
101
0
        T.tcolors = 3;
102
0
        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
103
0
    }
104
0
    catch (...)
105
0
    {
106
0
        return LIBRAW_NO_THUMBNAIL;
107
0
    }
108
0
      return 0;
109
110
0
  }
111
0
    else if ((Tformat >= LIBRAW_INTERNAL_THUMBNAIL_KODAK_THUMB)
112
0
    && ((Tformat <= LIBRAW_INTERNAL_THUMBNAIL_KODAK_RGB)))
113
0
    {
114
0
    try {
115
0
          kodak_thumb_loader();
116
0
          T.tformat = LIBRAW_THUMBNAIL_BITMAP;
117
0
          SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
118
0
    }
119
0
    catch (...)
120
0
    {
121
0
          return LIBRAW_NO_THUMBNAIL;
122
0
    }
123
0
      return 0;
124
0
    }
125
0
    else
126
0
    {
127
#ifdef USE_X3FTOOLS
128
  if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_X3F)
129
      {
130
        INT64 tsize = x3f_thumb_size();
131
        if (tsize < 2048 || INT64(ID.toffset) + tsize < 1)
132
          return LIBRAW_NO_THUMBNAIL;
133
134
        if (INT64(ID.toffset) + tsize > ID.input->size() + THUMB_READ_BEYOND)
135
          return LIBRAW_NO_THUMBNAIL;
136
137
        THUMB_SIZE_CHECKT(tsize);
138
      }
139
#else
140
0
  if (0) {}
141
0
#endif
142
0
      else
143
0
      {
144
0
        if (INT64(ID.toffset) + INT64(T.tlength) < 1)
145
0
          return LIBRAW_NO_THUMBNAIL;
146
147
0
        if (INT64(ID.toffset) + INT64(T.tlength) >
148
0
            ID.input->size() + THUMB_READ_BEYOND)
149
0
          return LIBRAW_NO_THUMBNAIL;
150
0
      }
151
152
0
      ID.input->seek(ID.toffset, SEEK_SET);
153
0
      if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_JPEG || Tformat == LIBRAW_INTERNAL_THUMBNAIL_JPEGXL)
154
0
      {
155
0
        THUMB_SIZE_CHECKTNZ(T.tlength);
156
0
        if (T.thumb)
157
0
          free(T.thumb);
158
#ifdef LIBRAW_CALLOC_RAWSTORE
159
        T.thumb = (char *)calloc(T.tlength,1);
160
#else
161
0
        T.thumb = (char *)malloc(T.tlength);
162
0
#endif
163
0
    if(!T.thumb)
164
0
          return LIBRAW_NO_THUMBNAIL;
165
0
        ID.input->read(T.thumb, 1, T.tlength);
166
0
    unsigned char *tthumb = (unsigned char *)T.thumb;
167
0
    if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_JPEGXL)
168
0
    {
169
0
          T.tformat = LIBRAW_THUMBNAIL_JPEGXL;
170
0
          SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
171
0
          return 0;
172
0
    }
173
0
    if (load_raw == &LibRaw::crxLoadRaw && T.tlength > 0xE0)
174
0
    {
175
      // Check if it is canon H.265 preview:  CISZ at bytes 4-6, CISZ prefix is 000n
176
0
      if (tthumb[0] == 0 && tthumb[1] == 0 && tthumb[2] == 0 && !memcmp(tthumb + 4, "CISZ", 4))
177
0
      {
178
0
        T.tformat = LIBRAW_THUMBNAIL_H265;
179
0
        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
180
0
        return 0;
181
0
      }
182
0
    }
183
0
        tthumb[0] = 0xff;
184
0
        tthumb[1] = 0xd8;
185
0
#ifdef NO_JPEG
186
0
        T.tcolors = 3;
187
#else
188
        {
189
          jpegErrorManager jerr;
190
          struct jpeg_decompress_struct cinfo;
191
          cinfo.err = jpeg_std_error(&jerr.pub);
192
          jerr.pub.error_exit = jpegErrorExit;
193
          if (setjmp(jerr.setjmp_buffer))
194
          {
195
          err2:
196
            // Error in original JPEG thumb, read it again because
197
            // original bytes 0-1 was damaged above
198
            jpeg_destroy_decompress(&cinfo);
199
            T.tcolors = 3;
200
            T.tformat = LIBRAW_THUMBNAIL_UNKNOWN;
201
            ID.input->seek(ID.toffset, SEEK_SET);
202
            ID.input->read(T.thumb, 1, T.tlength);
203
            SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
204
            return 0;
205
          }
206
          jpeg_create_decompress(&cinfo);
207
          jpeg_mem_src(&cinfo, (unsigned char *)T.thumb, T.tlength);
208
          int rc = jpeg_read_header(&cinfo, TRUE);
209
          if (rc != 1)
210
            goto err2;
211
          T.tcolors = (cinfo.num_components > 0 && cinfo.num_components <= 3)
212
                          ? cinfo.num_components
213
                          : 3;
214
          jpeg_destroy_decompress(&cinfo);
215
        }
216
#endif
217
0
        T.tformat = LIBRAW_THUMBNAIL_JPEG;
218
0
        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
219
0
        return 0;
220
0
      }
221
0
      else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_LAYER)
222
0
      {
223
0
        int colors = libraw_internal_data.unpacker_data.thumb_misc >> 5 & 7;
224
0
        if (colors != 1 && colors != 3)
225
0
          return LIBRAW_UNSUPPORTED_THUMBNAIL;
226
227
0
        THUMB_SIZE_CHECKWH(T.twidth, T.theight);
228
229
0
        int tlength = T.twidth * T.theight;
230
0
        if (T.thumb)
231
0
          free(T.thumb);
232
0
        T.thumb = (char *)calloc(colors, tlength);
233
0
    if(!T.thumb)
234
0
      return LIBRAW_NO_THUMBNAIL;
235
0
        unsigned char *tbuf = (unsigned char *)calloc(colors, tlength);
236
0
    if (!tbuf)
237
0
    {
238
0
      free(T.thumb);
239
0
      T.thumb = 0;
240
0
            return LIBRAW_NO_THUMBNAIL;
241
0
    }
242
        // Avoid OOB of tbuf, should use tlength
243
0
        ID.input->read(tbuf, colors, tlength);
244
0
        if (libraw_internal_data.unpacker_data.thumb_misc >> 8 &&
245
0
            colors == 3) // GRB order
246
0
          for (int i = 0; i < tlength; i++)
247
0
          {
248
0
            T.thumb[i * 3] = tbuf[i + tlength];
249
0
            T.thumb[i * 3 + 1] = tbuf[i];
250
0
            T.thumb[i * 3 + 2] = tbuf[i + 2 * tlength];
251
0
          }
252
0
        else if (colors == 3) // RGB or 1-channel
253
0
          for (int i = 0; i < tlength; i++)
254
0
          {
255
0
            T.thumb[i * 3] = tbuf[i];
256
0
            T.thumb[i * 3 + 1] = tbuf[i + tlength];
257
0
            T.thumb[i * 3 + 2] = tbuf[i + 2 * tlength];
258
0
          }
259
0
        else if (colors == 1)
260
0
        {
261
0
          free(T.thumb);
262
0
          T.thumb = (char *)tbuf;
263
0
          tbuf = 0;
264
0
        }
265
0
        if (tbuf)
266
0
          free(tbuf);
267
0
        T.tcolors = colors;
268
0
        T.tlength = colors * tlength;
269
0
        T.tformat = LIBRAW_THUMBNAIL_BITMAP;
270
0
        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
271
0
        return 0;
272
0
      }
273
0
      else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_ROLLEI)
274
0
      {
275
0
        int i;
276
0
        THUMB_SIZE_CHECKWH(T.twidth, T.theight);
277
0
        int tlength = T.twidth * T.theight;
278
0
        if (T.thumb)
279
0
          free(T.thumb);
280
0
        T.tcolors = 3;
281
0
        T.thumb = (char *)calloc(T.tcolors, tlength);
282
0
        if (!T.thumb)
283
0
          return LIBRAW_NO_THUMBNAIL;
284
0
        unsigned short *tbuf = (unsigned short *)calloc(2, tlength);
285
0
        if (!tbuf)
286
0
        {
287
0
          free(T.thumb);
288
0
          T.thumb = 0;
289
0
          return LIBRAW_NO_THUMBNAIL;
290
0
        }
291
0
    try {
292
0
        read_shorts(tbuf, tlength);
293
0
          for (i = 0; i < tlength; i++)
294
0
          {
295
0
            T.thumb[i * 3] = (tbuf[i] << 3) & 0xff;
296
0
            T.thumb[i * 3 + 1] = (tbuf[i] >> 5 << 2) & 0xff;
297
0
            T.thumb[i * 3 + 2] = (tbuf[i] >> 11 << 3) & 0xff;
298
0
          }
299
0
          free(tbuf);
300
0
          T.tlength = T.tcolors * tlength;
301
0
          T.tformat = LIBRAW_THUMBNAIL_BITMAP;
302
0
          SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
303
0
    }
304
0
    catch (...)
305
0
    {
306
0
      free(tbuf);
307
0
      return LIBRAW_NO_THUMBNAIL;
308
0
    }
309
0
        return 0;
310
0
      }
311
0
      else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_PPM)
312
0
      {
313
0
        if (t_bytesps > 1)
314
0
          return LIBRAW_NO_THUMBNAIL;  // 8-bit thumb, but parsed for more
315
                                             // bits
316
0
        THUMB_SIZE_CHECKWH(T.twidth, T.theight);
317
0
        int t_length = T.twidth * T.theight * t_colors;
318
319
0
        if (T.tlength &&
320
0
            (int)T.tlength < t_length) // try to find tiff ifd with needed offset
321
0
        {
322
0
          int pifd = find_ifd_by_offset(libraw_internal_data.internal_data.toffset);
323
0
          if (pifd >= 0 && tiff_ifd[pifd].strip_offsets_count &&
324
0
              tiff_ifd[pifd].strip_byte_counts_count)
325
0
          {
326
            // We found it, calculate final size
327
0
            INT64 total_size = 0;
328
0
            for (int i = 0; i < tiff_ifd[pifd].strip_byte_counts_count 
329
0
        && i < tiff_ifd[pifd].strip_offsets_count; i++)
330
0
              total_size += tiff_ifd[pifd].strip_byte_counts[i];
331
0
            if (total_size != (unsigned)t_length) // recalculate colors
332
0
            {
333
0
              if (total_size == T.twidth * T.tlength * 3)
334
0
                T.tcolors = 3;
335
0
              else if (total_size == T.twidth * T.tlength)
336
0
                T.tcolors = 1;
337
0
            }
338
0
            T.tlength = unsigned(total_size);
339
0
            THUMB_SIZE_CHECKTNZ(T.tlength);
340
0
            if (T.thumb)
341
0
              free(T.thumb);
342
#ifdef LIBRAW_CALLOC_RAWSTORE
343
            T.thumb = (char *)calloc(T.tlength,1);
344
#else
345
0
            T.thumb = (char *)malloc(T.tlength);
346
0
#endif
347
0
            if (!T.thumb)
348
0
              return LIBRAW_NO_THUMBNAIL;
349
350
0
            char *dest = T.thumb;
351
0
            INT64 pos = ID.input->tell();
352
0
            INT64 remain = T.tlength;
353
354
0
            for (int i = 0; i < tiff_ifd[pifd].strip_byte_counts_count &&
355
0
                            i < tiff_ifd[pifd].strip_offsets_count;
356
0
                 i++)
357
0
            {
358
0
              int sz = tiff_ifd[pifd].strip_byte_counts[i];
359
0
              INT64 off = tiff_ifd[pifd].strip_offsets[i];
360
0
              if (off >= 0 && off + sz <= ID.input->size() && sz > 0 && INT64(sz) <= remain)
361
0
              {
362
0
                ID.input->seek(off, SEEK_SET);
363
0
                ID.input->read(dest, sz, 1);
364
0
                remain -= sz;
365
0
                dest += sz;
366
0
              }
367
0
            }
368
0
            ID.input->seek(pos, SEEK_SET);
369
0
            T.tformat = LIBRAW_THUMBNAIL_BITMAP;
370
0
            SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
371
0
            return 0;
372
0
          }
373
0
        }
374
375
0
        if (!T.tlength)
376
0
          T.tlength = t_length;
377
0
        if (T.thumb)
378
0
          free(T.thumb);
379
380
0
        THUMB_SIZE_CHECKTNZ(T.tlength);
381
382
#ifdef LIBRAW_CALLOC_RAWSTORE
383
        T.thumb = (char *)calloc(T.tlength,1);
384
#else
385
0
        T.thumb = (char *)malloc(T.tlength);
386
0
#endif
387
0
        if (!T.thumb)
388
0
          return LIBRAW_NO_THUMBNAIL;
389
0
        if (!T.tcolors)
390
0
          T.tcolors = t_colors;
391
392
0
        ID.input->read(T.thumb, 1, T.tlength);
393
394
0
        T.tformat = LIBRAW_THUMBNAIL_BITMAP;
395
0
        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
396
0
        return 0;
397
0
      }
398
0
      else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_PPM16)
399
0
      {
400
0
        if (t_bytesps > 2)
401
0
      return LIBRAW_NO_THUMBNAIL; // 16-bit thumb, but parsed for
402
                                             // more bits
403
0
        int o_bps = (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_USE_PPM16_THUMBS) ? 2 : 1;
404
0
        int o_length = T.twidth * T.theight * t_colors * o_bps;
405
0
        int i_length = T.twidth * T.theight * t_colors * 2;
406
407
0
    THUMB_SIZE_CHECKTNZ(o_length);
408
0
        THUMB_SIZE_CHECKTNZ(i_length);
409
410
0
        ushort *t_thumb = (ushort *)calloc(i_length, 1);
411
0
    if (!t_thumb)
412
0
      return LIBRAW_NO_THUMBNAIL;;
413
0
        ID.input->read(t_thumb, 1, i_length);
414
0
        if ((libraw_internal_data.unpacker_data.order == 0x4949) ==
415
0
            (ntohs(0x1234) == 0x1234))
416
0
          libraw_swab(t_thumb, i_length);
417
418
0
        if (T.thumb)
419
0
          free(T.thumb);
420
0
        if ((imgdata.rawparams.options & LIBRAW_RAWOPTIONS_USE_PPM16_THUMBS))
421
0
        {
422
0
          T.thumb = (char *)t_thumb;
423
0
          T.tformat = LIBRAW_THUMBNAIL_BITMAP16;
424
0
          T.tlength = i_length;
425
0
        }
426
0
        else
427
0
        {
428
#ifdef LIBRAW_CALLOC_RAWSTORE
429
          T.thumb = (char *)calloc(o_length,1);
430
#else
431
0
          T.thumb = (char *)malloc(o_length);
432
0
#endif
433
0
      if (!T.thumb)
434
0
      {
435
0
        free(t_thumb);
436
0
        return LIBRAW_NO_THUMBNAIL;
437
0
      }
438
0
          for (int i = 0; i < o_length; i++)
439
0
            T.thumb[i] = t_thumb[i] >> 8;
440
0
          free(t_thumb);
441
0
          T.tformat = LIBRAW_THUMBNAIL_BITMAP;
442
0
          T.tlength = o_length;
443
0
        }
444
0
        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
445
0
        return 0;
446
0
      }
447
#ifdef USE_X3FTOOLS
448
    else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_X3F)
449
      {
450
        x3f_thumb_loader(); // errors already catched in this call
451
        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
452
        return 0;
453
      }
454
#endif
455
0
      else
456
0
      {
457
0
        return LIBRAW_UNSUPPORTED_THUMBNAIL;
458
0
      }
459
0
    }
460
    // last resort
461
0
    return LIBRAW_UNSUPPORTED_THUMBNAIL; /* warned as unreachable*/
462
0
  }
463
0
  catch (const LibRaw_exceptions& err)
464
0
  {
465
0
    EXCEPTION_HANDLER(err);
466
0
  }
467
0
}