• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // JPEG decode.
11 
12 #include "./jpegdec.h"
13 
14 #ifdef HAVE_CONFIG_H
15 #include "webp/config.h"
16 #endif
17 
18 #include <stdio.h>
19 
20 #ifdef WEBP_HAVE_JPEG
21 #include <jpeglib.h>
22 #include <jerror.h>
23 #include <setjmp.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "webp/encode.h"
28 #include "./imageio_util.h"
29 #include "./metadata.h"
30 
31 // -----------------------------------------------------------------------------
32 // Metadata processing
33 
34 #ifndef JPEG_APP1
35 # define JPEG_APP1 (JPEG_APP0 + 1)
36 #endif
37 #ifndef JPEG_APP2
38 # define JPEG_APP2 (JPEG_APP0 + 2)
39 #endif
40 
41 typedef struct {
42   const uint8_t* data;
43   size_t data_length;
44   int seq;  // this segment's sequence number [1, 255] for use in reassembly.
45 } ICCPSegment;
46 
SaveMetadataMarkers(j_decompress_ptr dinfo)47 static void SaveMetadataMarkers(j_decompress_ptr dinfo) {
48   const unsigned int max_marker_length = 0xffff;
49   jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length);  // Exif/XMP
50   jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length);  // ICC profile
51 }
52 
CompareICCPSegments(const void * a,const void * b)53 static int CompareICCPSegments(const void* a, const void* b) {
54   const ICCPSegment* s1 = (const ICCPSegment*)a;
55   const ICCPSegment* s2 = (const ICCPSegment*)b;
56   return s1->seq - s2->seq;
57 }
58 
59 // Extract ICC profile segments from the marker list in 'dinfo', reassembling
60 // and storing them in 'iccp'.
61 // Returns true on success and false for memory errors and corrupt profiles.
StoreICCP(j_decompress_ptr dinfo,MetadataPayload * const iccp)62 static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
63   // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
64   static const char kICCPSignature[] = "ICC_PROFILE";
65   static const size_t kICCPSignatureLength = 12;  // signature includes '\0'
66   static const size_t kICCPSkipLength = 14;  // signature + seq & count
67   int expected_count = 0;
68   int actual_count = 0;
69   int seq_max = 0;
70   size_t total_size = 0;
71   ICCPSegment iccp_segments[255];
72   jpeg_saved_marker_ptr marker;
73 
74   memset(iccp_segments, 0, sizeof(iccp_segments));
75   for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
76     if (marker->marker == JPEG_APP2 &&
77         marker->data_length > kICCPSkipLength &&
78         !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
79       // ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
80       const int seq = marker->data[kICCPSignatureLength];
81       const int count = marker->data[kICCPSignatureLength + 1];
82       const size_t segment_size = marker->data_length - kICCPSkipLength;
83       ICCPSegment* segment;
84 
85       if (segment_size == 0 || count == 0 || seq == 0) {
86         fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
87                         " cannot be 0!\n",
88                 (int)segment_size, seq, count);
89         return 0;
90       }
91 
92       if (expected_count == 0) {
93         expected_count = count;
94       } else if (expected_count != count) {
95         fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n",
96                 expected_count, count);
97         return 0;
98       }
99 
100       segment = iccp_segments + seq - 1;
101       if (segment->data_length != 0) {
102         fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
103         return 0;
104       }
105 
106       segment->data = marker->data + kICCPSkipLength;
107       segment->data_length = segment_size;
108       segment->seq = seq;
109       total_size += segment_size;
110       if (seq > seq_max) seq_max = seq;
111       ++actual_count;
112     }
113   }
114 
115   if (actual_count == 0) return 1;
116   if (seq_max != actual_count) {
117     fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n",
118             actual_count, seq_max);
119     return 0;
120   }
121   if (expected_count != actual_count) {
122     fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n",
123             actual_count, expected_count);
124     return 0;
125   }
126 
127   // The segments may appear out of order in the file, sort them based on
128   // sequence number before assembling the payload.
129   qsort(iccp_segments, actual_count, sizeof(*iccp_segments),
130         CompareICCPSegments);
131 
132   iccp->bytes = (uint8_t*)malloc(total_size);
133   if (iccp->bytes == NULL) return 0;
134   iccp->size = total_size;
135 
136   {
137     int i;
138     size_t offset = 0;
139     for (i = 0; i < seq_max; ++i) {
140       memcpy(iccp->bytes + offset,
141              iccp_segments[i].data, iccp_segments[i].data_length);
142       offset += iccp_segments[i].data_length;
143     }
144   }
145   return 1;
146 }
147 
148 // Returns true on success and false for memory errors and corrupt profiles.
149 // The caller must use MetadataFree() on 'metadata' in all cases.
ExtractMetadataFromJPEG(j_decompress_ptr dinfo,Metadata * const metadata)150 static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
151                                    Metadata* const metadata) {
152   static const struct {
153     int marker;
154     const char* signature;
155     size_t signature_length;
156     size_t storage_offset;
157   } kJPEGMetadataMap[] = {
158     // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
159     { JPEG_APP1, "Exif\0",                        6, METADATA_OFFSET(exif) },
160     // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
161     // TODO(jzern) Add support for 'ExtendedXMP'
162     { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
163     { 0, NULL, 0, 0 },
164   };
165   jpeg_saved_marker_ptr marker;
166   // Treat ICC profiles separately as they may be segmented and out of order.
167   if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
168 
169   for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
170     int i;
171     for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) {
172       if (marker->marker == kJPEGMetadataMap[i].marker &&
173           marker->data_length > kJPEGMetadataMap[i].signature_length &&
174           !memcmp(marker->data, kJPEGMetadataMap[i].signature,
175                   kJPEGMetadataMap[i].signature_length)) {
176         MetadataPayload* const payload =
177             (MetadataPayload*)((uint8_t*)metadata +
178                                kJPEGMetadataMap[i].storage_offset);
179 
180         if (payload->bytes == NULL) {
181           const char* marker_data = (const char*)marker->data +
182                                     kJPEGMetadataMap[i].signature_length;
183           const size_t marker_data_length =
184               marker->data_length - kJPEGMetadataMap[i].signature_length;
185           if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
186         } else {
187           fprintf(stderr, "Ignoring additional '%s' marker\n",
188                   kJPEGMetadataMap[i].signature);
189         }
190       }
191     }
192   }
193   return 1;
194 }
195 
196 #undef JPEG_APP1
197 #undef JPEG_APP2
198 
199 // -----------------------------------------------------------------------------
200 // JPEG decoding
201 
202 struct my_error_mgr {
203   struct jpeg_error_mgr pub;
204   jmp_buf setjmp_buffer;
205 };
206 
my_error_exit(j_common_ptr dinfo)207 static void my_error_exit(j_common_ptr dinfo) {
208   struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
209   // The following code is disabled in fuzzing mode because:
210   // - the logs can be flooded due to invalid JPEG files
211   // - msg_code is wrongfully seen as uninitialized by msan when the libjpeg
212   //   dependency is not built with sanitizers enabled
213 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
214   const int msg_code = myerr->pub.msg_code;
215   fprintf(stderr, "libjpeg error: ");
216   dinfo->err->output_message(dinfo);
217   if (msg_code == JERR_INPUT_EOF || msg_code == JERR_FILE_READ) {
218     fprintf(stderr, "`jpegtran -copy all` MAY be able to process this file.\n");
219   }
220 #endif
221   longjmp(myerr->setjmp_buffer, 1);
222 }
223 
224 typedef struct {
225   struct jpeg_source_mgr pub;
226   const uint8_t* data;
227   size_t data_size;
228 } JPEGReadContext;
229 
ContextInit(j_decompress_ptr cinfo)230 static void ContextInit(j_decompress_ptr cinfo) {
231   JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
232   ctx->pub.next_input_byte = ctx->data;
233   ctx->pub.bytes_in_buffer = ctx->data_size;
234 }
235 
ContextFill(j_decompress_ptr cinfo)236 static boolean ContextFill(j_decompress_ptr cinfo) {
237   // we shouldn't get here.
238   ERREXIT(cinfo, JERR_FILE_READ);
239   return FALSE;
240 }
241 
ContextSkip(j_decompress_ptr cinfo,long jump_size)242 static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
243   JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
244   size_t jump = (size_t)jump_size;
245   if (jump > ctx->pub.bytes_in_buffer) {  // Don't overflow the buffer.
246     jump = ctx->pub.bytes_in_buffer;
247   }
248   ctx->pub.bytes_in_buffer -= jump;
249   ctx->pub.next_input_byte += jump;
250 }
251 
ContextTerm(j_decompress_ptr cinfo)252 static void ContextTerm(j_decompress_ptr cinfo) {
253   (void)cinfo;
254 }
255 
ContextSetup(volatile struct jpeg_decompress_struct * const cinfo,JPEGReadContext * const ctx)256 static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
257                          JPEGReadContext* const ctx) {
258   cinfo->src = (struct jpeg_source_mgr*)ctx;
259   ctx->pub.init_source = ContextInit;
260   ctx->pub.fill_input_buffer = ContextFill;
261   ctx->pub.skip_input_data = ContextSkip;
262   ctx->pub.resync_to_restart = jpeg_resync_to_restart;
263   ctx->pub.term_source = ContextTerm;
264   ctx->pub.bytes_in_buffer = 0;
265   ctx->pub.next_input_byte = NULL;
266 }
267 
ReadJPEG(const uint8_t * const data,size_t data_size,WebPPicture * const pic,int keep_alpha,Metadata * const metadata)268 int ReadJPEG(const uint8_t* const data, size_t data_size,
269              WebPPicture* const pic, int keep_alpha,
270              Metadata* const metadata) {
271   volatile int ok = 0;
272   int width, height;
273   int64_t stride;
274   volatile struct jpeg_decompress_struct dinfo;
275   struct my_error_mgr jerr;
276   uint8_t* volatile rgb = NULL;
277   JSAMPROW buffer[1];
278   JPEGReadContext ctx;
279 
280   if (data == NULL || data_size == 0 || pic == NULL) return 0;
281 
282   (void)keep_alpha;
283   memset(&ctx, 0, sizeof(ctx));
284   ctx.data = data;
285   ctx.data_size = data_size;
286 
287   memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo));   // for setjmp safety
288   dinfo.err = jpeg_std_error(&jerr.pub);
289   jerr.pub.error_exit = my_error_exit;
290 
291   if (setjmp(jerr.setjmp_buffer)) {
292  Error:
293     MetadataFree(metadata);
294     jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
295     goto End;
296   }
297 
298   jpeg_create_decompress((j_decompress_ptr)&dinfo);
299   ContextSetup(&dinfo, &ctx);
300   if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
301   jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
302 
303   dinfo.out_color_space = JCS_RGB;
304   dinfo.do_fancy_upsampling = TRUE;
305 
306   jpeg_start_decompress((j_decompress_ptr)&dinfo);
307 
308   if (dinfo.output_components != 3) {
309     goto Error;
310   }
311 
312   width = dinfo.output_width;
313   height = dinfo.output_height;
314   stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
315 
316   if (stride != (int)stride ||
317       !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
318     goto Error;
319   }
320 
321   rgb = (uint8_t*)malloc((size_t)stride * height);
322   if (rgb == NULL) {
323     goto Error;
324   }
325   buffer[0] = (JSAMPLE*)rgb;
326 
327   while (dinfo.output_scanline < dinfo.output_height) {
328     if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) {
329       goto Error;
330     }
331     buffer[0] += stride;
332   }
333 
334   if (metadata != NULL) {
335     ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata);
336     if (!ok) {
337       fprintf(stderr, "Error extracting JPEG metadata!\n");
338       goto Error;
339     }
340   }
341 
342   jpeg_finish_decompress((j_decompress_ptr)&dinfo);
343   jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
344 
345   // WebP conversion.
346   pic->width = width;
347   pic->height = height;
348   ok = WebPPictureImportRGB(pic, rgb, (int)stride);
349   if (!ok) {
350     pic->width = 0;   // WebPPictureImportRGB() barely touches 'pic' on failure.
351     pic->height = 0;  // Just reset dimensions but keep any 'custom_ptr' etc.
352     MetadataFree(metadata);  // In case the caller forgets to free it on error.
353   }
354 
355  End:
356   free(rgb);
357   return ok;
358 }
359 #else  // !WEBP_HAVE_JPEG
ReadJPEG(const uint8_t * const data,size_t data_size,struct WebPPicture * const pic,int keep_alpha,struct Metadata * const metadata)360 int ReadJPEG(const uint8_t* const data, size_t data_size,
361              struct WebPPicture* const pic, int keep_alpha,
362              struct Metadata* const metadata) {
363   (void)data;
364   (void)data_size;
365   (void)pic;
366   (void)keep_alpha;
367   (void)metadata;
368   fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
369           "development package before building.\n");
370   return 0;
371 }
372 #endif  // WEBP_HAVE_JPEG
373 
374 // -----------------------------------------------------------------------------
375