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