• Home
  • History
  • Annotate
  • 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 //  WebP container demux.
11 //
12 
13 #ifdef HAVE_CONFIG_H
14 #include "../webp/config.h"
15 #endif
16 
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "../utils/utils.h"
22 #include "../webp/decode.h"     // WebPGetFeatures
23 #include "../webp/demux.h"
24 #include "../webp/format_constants.h"
25 
26 #define DMUX_MAJ_VERSION 0
27 #define DMUX_MIN_VERSION 2
28 #define DMUX_REV_VERSION 2
29 
30 typedef struct {
31   size_t start_;        // start location of the data
32   size_t end_;          // end location
33   size_t riff_end_;     // riff chunk end location, can be > end_.
34   size_t buf_size_;     // size of the buffer
35   const uint8_t* buf_;
36 } MemBuffer;
37 
38 typedef struct {
39   size_t offset_;
40   size_t size_;
41 } ChunkData;
42 
43 typedef struct Frame {
44   int x_offset_, y_offset_;
45   int width_, height_;
46   int has_alpha_;
47   int duration_;
48   WebPMuxAnimDispose dispose_method_;
49   WebPMuxAnimBlend blend_method_;
50   int is_fragment_;  // this is a frame fragment (and not a full frame).
51   int frame_num_;  // the referent frame number for use in assembling fragments.
52   int complete_;   // img_components_ contains a full image.
53   ChunkData img_components_[2];  // 0=VP8{,L} 1=ALPH
54   struct Frame* next_;
55 } Frame;
56 
57 typedef struct Chunk {
58   ChunkData data_;
59   struct Chunk* next_;
60 } Chunk;
61 
62 struct WebPDemuxer {
63   MemBuffer mem_;
64   WebPDemuxState state_;
65   int is_ext_format_;
66   uint32_t feature_flags_;
67   int canvas_width_, canvas_height_;
68   int loop_count_;
69   uint32_t bgcolor_;
70   int num_frames_;
71   Frame* frames_;
72   Frame** frames_tail_;
73   Chunk* chunks_;  // non-image chunks
74   Chunk** chunks_tail_;
75 };
76 
77 typedef enum {
78   PARSE_OK,
79   PARSE_NEED_MORE_DATA,
80   PARSE_ERROR
81 } ParseStatus;
82 
83 typedef struct ChunkParser {
84   uint8_t id[4];
85   ParseStatus (*parse)(WebPDemuxer* const dmux);
86   int (*valid)(const WebPDemuxer* const dmux);
87 } ChunkParser;
88 
89 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
90 static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
91 static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
92 static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
93 
94 static const ChunkParser kMasterChunks[] = {
95   { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
96   { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
97   { { 'V', 'P', '8', 'X' }, ParseVP8X,        IsValidExtendedFormat },
98   { { '0', '0', '0', '0' }, NULL,             NULL },
99 };
100 
101 //------------------------------------------------------------------------------
102 
WebPGetDemuxVersion(void)103 int WebPGetDemuxVersion(void) {
104   return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
105 }
106 
107 // -----------------------------------------------------------------------------
108 // MemBuffer
109 
RemapMemBuffer(MemBuffer * const mem,const uint8_t * data,size_t size)110 static int RemapMemBuffer(MemBuffer* const mem,
111                           const uint8_t* data, size_t size) {
112   if (size < mem->buf_size_) return 0;  // can't remap to a shorter buffer!
113 
114   mem->buf_ = data;
115   mem->end_ = mem->buf_size_ = size;
116   return 1;
117 }
118 
InitMemBuffer(MemBuffer * const mem,const uint8_t * data,size_t size)119 static int InitMemBuffer(MemBuffer* const mem,
120                          const uint8_t* data, size_t size) {
121   memset(mem, 0, sizeof(*mem));
122   return RemapMemBuffer(mem, data, size);
123 }
124 
125 // Return the remaining data size available in 'mem'.
MemDataSize(const MemBuffer * const mem)126 static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
127   return (mem->end_ - mem->start_);
128 }
129 
130 // Return true if 'size' exceeds the end of the RIFF chunk.
SizeIsInvalid(const MemBuffer * const mem,size_t size)131 static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
132   return (size > mem->riff_end_ - mem->start_);
133 }
134 
Skip(MemBuffer * const mem,size_t size)135 static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
136   mem->start_ += size;
137 }
138 
Rewind(MemBuffer * const mem,size_t size)139 static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
140   mem->start_ -= size;
141 }
142 
GetBuffer(MemBuffer * const mem)143 static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
144   return mem->buf_ + mem->start_;
145 }
146 
147 // Read from 'mem' and skip the read bytes.
ReadByte(MemBuffer * const mem)148 static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
149   const uint8_t byte = mem->buf_[mem->start_];
150   Skip(mem, 1);
151   return byte;
152 }
153 
ReadLE16s(MemBuffer * const mem)154 static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
155   const uint8_t* const data = mem->buf_ + mem->start_;
156   const int val = GetLE16(data);
157   Skip(mem, 2);
158   return val;
159 }
160 
ReadLE24s(MemBuffer * const mem)161 static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
162   const uint8_t* const data = mem->buf_ + mem->start_;
163   const int val = GetLE24(data);
164   Skip(mem, 3);
165   return val;
166 }
167 
ReadLE32(MemBuffer * const mem)168 static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
169   const uint8_t* const data = mem->buf_ + mem->start_;
170   const uint32_t val = GetLE32(data);
171   Skip(mem, 4);
172   return val;
173 }
174 
175 // -----------------------------------------------------------------------------
176 // Secondary chunk parsing
177 
AddChunk(WebPDemuxer * const dmux,Chunk * const chunk)178 static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
179   *dmux->chunks_tail_ = chunk;
180   chunk->next_ = NULL;
181   dmux->chunks_tail_ = &chunk->next_;
182 }
183 
184 // Add a frame to the end of the list, ensuring the last frame is complete.
185 // Returns true on success, false otherwise.
AddFrame(WebPDemuxer * const dmux,Frame * const frame)186 static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
187   const Frame* const last_frame = *dmux->frames_tail_;
188   if (last_frame != NULL && !last_frame->complete_) return 0;
189 
190   *dmux->frames_tail_ = frame;
191   frame->next_ = NULL;
192   dmux->frames_tail_ = &frame->next_;
193   return 1;
194 }
195 
196 // Store image bearing chunks to 'frame'.
StoreFrame(int frame_num,uint32_t min_size,MemBuffer * const mem,Frame * const frame)197 static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
198                               MemBuffer* const mem, Frame* const frame) {
199   int alpha_chunks = 0;
200   int image_chunks = 0;
201   int done = (MemDataSize(mem) < min_size);
202   ParseStatus status = PARSE_OK;
203 
204   if (done) return PARSE_NEED_MORE_DATA;
205 
206   do {
207     const size_t chunk_start_offset = mem->start_;
208     const uint32_t fourcc = ReadLE32(mem);
209     const uint32_t payload_size = ReadLE32(mem);
210     const uint32_t payload_size_padded = payload_size + (payload_size & 1);
211     const size_t payload_available = (payload_size_padded > MemDataSize(mem))
212                                    ? MemDataSize(mem) : payload_size_padded;
213     const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
214 
215     if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
216     if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
217     if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
218 
219     switch (fourcc) {
220       case MKFOURCC('A', 'L', 'P', 'H'):
221         if (alpha_chunks == 0) {
222           ++alpha_chunks;
223           frame->img_components_[1].offset_ = chunk_start_offset;
224           frame->img_components_[1].size_ = chunk_size;
225           frame->has_alpha_ = 1;
226           frame->frame_num_ = frame_num;
227           Skip(mem, payload_available);
228         } else {
229           goto Done;
230         }
231         break;
232       case MKFOURCC('V', 'P', '8', 'L'):
233         if (alpha_chunks > 0) return PARSE_ERROR;  // VP8L has its own alpha
234         // fall through
235       case MKFOURCC('V', 'P', '8', ' '):
236         if (image_chunks == 0) {
237           // Extract the bitstream features, tolerating failures when the data
238           // is incomplete.
239           WebPBitstreamFeatures features;
240           const VP8StatusCode vp8_status =
241               WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
242                               &features);
243           if (status == PARSE_NEED_MORE_DATA &&
244               vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
245             return PARSE_NEED_MORE_DATA;
246           } else if (vp8_status != VP8_STATUS_OK) {
247             // We have enough data, and yet WebPGetFeatures() failed.
248             return PARSE_ERROR;
249           }
250           ++image_chunks;
251           frame->img_components_[0].offset_ = chunk_start_offset;
252           frame->img_components_[0].size_ = chunk_size;
253           frame->width_ = features.width;
254           frame->height_ = features.height;
255           frame->has_alpha_ |= features.has_alpha;
256           frame->frame_num_ = frame_num;
257           frame->complete_ = (status == PARSE_OK);
258           Skip(mem, payload_available);
259         } else {
260           goto Done;
261         }
262         break;
263  Done:
264       default:
265         // Restore fourcc/size when moving up one level in parsing.
266         Rewind(mem, CHUNK_HEADER_SIZE);
267         done = 1;
268         break;
269     }
270 
271     if (mem->start_ == mem->riff_end_) {
272       done = 1;
273     } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
274       status = PARSE_NEED_MORE_DATA;
275     }
276   } while (!done && status == PARSE_OK);
277 
278   return status;
279 }
280 
281 // Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
282 // enough data ('min_size') to parse the payload.
283 // Returns PARSE_OK on success with *frame pointing to the new Frame.
284 // Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
NewFrame(const MemBuffer * const mem,uint32_t min_size,uint32_t actual_size,Frame ** frame)285 static ParseStatus NewFrame(const MemBuffer* const mem,
286                             uint32_t min_size, uint32_t actual_size,
287                             Frame** frame) {
288   if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
289   if (actual_size < min_size) return PARSE_ERROR;
290   if (MemDataSize(mem) < min_size)  return PARSE_NEED_MORE_DATA;
291 
292   *frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame));
293   return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
294 }
295 
296 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
297 // 'frame_chunk_size' is the previously validated, padded chunk size.
ParseAnimationFrame(WebPDemuxer * const dmux,uint32_t frame_chunk_size)298 static ParseStatus ParseAnimationFrame(
299     WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
300   const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
301   const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
302   int added_frame = 0;
303   int bits;
304   MemBuffer* const mem = &dmux->mem_;
305   Frame* frame;
306   ParseStatus status =
307       NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
308   if (status != PARSE_OK) return status;
309 
310   frame->x_offset_       = 2 * ReadLE24s(mem);
311   frame->y_offset_       = 2 * ReadLE24s(mem);
312   frame->width_          = 1 + ReadLE24s(mem);
313   frame->height_         = 1 + ReadLE24s(mem);
314   frame->duration_       = ReadLE24s(mem);
315   bits = ReadByte(mem);
316   frame->dispose_method_ =
317       (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
318   frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
319   if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
320     WebPSafeFree(frame);
321     return PARSE_ERROR;
322   }
323 
324   // Store a frame only if the animation flag is set there is some data for
325   // this frame is available.
326   status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
327   if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) {
328     added_frame = AddFrame(dmux, frame);
329     if (added_frame) {
330       ++dmux->num_frames_;
331     } else {
332       status = PARSE_ERROR;
333     }
334   }
335 
336   if (!added_frame) WebPSafeFree(frame);
337   return status;
338 }
339 
340 #ifdef WEBP_EXPERIMENTAL_FEATURES
341 // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
342 // 'fragment_chunk_size' is the previously validated, padded chunk size.
ParseFragment(WebPDemuxer * const dmux,uint32_t fragment_chunk_size)343 static ParseStatus ParseFragment(WebPDemuxer* const dmux,
344                                  uint32_t fragment_chunk_size) {
345   const int frame_num = 1;  // All fragments belong to the 1st (and only) frame.
346   const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
347   const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE;
348   int added_fragment = 0;
349   MemBuffer* const mem = &dmux->mem_;
350   Frame* frame;
351   ParseStatus status =
352       NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
353   if (status != PARSE_OK) return status;
354 
355   frame->is_fragment_ = 1;
356   frame->x_offset_ = 2 * ReadLE24s(mem);
357   frame->y_offset_ = 2 * ReadLE24s(mem);
358 
359   // Store a fragment only if the 'fragments' flag is set and there is some
360   // data available.
361   status = StoreFrame(frame_num, frgm_payload_size, mem, frame);
362   if (status != PARSE_ERROR && is_fragmented && frame->frame_num_ > 0) {
363     added_fragment = AddFrame(dmux, frame);
364     if (!added_fragment) {
365       status = PARSE_ERROR;
366     } else {
367       dmux->num_frames_ = 1;
368     }
369   }
370 
371   if (!added_fragment) WebPSafeFree(frame);
372   return status;
373 }
374 #endif  // WEBP_EXPERIMENTAL_FEATURES
375 
376 // General chunk storage, starting with the header at 'start_offset', allowing
377 // the user to request the payload via a fourcc string. 'size' includes the
378 // header and the unpadded payload size.
379 // Returns true on success, false otherwise.
StoreChunk(WebPDemuxer * const dmux,size_t start_offset,uint32_t size)380 static int StoreChunk(WebPDemuxer* const dmux,
381                       size_t start_offset, uint32_t size) {
382   Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk));
383   if (chunk == NULL) return 0;
384 
385   chunk->data_.offset_ = start_offset;
386   chunk->data_.size_ = size;
387   AddChunk(dmux, chunk);
388   return 1;
389 }
390 
391 // -----------------------------------------------------------------------------
392 // Primary chunk parsing
393 
ReadHeader(MemBuffer * const mem)394 static ParseStatus ReadHeader(MemBuffer* const mem) {
395   const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
396   uint32_t riff_size;
397 
398   // Basic file level validation.
399   if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
400   if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
401       memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
402     return PARSE_ERROR;
403   }
404 
405   riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
406   if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR;
407   if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
408 
409   // There's no point in reading past the end of the RIFF chunk
410   mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
411   if (mem->buf_size_ > mem->riff_end_) {
412     mem->buf_size_ = mem->end_ = mem->riff_end_;
413   }
414 
415   Skip(mem, RIFF_HEADER_SIZE);
416   return PARSE_OK;
417 }
418 
ParseSingleImage(WebPDemuxer * const dmux)419 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
420   const size_t min_size = CHUNK_HEADER_SIZE;
421   MemBuffer* const mem = &dmux->mem_;
422   Frame* frame;
423   ParseStatus status;
424   int image_added = 0;
425 
426   if (dmux->frames_ != NULL) return PARSE_ERROR;
427   if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
428   if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
429 
430   frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
431   if (frame == NULL) return PARSE_ERROR;
432 
433   // For the single image case we allow parsing of a partial frame, but we need
434   // at least CHUNK_HEADER_SIZE for parsing.
435   status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame);
436   if (status != PARSE_ERROR) {
437     const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
438     // Clear any alpha when the alpha flag is missing.
439     if (!has_alpha && frame->img_components_[1].size_ > 0) {
440       frame->img_components_[1].offset_ = 0;
441       frame->img_components_[1].size_ = 0;
442       frame->has_alpha_ = 0;
443     }
444 
445     // Use the frame width/height as the canvas values for non-vp8x files.
446     // Also, set ALPHA_FLAG if this is a lossless image with alpha.
447     if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
448       dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
449       dmux->canvas_width_ = frame->width_;
450       dmux->canvas_height_ = frame->height_;
451       dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
452     }
453     if (!AddFrame(dmux, frame)) {
454       status = PARSE_ERROR;  // last frame was left incomplete
455     } else {
456       image_added = 1;
457       dmux->num_frames_ = 1;
458     }
459   }
460 
461   if (!image_added) WebPSafeFree(frame);
462   return status;
463 }
464 
ParseVP8XChunks(WebPDemuxer * const dmux)465 static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
466   const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
467   MemBuffer* const mem = &dmux->mem_;
468   int anim_chunks = 0;
469   ParseStatus status = PARSE_OK;
470 
471   do {
472     int store_chunk = 1;
473     const size_t chunk_start_offset = mem->start_;
474     const uint32_t fourcc = ReadLE32(mem);
475     const uint32_t chunk_size = ReadLE32(mem);
476     const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
477 
478     if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
479     if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
480 
481     switch (fourcc) {
482       case MKFOURCC('V', 'P', '8', 'X'): {
483         return PARSE_ERROR;
484       }
485       case MKFOURCC('A', 'L', 'P', 'H'):
486       case MKFOURCC('V', 'P', '8', ' '):
487       case MKFOURCC('V', 'P', '8', 'L'): {
488         // check that this isn't an animation (all frames should be in an ANMF).
489         if (anim_chunks > 0 || is_animation) return PARSE_ERROR;
490 
491         Rewind(mem, CHUNK_HEADER_SIZE);
492         status = ParseSingleImage(dmux);
493         break;
494       }
495       case MKFOURCC('A', 'N', 'I', 'M'): {
496         if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
497 
498         if (MemDataSize(mem) < chunk_size_padded) {
499           status = PARSE_NEED_MORE_DATA;
500         } else if (anim_chunks == 0) {
501           ++anim_chunks;
502           dmux->bgcolor_ = ReadLE32(mem);
503           dmux->loop_count_ = ReadLE16s(mem);
504           Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
505         } else {
506           store_chunk = 0;
507           goto Skip;
508         }
509         break;
510       }
511       case MKFOURCC('A', 'N', 'M', 'F'): {
512         if (anim_chunks == 0) return PARSE_ERROR;  // 'ANIM' precedes frames.
513         status = ParseAnimationFrame(dmux, chunk_size_padded);
514         break;
515       }
516 #ifdef WEBP_EXPERIMENTAL_FEATURES
517       case MKFOURCC('F', 'R', 'G', 'M'): {
518         status = ParseFragment(dmux, chunk_size_padded);
519         break;
520       }
521 #endif
522       case MKFOURCC('I', 'C', 'C', 'P'): {
523         store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
524         goto Skip;
525       }
526       case MKFOURCC('E', 'X', 'I', 'F'): {
527         store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
528         goto Skip;
529       }
530       case MKFOURCC('X', 'M', 'P', ' '): {
531         store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
532         goto Skip;
533       }
534  Skip:
535       default: {
536         if (chunk_size_padded <= MemDataSize(mem)) {
537           if (store_chunk) {
538             // Store only the chunk header and unpadded size as only the payload
539             // will be returned to the user.
540             if (!StoreChunk(dmux, chunk_start_offset,
541                             CHUNK_HEADER_SIZE + chunk_size)) {
542               return PARSE_ERROR;
543             }
544           }
545           Skip(mem, chunk_size_padded);
546         } else {
547           status = PARSE_NEED_MORE_DATA;
548         }
549       }
550     }
551 
552     if (mem->start_ == mem->riff_end_) {
553       break;
554     } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
555       status = PARSE_NEED_MORE_DATA;
556     }
557   } while (status == PARSE_OK);
558 
559   return status;
560 }
561 
ParseVP8X(WebPDemuxer * const dmux)562 static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
563   MemBuffer* const mem = &dmux->mem_;
564   uint32_t vp8x_size;
565 
566   if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
567 
568   dmux->is_ext_format_ = 1;
569   Skip(mem, TAG_SIZE);  // VP8X
570   vp8x_size = ReadLE32(mem);
571   if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
572   if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
573   vp8x_size += vp8x_size & 1;
574   if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
575   if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
576 
577   dmux->feature_flags_ = ReadByte(mem);
578   Skip(mem, 3);  // Reserved.
579   dmux->canvas_width_  = 1 + ReadLE24s(mem);
580   dmux->canvas_height_ = 1 + ReadLE24s(mem);
581   if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
582     return PARSE_ERROR;  // image final dimension is too large
583   }
584   Skip(mem, vp8x_size - VP8X_CHUNK_SIZE);  // skip any trailing data.
585   dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
586 
587   if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
588   if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
589 
590   return ParseVP8XChunks(dmux);
591 }
592 
593 // -----------------------------------------------------------------------------
594 // Format validation
595 
IsValidSimpleFormat(const WebPDemuxer * const dmux)596 static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
597   const Frame* const frame = dmux->frames_;
598   if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
599 
600   if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
601   if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
602 
603   if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
604   return 1;
605 }
606 
607 // If 'exact' is true, check that the image resolution matches the canvas.
608 // If 'exact' is false, check that the x/y offsets do not exceed the canvas.
609 // TODO(jzern): this is insufficient in the fragmented image case if the
610 // expectation is that the fragments completely cover the canvas.
CheckFrameBounds(const Frame * const frame,int exact,int canvas_width,int canvas_height)611 static int CheckFrameBounds(const Frame* const frame, int exact,
612                             int canvas_width, int canvas_height) {
613   if (exact) {
614     if (frame->x_offset_ != 0 || frame->y_offset_ != 0) {
615       return 0;
616     }
617     if (frame->width_ != canvas_width || frame->height_ != canvas_height) {
618       return 0;
619     }
620   } else {
621     if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0;
622     if (frame->width_ + frame->x_offset_ > canvas_width) return 0;
623     if (frame->height_ + frame->y_offset_ > canvas_height) return 0;
624   }
625   return 1;
626 }
627 
IsValidExtendedFormat(const WebPDemuxer * const dmux)628 static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
629   const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
630   const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
631   const Frame* f = dmux->frames_;
632 
633   if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
634 
635   if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
636   if (dmux->loop_count_ < 0) return 0;
637   if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
638 #ifndef WEBP_EXPERIMENTAL_FEATURES
639   if (is_fragmented) return 0;
640 #endif
641 
642   while (f != NULL) {
643     const int cur_frame_set = f->frame_num_;
644     int frame_count = 0, fragment_count = 0;
645 
646     // Check frame properties and if the image is composed of fragments that
647     // each fragment came from a fragment.
648     for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
649       const ChunkData* const image = f->img_components_;
650       const ChunkData* const alpha = f->img_components_ + 1;
651 
652       if (is_fragmented && !f->is_fragment_) return 0;
653       if (!is_fragmented && f->is_fragment_) return 0;
654       if (!is_animation && f->frame_num_ > 1) return 0;
655 
656       if (f->complete_) {
657         if (alpha->size_ == 0 && image->size_ == 0) return 0;
658         // Ensure alpha precedes image bitstream.
659         if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
660           return 0;
661         }
662 
663         if (f->width_ <= 0 || f->height_ <= 0) return 0;
664       } else {
665         // There shouldn't be a partial frame in a complete file.
666         if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
667 
668         // Ensure alpha precedes image bitstream.
669         if (alpha->size_ > 0 && image->size_ > 0 &&
670             alpha->offset_ > image->offset_) {
671           return 0;
672         }
673         // There shouldn't be any frames after an incomplete one.
674         if (f->next_ != NULL) return 0;
675       }
676 
677       if (f->width_ > 0 && f->height_ > 0 &&
678           !CheckFrameBounds(f, !(is_animation || is_fragmented),
679                             dmux->canvas_width_, dmux->canvas_height_)) {
680         return 0;
681       }
682 
683       fragment_count += f->is_fragment_;
684       ++frame_count;
685     }
686     if (!is_fragmented && frame_count > 1) return 0;
687     if (fragment_count > 0 && frame_count != fragment_count) return 0;
688   }
689   return 1;
690 }
691 
692 // -----------------------------------------------------------------------------
693 // WebPDemuxer object
694 
InitDemux(WebPDemuxer * const dmux,const MemBuffer * const mem)695 static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
696   dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
697   dmux->loop_count_ = 1;
698   dmux->bgcolor_ = 0xFFFFFFFF;  // White background by default.
699   dmux->canvas_width_ = -1;
700   dmux->canvas_height_ = -1;
701   dmux->frames_tail_ = &dmux->frames_;
702   dmux->chunks_tail_ = &dmux->chunks_;
703   dmux->mem_ = *mem;
704 }
705 
WebPDemuxInternal(const WebPData * data,int allow_partial,WebPDemuxState * state,int version)706 WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
707                                WebPDemuxState* state, int version) {
708   const ChunkParser* parser;
709   int partial;
710   ParseStatus status = PARSE_ERROR;
711   MemBuffer mem;
712   WebPDemuxer* dmux;
713 
714   if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR;
715 
716   if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
717   if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
718 
719   if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
720   status = ReadHeader(&mem);
721   if (status != PARSE_OK) {
722     if (state != NULL) {
723       *state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER
724                                                 : WEBP_DEMUX_PARSE_ERROR;
725     }
726     return NULL;
727   }
728 
729   partial = (mem.buf_size_ < mem.riff_end_);
730   if (!allow_partial && partial) return NULL;
731 
732   dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
733   if (dmux == NULL) return NULL;
734   InitDemux(dmux, &mem);
735 
736   status = PARSE_ERROR;
737   for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
738     if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
739       status = parser->parse(dmux);
740       if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
741       if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
742       if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
743       if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR;
744       break;
745     }
746   }
747   if (state != NULL) *state = dmux->state_;
748 
749   if (status == PARSE_ERROR) {
750     WebPDemuxDelete(dmux);
751     return NULL;
752   }
753   return dmux;
754 }
755 
WebPDemuxDelete(WebPDemuxer * dmux)756 void WebPDemuxDelete(WebPDemuxer* dmux) {
757   Chunk* c;
758   Frame* f;
759   if (dmux == NULL) return;
760 
761   for (f = dmux->frames_; f != NULL;) {
762     Frame* const cur_frame = f;
763     f = f->next_;
764     WebPSafeFree(cur_frame);
765   }
766   for (c = dmux->chunks_; c != NULL;) {
767     Chunk* const cur_chunk = c;
768     c = c->next_;
769     WebPSafeFree(cur_chunk);
770   }
771   WebPSafeFree(dmux);
772 }
773 
774 // -----------------------------------------------------------------------------
775 
WebPDemuxGetI(const WebPDemuxer * dmux,WebPFormatFeature feature)776 uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
777   if (dmux == NULL) return 0;
778 
779   switch (feature) {
780     case WEBP_FF_FORMAT_FLAGS:     return dmux->feature_flags_;
781     case WEBP_FF_CANVAS_WIDTH:     return (uint32_t)dmux->canvas_width_;
782     case WEBP_FF_CANVAS_HEIGHT:    return (uint32_t)dmux->canvas_height_;
783     case WEBP_FF_LOOP_COUNT:       return (uint32_t)dmux->loop_count_;
784     case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
785     case WEBP_FF_FRAME_COUNT:      return (uint32_t)dmux->num_frames_;
786   }
787   return 0;
788 }
789 
790 // -----------------------------------------------------------------------------
791 // Frame iteration
792 
793 // Find the first 'frame_num' frame. There may be multiple such frames in a
794 // fragmented frame.
GetFrame(const WebPDemuxer * const dmux,int frame_num)795 static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
796   const Frame* f;
797   for (f = dmux->frames_; f != NULL; f = f->next_) {
798     if (frame_num == f->frame_num_) break;
799   }
800   return f;
801 }
802 
803 // Returns fragment 'fragment_num' and the total count.
GetFragment(const Frame * const frame_set,int fragment_num,int * const count)804 static const Frame* GetFragment(
805     const Frame* const frame_set, int fragment_num, int* const count) {
806   const int this_frame = frame_set->frame_num_;
807   const Frame* f = frame_set;
808   const Frame* fragment = NULL;
809   int total;
810 
811   for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
812     if (++total == fragment_num) fragment = f;
813   }
814   *count = total;
815   return fragment;
816 }
817 
GetFramePayload(const uint8_t * const mem_buf,const Frame * const frame,size_t * const data_size)818 static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
819                                       const Frame* const frame,
820                                       size_t* const data_size) {
821   *data_size = 0;
822   if (frame != NULL) {
823     const ChunkData* const image = frame->img_components_;
824     const ChunkData* const alpha = frame->img_components_ + 1;
825     size_t start_offset = image->offset_;
826     *data_size = image->size_;
827 
828     // if alpha exists it precedes image, update the size allowing for
829     // intervening chunks.
830     if (alpha->size_ > 0) {
831       const size_t inter_size = (image->offset_ > 0)
832                               ? image->offset_ - (alpha->offset_ + alpha->size_)
833                               : 0;
834       start_offset = alpha->offset_;
835       *data_size  += alpha->size_ + inter_size;
836     }
837     return mem_buf + start_offset;
838   }
839   return NULL;
840 }
841 
842 // Create a whole 'frame' from VP8 (+ alpha) or lossless.
SynthesizeFrame(const WebPDemuxer * const dmux,const Frame * const first_frame,int fragment_num,WebPIterator * const iter)843 static int SynthesizeFrame(const WebPDemuxer* const dmux,
844                            const Frame* const first_frame,
845                            int fragment_num, WebPIterator* const iter) {
846   const uint8_t* const mem_buf = dmux->mem_.buf_;
847   int num_fragments;
848   size_t payload_size = 0;
849   const Frame* const fragment =
850       GetFragment(first_frame, fragment_num, &num_fragments);
851   const uint8_t* const payload =
852       GetFramePayload(mem_buf, fragment, &payload_size);
853   if (payload == NULL) return 0;
854   assert(first_frame != NULL);
855 
856   iter->frame_num      = first_frame->frame_num_;
857   iter->num_frames     = dmux->num_frames_;
858   iter->fragment_num   = fragment_num;
859   iter->num_fragments  = num_fragments;
860   iter->x_offset       = fragment->x_offset_;
861   iter->y_offset       = fragment->y_offset_;
862   iter->width          = fragment->width_;
863   iter->height         = fragment->height_;
864   iter->has_alpha      = fragment->has_alpha_;
865   iter->duration       = fragment->duration_;
866   iter->dispose_method = fragment->dispose_method_;
867   iter->blend_method   = fragment->blend_method_;
868   iter->complete       = fragment->complete_;
869   iter->fragment.bytes = payload;
870   iter->fragment.size  = payload_size;
871   // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
872   return 1;
873 }
874 
SetFrame(int frame_num,WebPIterator * const iter)875 static int SetFrame(int frame_num, WebPIterator* const iter) {
876   const Frame* frame;
877   const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
878   if (dmux == NULL || frame_num < 0) return 0;
879   if (frame_num > dmux->num_frames_) return 0;
880   if (frame_num == 0) frame_num = dmux->num_frames_;
881 
882   frame = GetFrame(dmux, frame_num);
883   if (frame == NULL) return 0;
884 
885   return SynthesizeFrame(dmux, frame, 1, iter);
886 }
887 
WebPDemuxGetFrame(const WebPDemuxer * dmux,int frame,WebPIterator * iter)888 int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
889   if (iter == NULL) return 0;
890 
891   memset(iter, 0, sizeof(*iter));
892   iter->private_ = (void*)dmux;
893   return SetFrame(frame, iter);
894 }
895 
WebPDemuxNextFrame(WebPIterator * iter)896 int WebPDemuxNextFrame(WebPIterator* iter) {
897   if (iter == NULL) return 0;
898   return SetFrame(iter->frame_num + 1, iter);
899 }
900 
WebPDemuxPrevFrame(WebPIterator * iter)901 int WebPDemuxPrevFrame(WebPIterator* iter) {
902   if (iter == NULL) return 0;
903   if (iter->frame_num <= 1) return 0;
904   return SetFrame(iter->frame_num - 1, iter);
905 }
906 
WebPDemuxSelectFragment(WebPIterator * iter,int fragment_num)907 int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
908   if (iter != NULL && iter->private_ != NULL && fragment_num > 0) {
909     const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
910     const Frame* const frame = GetFrame(dmux, iter->frame_num);
911     if (frame == NULL) return 0;
912 
913     return SynthesizeFrame(dmux, frame, fragment_num, iter);
914   }
915   return 0;
916 }
917 
WebPDemuxReleaseIterator(WebPIterator * iter)918 void WebPDemuxReleaseIterator(WebPIterator* iter) {
919   (void)iter;
920 }
921 
922 // -----------------------------------------------------------------------------
923 // Chunk iteration
924 
ChunkCount(const WebPDemuxer * const dmux,const char fourcc[4])925 static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
926   const uint8_t* const mem_buf = dmux->mem_.buf_;
927   const Chunk* c;
928   int count = 0;
929   for (c = dmux->chunks_; c != NULL; c = c->next_) {
930     const uint8_t* const header = mem_buf + c->data_.offset_;
931     if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
932   }
933   return count;
934 }
935 
GetChunk(const WebPDemuxer * const dmux,const char fourcc[4],int chunk_num)936 static const Chunk* GetChunk(const WebPDemuxer* const dmux,
937                              const char fourcc[4], int chunk_num) {
938   const uint8_t* const mem_buf = dmux->mem_.buf_;
939   const Chunk* c;
940   int count = 0;
941   for (c = dmux->chunks_; c != NULL; c = c->next_) {
942     const uint8_t* const header = mem_buf + c->data_.offset_;
943     if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
944     if (count == chunk_num) break;
945   }
946   return c;
947 }
948 
SetChunk(const char fourcc[4],int chunk_num,WebPChunkIterator * const iter)949 static int SetChunk(const char fourcc[4], int chunk_num,
950                     WebPChunkIterator* const iter) {
951   const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
952   int count;
953 
954   if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
955   count = ChunkCount(dmux, fourcc);
956   if (count == 0) return 0;
957   if (chunk_num == 0) chunk_num = count;
958 
959   if (chunk_num <= count) {
960     const uint8_t* const mem_buf = dmux->mem_.buf_;
961     const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
962     iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
963     iter->chunk.size  = chunk->data_.size_ - CHUNK_HEADER_SIZE;
964     iter->num_chunks  = count;
965     iter->chunk_num   = chunk_num;
966     return 1;
967   }
968   return 0;
969 }
970 
WebPDemuxGetChunk(const WebPDemuxer * dmux,const char fourcc[4],int chunk_num,WebPChunkIterator * iter)971 int WebPDemuxGetChunk(const WebPDemuxer* dmux,
972                       const char fourcc[4], int chunk_num,
973                       WebPChunkIterator* iter) {
974   if (iter == NULL) return 0;
975 
976   memset(iter, 0, sizeof(*iter));
977   iter->private_ = (void*)dmux;
978   return SetChunk(fourcc, chunk_num, iter);
979 }
980 
WebPDemuxNextChunk(WebPChunkIterator * iter)981 int WebPDemuxNextChunk(WebPChunkIterator* iter) {
982   if (iter != NULL) {
983     const char* const fourcc =
984         (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
985     return SetChunk(fourcc, iter->chunk_num + 1, iter);
986   }
987   return 0;
988 }
989 
WebPDemuxPrevChunk(WebPChunkIterator * iter)990 int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
991   if (iter != NULL && iter->chunk_num > 1) {
992     const char* const fourcc =
993         (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
994     return SetChunk(fourcc, iter->chunk_num - 1, iter);
995   }
996   return 0;
997 }
998 
WebPDemuxReleaseChunkIterator(WebPChunkIterator * iter)999 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
1000   (void)iter;
1001 }
1002 
1003