tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

demux.c (31935B)


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