Commit message: ...
CVE: CVE-2019-10131
CWE: CWE-119
Vulnerability description: An off-by-one read vulnerability was discovered in ImageMagick before version 7.0.7-28 in the formatIPTCfromBuffer function in coders/meta.c. A local attacker may use this flaw to read beyond the end of the buffer or to crash the program.

Project: ImageMagick
File: meta.c

static size_t GetIPTCStream(unsigned char **info,size_t length)
{
  int
    c;

  register ssize_t
    i;

  register unsigned char
    *p;

  size_t
    extent,
    info_length;

  unsigned int
    marker;

  size_t
    tag_length;

  p=(*info);
  extent=length;
  if ((*p == 0x1c) && (*(p+1) == 0x02))
    return(length);
  /*
    Extract IPTC from 8BIM resource block.
  */
  while (extent >= 12)
  {
    if (strncmp((const char *) p,"8BIM",4))
      break;
    p+=4;
    extent-=4;
    marker=(unsigned int) (*p) << 8 | *(p+1);
    p+=2;
    extent-=2;
    c=*p++;
    extent--;
    c|=0x01;
    if ((size_t) c >= extent)
      break;
    p+=c;
    extent-=c;
    if (extent < 4)
      break;
    tag_length=(((size_t) *p) << 24) | (((size_t) *(p+1)) << 16) |
      (((size_t) *(p+2)) << 8) | ((size_t) *(p+3));
    p+=4;
    extent-=4;
    if (tag_length > extent)
      break;
    if (marker == IPTC_ID)
      {
        *info=p;
        return(tag_length);
      }
    if ((tag_length & 0x01) != 0)
      tag_length++;
    p+=tag_length;
    extent-=tag_length;
  }
  /*
    Find the beginning of the IPTC info.
  */
  p=(*info);
  tag_length=0;
iptc_find:
  info_length=0;
  marker=MagickFalse;
  while (length != 0)
  {
    c=(*p++);
    length--;
    if (length == 0)
      break;
    if (c == 0x1c)
      {
        p--;
        *info=p; /* let the caller know were it is */
        break;
      }
  }
  /*
    Determine the length of the IPTC info.
  */
  while (length != 0)
  {
    c=(*p++);
    length--;
    if (length == 0)
      break;
    if (c == 0x1c)
      marker=MagickTrue;
    else
      if (marker)
        break;
      else
        continue;
    info_length++;
    /*
      Found the 0x1c tag; skip the dataset and record number tags.
    */
    c=(*p++); /* should be 2 */
    length--;
    if (length == 0)
      break;
    if ((info_length == 1) && (c != 2))
      goto iptc_find;
    info_length++;
    c=(*p++); /* should be 0 */
    length--;
    if (length == 0)
      break;
    if ((info_length == 2) && (c != 0))
      goto iptc_find;
    info_length++;
    /*
      Decode the length of the block that follows - ssize_t or short format.
    */
    c=(*p++);
    length--;
    if (length == 0)
      break;
    info_length++;
    if ((c & 0x80) != 0)
      {
        /*
          Long format.
        */
        tag_length=0;
        for (i=0; i < 4; i++)
        {
          tag_length<<=8;
          tag_length|=(*p++);
          length--;
          if (length == 0)
            break;
          info_length++;
        }
      }
    else
      {
        /*
          Short format.
        */
        tag_length=((long) c) << 8;
        c=(*p++);
        length--;
        if (length == 0)
          break;
        info_length++;
        tag_length|=(long) c;
      }
    if (tag_length > (length+1))
      break;
    p+=tag_length;
    length-=tag_length;
    if (length == 0)
      break;
    info_length+=tag_length;
  }
  return(info_length);
}

Project: ImageMagick
File: meta.c

static int formatIPTC(Image *ifile, Image *ofile)
{
  char
    temp[MagickPathExtent];

  unsigned int
    foundiptc,
    tagsfound;

  unsigned char
    recnum,
    dataset;

  unsigned char
    *readable,
    *str;

  ssize_t
    tagindx,
    taglen;

  int
    i,
    tagcount = (int) (sizeof(tags) / sizeof(tag_spec));

  int
    c;

  foundiptc = 0; /* found the IPTC-Header */
  tagsfound = 0; /* number of tags found */

  c = ReadBlobByte(ifile);
  while (c != EOF)
  {
    if (c == 0x1c)
      foundiptc = 1;
    else
      {
        if (foundiptc)
          return(-1);
        else
          {
            c=0;
            continue;
          }
      }

    /* we found the 0x1c tag and now grab the dataset and record number tags */
    c = ReadBlobByte(ifile);
    if (c == EOF)
      return(-1);
    dataset = (unsigned char) c;
    c = ReadBlobByte(ifile);
    if (c == EOF)
      return(-1);
    recnum = (unsigned char) c;
    /* try to match this record to one of the ones in our named table */
    for (i=0; i< tagcount; i++)
    {
      if (tags[i].id == (short) recnum)
          break;
    }
    if (i < tagcount)
      readable = (unsigned char *) tags[i].name;
    else
      readable = (unsigned char *) "";
    /*
      We decode the length of the block that follows - ssize_t or short fmt.
    */
    c=ReadBlobByte(ifile);
    if (c == EOF)
      return(-1);
    if (c & (unsigned char) 0x80)
      return(0);
    else
      {
        int
          c0;

        c0=ReadBlobByte(ifile);
        if (c0 == EOF)
          return(-1);
        taglen = (c << 8) | c0;
      }
    if (taglen < 0)
      return(-1);
    /* make a buffer to hold the tag datand snag it from the input stream */
    str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MagickPathExtent),
      sizeof(*str));
    if (str == (unsigned char *) NULL)
      return(0);
    for (tagindx=0; tagindx<taglen; tagindx++)
    {
      c=ReadBlobByte(ifile);
      if (c == EOF)
        {
          str=(unsigned char *) RelinquishMagickMemory(str);
          return(-1);
        }
      str[tagindx] = (unsigned char) c;
    }
    str[taglen] = 0;

    /* now finish up by formatting this binary data into ASCII equivalent */
    if (strlen((char *)readable) > 0)
      (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=",
        (unsigned int) dataset, (unsigned int) recnum, readable);
    else
      (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=",
        (unsigned int) dataset,(unsigned int) recnum);
    (void) WriteBlobString(ofile,temp);
    formatString( ofile, (char *)str, taglen );
    str=(unsigned char *) RelinquishMagickMemory(str);

    tagsfound++;

    c=ReadBlobByte(ifile);
  }
  return((int) tagsfound);
}

Project: ImageMagick
File: meta.c

static int jpeg_skip_till_marker(Image *ifile, int marker)
{
  int c, i;

  do
  {
    /* skip anything until we hit 0xff */
    i = 0;
    do
    {
      c = ReadBlobByte(ifile);
      i++;
      if (c == EOF)
        return M_EOI; /* we hit EOF */
    } while (c != 0xff);

    /* get marker byte, swallowing possible padding */
    do
    {
      c = ReadBlobByte(ifile);
      if (c == EOF)
        return M_EOI; /* we hit EOF */
    } while (c == 0xff);
  } while (c != marker);
  return c;
}