Merge commit '0a467a9b594dd67aa96bad687d05f8845b009f18'
authorMichael Niedermayer <michaelni@gmx.at>
Sun, 13 Apr 2014 01:01:03 +0000 (03:01 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Sun, 13 Apr 2014 01:20:09 +0000 (03:20 +0200)
* commit '0a467a9b594dd67aa96bad687d05f8845b009f18':
  tiffdec: use bytestream2 to simplify overread/overwrite protection

Conflicts:
libavcodec/tiff.c

See: 1ec83d9a9e472f485897ac92bad9631d551a8c5b
Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
libavcodec/tiff.c
libavcodec/tiff_common.c

@@@ -383,13 -168,10 +383,14 @@@ static int tiff_unpack_fax(TiffContext 
  static int tiff_unpack_strip(TiffContext *s, uint8_t *dst, int stride,
                               const uint8_t *src, int size, int lines)
  {
+     PutByteContext pb;
      int c, line, pixels, code, ret;
-     int width           = ((s->width * s->bpp) + 7) >> 3;
 +    const uint8_t *ssrc = src;
+     int width = ((s->width * s->bpp) + 7) >> 3;
  
 +    if (s->planar)
 +        width /= s->bppcount;
 +
      if (size <= 0)
          return AVERROR_INVALIDDATA;
  
              av_log(s->avctx, AV_LOG_ERROR, "Error initializing LZW decoder\n");
              return ret;
          }
+         for (line = 0; line < lines; line++) {
+             pixels = ff_lzw_decode(s->lzw, dst, width);
+             if (pixels < width) {
+                 av_log(s->avctx, AV_LOG_ERROR, "Decoded only %i bytes of %i\n",
+                        pixels, width);
+                 return AVERROR_INVALIDDATA;
+             }
++            if (s->bpp < 8 && s->avctx->pix_fmt == AV_PIX_FMT_PAL8)
++                horizontal_fill(s->bpp, dst, 1, dst, 0, width, 0);
+             dst += stride;
+         }
+         return 0;
      }
      if (s->compr == TIFF_CCITT_RLE ||
          s->compr == TIFF_G3        ||
          s->compr == TIFF_G4) {
 -        return tiff_unpack_fax(s, dst, stride, src, size, lines);
 +        return tiff_unpack_fax(s, dst, stride, src, size, width, lines);
      }
+     bytestream2_init(&s->gb, src, size);
+     bytestream2_init_writer(&pb, dst, stride * lines);
      for (line = 0; line < lines; line++) {
 +        if (src - ssrc > size) {
 +            av_log(s->avctx, AV_LOG_ERROR, "Source data overread\n");
 +            return AVERROR_INVALIDDATA;
 +        }
++
+         if (bytestream2_get_bytes_left(&s->gb) == 0 || bytestream2_get_eof(&pb))
+             break;
+         bytestream2_seek_p(&pb, stride * line, SEEK_SET);
          switch (s->compr) {
          case TIFF_RAW:
 +            if (ssrc + size - src < width)
 +                return AVERROR_INVALIDDATA;
++
              if (!s->fill_order) {
 -                bytestream2_copy_buffer(&pb, &s->gb, width);
 +                horizontal_fill(s->bpp * (s->avctx->pix_fmt == AV_PIX_FMT_PAL8),
 +                                dst, 1, src, 0, width, 0);
              } else {
                  int i;
                  for (i = 0; i < width; i++)
                      pixels += code;
                  }
              }
 +            if (s->fill_order) {
 +                int i;
 +                for (i = 0; i < width; i++)
 +                    dst[i] = ff_reverse[dst[i]];
 +            }
              break;
-         case TIFF_LZW:
-             pixels = ff_lzw_decode(s->lzw, dst, width);
-             if (pixels < width) {
-                 av_log(s->avctx, AV_LOG_ERROR, "Decoded only %i bytes of %i\n",
-                        pixels, width);
-                 return AVERROR_INVALIDDATA;
-             }
-             if (s->bpp < 8 && s->avctx->pix_fmt == AV_PIX_FMT_PAL8)
-                 horizontal_fill(s->bpp, dst, 1, dst, 0, width, 0);
-             break;
          }
 +        dst += stride;
      }
      return 0;
  }
@@@ -567,30 -296,26 +579,31 @@@ static int init_image(TiffContext *s, T
      return 0;
  }
  
 -static int tiff_decode_tag(TiffContext *s)
 +static void set_sar(TiffContext *s, unsigned tag, unsigned num, unsigned den)
 +{
 +    int offset = tag == TIFF_YRES ? 2 : 0;
 +    s->res[offset++] = num;
 +    s->res[offset]   = den;
 +    if (s->res[0] && s->res[1] && s->res[2] && s->res[3])
 +        av_reduce(&s->avctx->sample_aspect_ratio.num, &s->avctx->sample_aspect_ratio.den,
 +                  s->res[2] * (uint64_t)s->res[1], s->res[0] * (uint64_t)s->res[3], INT32_MAX);
 +}
 +
 +static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
  {
 -    unsigned tag, type, count, off, value = 0;
 +    unsigned tag, type, count, off, value = 0, value2 = 0;
-     int i, j, k, pos, start;
+     int i, start;
++    int j, k, pos;
 +    int ret;
      uint32_t *pal;
 +    double *dp;
  
 -    if (bytestream2_get_bytes_left(&s->gb) < 12)
 -        return AVERROR_INVALIDDATA;
 -    tag   = tget_short(&s->gb, s->le);
 -    type  = tget_short(&s->gb, s->le);
 -    count = tget_long(&s->gb, s->le);
 -    off   = tget_long(&s->gb, s->le);
 -    start = bytestream2_tell(&s->gb);
 -
 -    if (type == 0 || type >= FF_ARRAY_ELEMS(type_sizes)) {
 -        av_log(s->avctx, AV_LOG_DEBUG, "Unknown tiff type (%u) encountered\n",
 -               type);
 -        return 0;
 +    ret = ff_tread_tag(&s->gb, s->le, &tag, &type, &count, &start);
 +    if (ret < 0) {
 +        goto end;
      }
  
 +    off = bytestream2_tell(&s->gb);
      if (count == 1) {
          switch (type) {
          case TIFF_BYTE:
          }
          s->strips = count;
          s->sstype = type;
-         if (s->stripsizesoff > bytestream2_size(&s->gb)) {
-             av_log(s->avctx, AV_LOG_ERROR,
-                    "Tag referencing position outside the image\n");
-             return AVERROR_INVALIDDATA;
-         }
          break;
 +    case TIFF_XRES:
 +    case TIFF_YRES:
 +        set_sar(s, tag, value, value2);
 +        break;
 +    case TIFF_TILE_BYTE_COUNTS:
 +    case TIFF_TILE_LENGTH:
 +    case TIFF_TILE_OFFSETS:
 +    case TIFF_TILE_WIDTH:
 +        av_log(s->avctx, AV_LOG_ERROR, "Tiled images are not supported\n");
 +        return AVERROR_PATCHWELCOME;
 +        break;
      case TIFF_PREDICTOR:
          s->predictor = value;
          break;
          }
          s->fill_order = value - 1;
          break;
-     case TIFF_PAL:
+     case TIFF_PAL: {
 -        GetByteContext pal_gb[3];
          pal = (uint32_t *) s->palette;
          off = type_sizes[type];
-         if (count / 3 > 256 || bytestream2_get_bytes_left(&s->gb) < count / 3 * off * 3)
+         if (count / 3 > 256 ||
+             bytestream2_get_bytes_left(&s->gb) < count / 3 * off * 3)
              return AVERROR_INVALIDDATA;
 -        pal_gb[0] = pal_gb[1] = pal_gb[2] = s->gb;
 -        bytestream2_skip(&pal_gb[1], count / 3 * off);
 -        bytestream2_skip(&pal_gb[2], count / 3 * off * 2);
++
          off = (type_sizes[type] - 1) << 3;
 -        for (i = 0; i < count / 3; i++) {
 -            uint32_t p = 0xFF000000;
 -            p |= (tget(&pal_gb[0], type, s->le) >> off) << 16;
 -            p |= (tget(&pal_gb[1], type, s->le) >> off) << 8;
 -            p |=  tget(&pal_gb[2], type, s->le) >> off;
 -            pal[i] = p;
 +        for (k = 2; k >= 0; k--) {
 +            for (i = 0; i < count / 3; i++) {
 +                if (k == 2)
 +                    pal[i] = 0xFFU << 24;
 +                j =  (ff_tget(&s->gb, type, s->le) >> off) << (k * 8);
 +                pal[i] |= j;
 +            }
          }
          s->palette_is_set = 1;
          break;
+     }
      case TIFF_PLANAR:
 -        if (value == 2) {
 -            avpriv_report_missing_feature(s->avctx, "Planar format");
 -            return AVERROR_PATCHWELCOME;
 -        }
 +        s->planar = value == 2;
          break;
      case TIFF_T4OPTIONS:
          if (s->compr == TIFF_G3)
@@@ -1015,35 -588,30 +1021,37 @@@ static int decode_frame(AVCodecContext 
          av_log(avctx, AV_LOG_WARNING, "Image data size missing\n");
          s->stripsize = avpkt->size - s->stripoff;
      }
 -    stride = p->linesize[0];
 -    dst    = p->data[0];
  
      if (s->stripsizesoff) {
 -        if (s->stripsizesoff >= avpkt->size)
 +        if (s->stripsizesoff >= (unsigned)avpkt->size)
              return AVERROR_INVALIDDATA;
-         bytestream2_init(&stripsizes, avpkt->data + s->stripsizesoff, avpkt->size - s->stripsizesoff);
+         bytestream2_init(&stripsizes, avpkt->data + s->stripsizesoff,
+                          avpkt->size - s->stripsizesoff);
      }
      if (s->strippos) {
 -        if (s->strippos >= avpkt->size)
 +        if (s->strippos >= (unsigned)avpkt->size)
              return AVERROR_INVALIDDATA;
-         bytestream2_init(&stripdata, avpkt->data + s->strippos, avpkt->size - s->strippos);
+         bytestream2_init(&stripdata, avpkt->data + s->strippos,
+                          avpkt->size - s->strippos);
      }
  
 +    if (s->rps <= 0) {
 +        av_log(avctx, AV_LOG_ERROR, "rps %d invalid\n", s->rps);
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    planes = s->planar ? s->bppcount : 1;
 +    for (plane = 0; plane < planes; plane++) {
 +        stride = p->linesize[plane];
 +        dst    = p->data[plane];
      for (i = 0; i < s->height; i += s->rps) {
          if (s->stripsizesoff)
-             ssize = ff_tget(&stripsizes, s->sstype, s->le);
 -            ssize = tget(&stripsizes, s->sstype, le);
++            ssize = ff_tget(&stripsizes, s->sstype, le);
          else
              ssize = s->stripsize;
  
          if (s->strippos)
-             soff = ff_tget(&stripdata, s->sot, s->le);
 -            soff = tget(&stripdata, s->sot, le);
++            soff = ff_tget(&stripdata, s->sot, le);
          else
              soff = s->stripoff;
  
index fc34087,0000000..f14ecf1
mode 100644,000000..100644
--- /dev/null
@@@ -1,317 -1,0 +1,311 @@@
-     unsigned v = le ? bytestream2_get_le16(gb) : bytestream2_get_be16(gb);
-     return v;
 +/*
 + * TIFF Common Routines
 + * Copyright (c) 2013 Thilo Borgmann <thilo.borgmann _at_ mail.de>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * TIFF Common Routines
 + * @author Thilo Borgmann <thilo.borgmann _at_ mail.de>
 + */
 +
 +#include "tiff_common.h"
 +
 +
 +int ff_tis_ifd(unsigned tag)
 +{
 +    int i;
 +    for (i = 0; i < FF_ARRAY_ELEMS(ifd_tags); i++) {
 +        if (ifd_tags[i] == tag) {
 +            return i + 1;
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +unsigned ff_tget_short(GetByteContext *gb, int le)
 +{
-     unsigned v = le ? bytestream2_get_le32(gb) : bytestream2_get_be32(gb);
-     return v;
++    return le ? bytestream2_get_le16(gb) : bytestream2_get_be16(gb);
 +}
 +
 +
 +unsigned ff_tget_long(GetByteContext *gb, int le)
 +{
-     case TIFF_BYTE:
-         return bytestream2_get_byte(gb);
-     case TIFF_SHORT:
-         return ff_tget_short(gb, le);
-     case TIFF_LONG:
-         return ff_tget_long(gb, le);
-     default:
-         return UINT_MAX;
++    return le ? bytestream2_get_le32(gb) : bytestream2_get_be32(gb);
 +}
 +
 +
 +double ff_tget_double(GetByteContext *gb, int le)
 +{
 +    av_alias64 i = { .u64 = le ? bytestream2_get_le64(gb) : bytestream2_get_be64(gb)};
 +    return i.f64;
 +}
 +
 +
 +unsigned ff_tget(GetByteContext *gb, int type, int le)
 +{
 +    switch (type) {
++    case TIFF_BYTE:  return bytestream2_get_byte(gb);
++    case TIFF_SHORT: return ff_tget_short(gb, le);
++    case TIFF_LONG:  return ff_tget_long(gb, le);
++    default:         return UINT_MAX;
 +    }
 +}
 +
 +static const char *auto_sep(int count, const char *sep, int i, int columns)
 +{
 +    if (sep)
 +        return i ? sep : "";
 +    if (i && i%columns) {
 +        return ", ";
 +    } else
 +        return columns < count ? "\n" : "";
 +}
 +
 +int ff_tadd_rational_metadata(int count, const char *name, const char *sep,
 +                              GetByteContext *gb, int le, AVDictionary **metadata)
 +{
 +    AVBPrint bp;
 +    char *ap;
 +    int32_t nom, denom;
 +    int i;
 +
 +    if (count >= INT_MAX / sizeof(int64_t) || count <= 0)
 +        return AVERROR_INVALIDDATA;
 +    if (bytestream2_get_bytes_left(gb) < count * sizeof(int64_t))
 +        return AVERROR_INVALIDDATA;
 +
 +    av_bprint_init(&bp, 10 * count, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    for (i = 0; i < count; i++) {
 +        nom   = ff_tget_long(gb, le);
 +        denom = ff_tget_long(gb, le);
 +        av_bprintf(&bp, "%s%7i:%-7i", auto_sep(count, sep, i, 4), nom, denom);
 +    }
 +
 +    if ((i = av_bprint_finalize(&bp, &ap))) {
 +        return i;
 +    }
 +    if (!ap) {
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    av_dict_set(metadata, name, ap, AV_DICT_DONT_STRDUP_VAL);
 +
 +    return 0;
 +}
 +
 +
 +int ff_tadd_long_metadata(int count, const char *name, const char *sep,
 +                          GetByteContext *gb, int le, AVDictionary **metadata)
 +{
 +    AVBPrint bp;
 +    char *ap;
 +    int i;
 +
 +    if (count >= INT_MAX / sizeof(int32_t) || count <= 0)
 +        return AVERROR_INVALIDDATA;
 +    if (bytestream2_get_bytes_left(gb) < count * sizeof(int32_t))
 +        return AVERROR_INVALIDDATA;
 +
 +    av_bprint_init(&bp, 10 * count, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    for (i = 0; i < count; i++) {
 +        av_bprintf(&bp, "%s%7i", auto_sep(count, sep, i, 8), ff_tget_long(gb, le));
 +    }
 +
 +    if ((i = av_bprint_finalize(&bp, &ap))) {
 +        return i;
 +    }
 +    if (!ap) {
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    av_dict_set(metadata, name, ap, AV_DICT_DONT_STRDUP_VAL);
 +
 +    return 0;
 +}
 +
 +
 +int ff_tadd_doubles_metadata(int count, const char *name, const char *sep,
 +                             GetByteContext *gb, int le, AVDictionary **metadata)
 +{
 +    AVBPrint bp;
 +    char *ap;
 +    int i;
 +
 +    if (count >= INT_MAX / sizeof(int64_t) || count <= 0)
 +        return AVERROR_INVALIDDATA;
 +    if (bytestream2_get_bytes_left(gb) < count * sizeof(int64_t))
 +        return AVERROR_INVALIDDATA;
 +
 +    av_bprint_init(&bp, 10 * count, 100 * count);
 +
 +    for (i = 0; i < count; i++) {
 +        av_bprintf(&bp, "%s%.15g", auto_sep(count, sep, i, 4), ff_tget_double(gb, le));
 +    }
 +
 +    if ((i = av_bprint_finalize(&bp, &ap))) {
 +        return i;
 +    }
 +    if (!ap) {
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    av_dict_set(metadata, name, ap, AV_DICT_DONT_STRDUP_VAL);
 +
 +    return 0;
 +}
 +
 +
 +int ff_tadd_shorts_metadata(int count, const char *name, const char *sep,
 +                            GetByteContext *gb, int le, AVDictionary **metadata)
 +{
 +    AVBPrint bp;
 +    char *ap;
 +    int i;
 +
 +    if (count >= INT_MAX / sizeof(int16_t) || count <= 0)
 +        return AVERROR_INVALIDDATA;
 +    if (bytestream2_get_bytes_left(gb) < count * sizeof(int16_t))
 +        return AVERROR_INVALIDDATA;
 +
 +    av_bprint_init(&bp, 10 * count, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    for (i = 0; i < count; i++) {
 +        av_bprintf(&bp, "%s%5i", auto_sep(count, sep, i, 8), ff_tget_short(gb, le));
 +    }
 +
 +    if ((i = av_bprint_finalize(&bp, &ap))) {
 +        return i;
 +    }
 +    if (!ap) {
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    av_dict_set(metadata, name, ap, AV_DICT_DONT_STRDUP_VAL);
 +
 +    return 0;
 +}
 +
 +
 +int ff_tadd_bytes_metadata(int count, const char *name, const char *sep,
 +                           GetByteContext *gb, int le, AVDictionary **metadata)
 +{
 +    AVBPrint bp;
 +    char *ap;
 +    int i;
 +
 +    if (count >= INT_MAX / sizeof(int8_t) || count < 0)
 +        return AVERROR_INVALIDDATA;
 +    if (bytestream2_get_bytes_left(gb) < count * sizeof(int8_t))
 +        return AVERROR_INVALIDDATA;
 +
 +    av_bprint_init(&bp, 10 * count, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    for (i = 0; i < count; i++) {
 +        av_bprintf(&bp, "%s%3i", auto_sep(count, sep, i, 16), bytestream2_get_byte(gb));
 +    }
 +
 +    if ((i = av_bprint_finalize(&bp, &ap))) {
 +        return i;
 +    }
 +    if (!ap) {
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    av_dict_set(metadata, name, ap, AV_DICT_DONT_STRDUP_VAL);
 +
 +    return 0;
 +}
 +
 +int ff_tadd_string_metadata(int count, const char *name,
 +                            GetByteContext *gb, int le, AVDictionary **metadata)
 +{
 +    char *value;
 +
 +    if (bytestream2_get_bytes_left(gb) < count || count < 0)
 +        return AVERROR_INVALIDDATA;
 +
 +    value = av_malloc(count + 1);
 +    if (!value)
 +        return AVERROR(ENOMEM);
 +
 +    bytestream2_get_bufferu(gb, value, count);
 +    value[count] = 0;
 +
 +    av_dict_set(metadata, name, value, AV_DICT_DONT_STRDUP_VAL);
 +    return 0;
 +}
 +
 +
 +int ff_tdecode_header(GetByteContext *gb, int *le, int *ifd_offset)
 +{
 +    if (bytestream2_get_bytes_left(gb) < 8) {
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    *le = bytestream2_get_le16u(gb);
 +    if (*le == AV_RB16("II")) {
 +        *le = 1;
 +    } else if (*le == AV_RB16("MM")) {
 +        *le = 0;
 +    } else {
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    if (ff_tget_short(gb, *le) != 42) {
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    *ifd_offset = ff_tget_long(gb, *le);
 +
 +    return 0;
 +}
 +
 +
 +int ff_tread_tag(GetByteContext *gb, int le, unsigned *tag, unsigned *type,
 +                 unsigned *count, int *next)
 +{
 +    int ifd_tag;
 +    int valid_type;
 +
 +    *tag    = ff_tget_short(gb, le);
 +    *type   = ff_tget_short(gb, le);
 +    *count  = ff_tget_long (gb, le);
 +
 +    ifd_tag    = ff_tis_ifd(*tag);
 +    valid_type = *type != 0 && *type < FF_ARRAY_ELEMS(type_sizes);
 +
 +    *next = bytestream2_tell(gb) + 4;
 +
 +    // check for valid type
 +    if (!valid_type) {
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    // seek to offset if this is an IFD-tag or
 +    // if count values do not fit into the offset value
 +    if (ifd_tag || (*count > 4 || !(type_sizes[*type] * (*count) <= 4 || *type == TIFF_STRING))) {
 +        bytestream2_seek(gb, ff_tget_long (gb, le), SEEK_SET);
 +    }
 +
 +    return 0;
 +}