Commit message: https://github.com/ImageMagick/ImageMagick/issues/660
CVE: CVE-2017-12806
CWE: CWE-400
Vulnerability description: In ImageMagick 7.0.6-6, a memory exhaustion vulnerability was found in the function format8BIM, which allows attackers to cause a denial of service.

Project: ImageMagick6
File: meta.c

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

  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+MaxTextExtent),
      sizeof(*str));
    if (str == (unsigned char *) NULL)
      {
        printf("MemoryAllocationFailed");
        return 0;
      }
    for (tagindx=0; tagindx<taglen; tagindx++)
    {
      c=ReadBlobByte(ifile);
      if (c == EOF) 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,MaxTextExtent,"%d#%d#%s=",
        (unsigned int) dataset, (unsigned int) recnum, readable);
    else
      (void) FormatLocaleString(temp,MaxTextExtent,"%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: ImageMagick6
File: meta.c

static MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
  Image *image)
{
  const StringInfo
    *profile;

  MagickBooleanType
    status;

  size_t
    length;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  length=0;
  if (LocaleCompare(image_info->magick,"8BIM") == 0)
    {
      /*
        Write 8BIM image.
      */
      profile=GetImageProfile(image,"8bim");
      if (profile == (StringInfo *) NULL)
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
      if (status == MagickFalse)
        return(status);
      (void) WriteBlob(image,GetStringInfoLength(profile),
        GetStringInfoDatum(profile));
      (void) CloseBlob(image);
      return(MagickTrue);
    }
  if (LocaleCompare(image_info->magick,"iptc") == 0)
    {
      size_t
        length;

      unsigned char
        *info;

      profile=GetImageProfile(image,"iptc");
      if (profile == (StringInfo *) NULL)
        profile=GetImageProfile(image,"8bim");
      if (profile == (StringInfo *) NULL)
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
      info=GetStringInfoDatum(profile);
      length=GetStringInfoLength(profile);
      length=GetIPTCStream(&info,length);
      if (length == 0)
        ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
      (void) WriteBlob(image,length,info);
      (void) CloseBlob(image);
      return(MagickTrue);
    }
  if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
    {
      Image
        *buff;

      profile=GetImageProfile(image,"8bim");
      if (profile == (StringInfo *) NULL)
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
      if (status == MagickFalse)
        return(status);
      buff=AcquireImage((ImageInfo *) NULL);
      if (buff == (Image *) NULL)
        ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
      AttachBlob(buff->blob,GetStringInfoDatum(profile),
        GetStringInfoLength(profile));
      format8BIM(buff,image);
      (void) DetachBlob(buff->blob);
      buff=DestroyImage(buff);
      (void) CloseBlob(image);
      return(MagickTrue);
    }
  if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
    return(MagickFalse);
  if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0)
    {
      Image
        *buff;

      unsigned char
        *info;

      profile=GetImageProfile(image,"8bim");
      if (profile == (StringInfo *) NULL)
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
      info=GetStringInfoDatum(profile);
      length=GetStringInfoLength(profile);
      length=GetIPTCStream(&info,length);
      if (length == 0)
        ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
      if (status == MagickFalse)
        return(status);
      buff=AcquireImage((ImageInfo *) NULL);
      if (buff == (Image *) NULL)
        ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
      AttachBlob(buff->blob,info,length);
      formatIPTC(buff,image);
      (void) DetachBlob(buff->blob);
      buff=DestroyImage(buff);
      (void) CloseBlob(image);
      return(MagickTrue);
    }
  if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0)
    return(MagickFalse);
  if ((LocaleCompare(image_info->magick,"APP1") == 0) ||
      (LocaleCompare(image_info->magick,"EXIF") == 0) ||
      (LocaleCompare(image_info->magick,"XMP") == 0))
    {
      /*
        (void) Write APP1 image.
      */
      profile=GetImageProfile(image,image_info->magick);
      if (profile == (StringInfo *) NULL)
        ThrowWriterException(CoderError,"NoAPP1DataIsAvailable");
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
      if (status == MagickFalse)
        return(status);
      (void) WriteBlob(image,GetStringInfoLength(profile),
        GetStringInfoDatum(profile));
      (void) CloseBlob(image);
      return(MagickTrue);
    }
  if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
      (LocaleCompare(image_info->magick,"ICM") == 0))
    {
      /*
        Write ICM image.
      */
      profile=GetImageProfile(image,"icc");
      if (profile == (StringInfo *) NULL)
        ThrowWriterException(CoderError,"NoColorProfileIsAvailable");
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
      if (status == MagickFalse)
        return(status);
      (void) WriteBlob(image,GetStringInfoLength(profile),
        GetStringInfoDatum(profile));
      (void) CloseBlob(image);
      return(MagickTrue);
    }
  return(MagickFalse);
}

Project: ImageMagick6
File: meta.c

static int formatIPTCfromBuffer(Image *ofile, char *s, ssize_t len)
{
  char
    temp[MaxTextExtent];

  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 */

  while (len > 0)
  {
    c = *s++; len--;
    if (c == 0x1c)
      foundiptc = 1;
    else
      {
        if (foundiptc)
          return -1;
        else
          continue;
      }
    /*
      We found the 0x1c tag and now grab the dataset and record number tags.
    */
    c = *s++; len--;
    if (len < 0) return -1;
    dataset = (unsigned char) c;
    c = *s++; len--;
    if (len < 0) 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=(*s++);
    len--;
    if (len < 0)
      return(-1);
    if (c & (unsigned char) 0x80)
      return(0);
    else
      {
        s--;
        len++;
        taglen=readWordFromBuffer(&s, &len);
      }
    if (taglen < 0)
      return(-1);
    if (taglen > 65535)
      return(-1);
    /* make a buffer to hold the tag datand snag it from the input stream */
    str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MaxTextExtent),
      sizeof(*str));
    if (str == (unsigned char *) NULL)
      {
        printf("MemoryAllocationFailed");
        return 0;
      }
    for (tagindx=0; tagindx<taglen; tagindx++)
    {
      c = *s++; len--;
      if (len < 0)
        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,MaxTextExtent,"%d#%d#%s=",
        (unsigned int) dataset,(unsigned int) recnum, readable);
    else
      (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d=",
        (unsigned int) dataset,(unsigned int) recnum);
    (void) WriteBlobString(ofile,temp);
    formatString( ofile, (char *)str, taglen );
    str=(unsigned char *) RelinquishMagickMemory(str);

    tagsfound++;
  }
  return ((int) tagsfound);
}

Project: ImageMagick6
File: meta.c

static Image *ReadMETAImage(const ImageInfo *image_info,
  ExceptionInfo *exception)
{
  Image
    *buff,
    *image;

  MagickBooleanType
    status;

  StringInfo
    *profile;

  size_t
    length;

  void
    *blob;

  /*
    Open file containing binary metadata
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickSignature);
  image=AcquireImage(image_info);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  image->columns=1;
  image->rows=1;
  if (SetImageBackgroundColor(image) == MagickFalse)
    {
      InheritException(exception,&image->exception);
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  length=1;
  if (LocaleNCompare(image_info->magick,"8BIM",4) == 0)
    {
      /*
        Read 8BIM binary metadata.
      */
      buff=AcquireImage((ImageInfo *) NULL);
      if (buff == (Image *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
      if (blob == (unsigned char *) NULL)
        {
          buff=DestroyImage(buff);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
      AttachBlob(buff->blob,blob,length);
      if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
        {
          length=(size_t) parse8BIM(image, buff);
          if (length & 1)
            (void) WriteBlobByte(buff,0x0);
        }
      else if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
        {
          length=(size_t) parse8BIMW(image, buff);
          if (length & 1)
            (void) WriteBlobByte(buff,0x0);
        }
      else
        CopyBlob(image,buff);
      profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
        GetBlobSize(buff));
      if (profile == (StringInfo *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      status=SetImageProfile(image,"8bim",profile);
      profile=DestroyStringInfo(profile);
      if (status == MagickFalse)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      blob=DetachBlob(buff->blob);
      blob=(unsigned char *) RelinquishMagickMemory(blob);
      buff=DestroyImage(buff);
    }
  if (LocaleNCompare(image_info->magick,"APP1",4) == 0)
    {
      char
        name[MaxTextExtent];

      (void) FormatLocaleString(name,MaxTextExtent,"APP%d",1);
      buff=AcquireImage((ImageInfo *) NULL);
      if (buff == (Image *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
      if (blob == (unsigned char *) NULL)
        {
          buff=DestroyImage(buff);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
      AttachBlob(buff->blob,blob,length);
      if (LocaleCompare(image_info->magick,"APP1JPEG") == 0)
        {
          Image
            *iptc;

          int
            result;

          if (image_info->profile == (void *) NULL)
            {
              blob=DetachBlob(buff->blob);
              blob=RelinquishMagickMemory(blob);
              buff=DestroyImage(buff);
              ThrowReaderException(CoderError,"NoIPTCProfileAvailable");
            }
          profile=CloneStringInfo((StringInfo *) image_info->profile);
          iptc=AcquireImage((ImageInfo *) NULL);
          if (iptc == (Image *) NULL)
            {
              blob=DetachBlob(buff->blob);
              blob=RelinquishMagickMemory(blob);
              buff=DestroyImage(buff);
              ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
            }
          AttachBlob(iptc->blob,GetStringInfoDatum(profile),
            GetStringInfoLength(profile));
          result=jpeg_embed(image,buff,iptc);
          blob=DetachBlob(iptc->blob);
          blob=RelinquishMagickMemory(blob);
          iptc=DestroyImage(iptc);
          if (result == 0)
            {
              blob=DetachBlob(buff->blob);
              blob=RelinquishMagickMemory(blob);
              buff=DestroyImage(buff);
              ThrowReaderException(CoderError,"JPEGEmbeddingFailed");
            }
        }
      else
        CopyBlob(image,buff);
      profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
        GetBlobSize(buff));
      if (profile == (StringInfo *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      status=SetImageProfile(image,name,profile);
      profile=DestroyStringInfo(profile);
      if (status == MagickFalse)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      blob=DetachBlob(buff->blob);
      blob=RelinquishMagickMemory(blob);
      buff=DestroyImage(buff);
    }
  if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
      (LocaleCompare(image_info->magick,"ICM") == 0))
    {
      buff=AcquireImage((ImageInfo *) NULL);
      if (buff == (Image *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
      if (blob == (unsigned char *) NULL)
        {
          buff=DestroyImage(buff);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
      AttachBlob(buff->blob,blob,length);
      CopyBlob(image,buff);
      profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
        GetBlobSize(buff));
      if (profile == (StringInfo *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      (void) SetImageProfile(image,"icc",profile);
      profile=DestroyStringInfo(profile);
      blob=DetachBlob(buff->blob);
      blob=(unsigned char *) RelinquishMagickMemory(blob);
      buff=DestroyImage(buff);
    }
  if (LocaleCompare(image_info->magick,"IPTC") == 0)
    {
      buff=AcquireImage((ImageInfo *) NULL);
      if (buff == (Image *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
      if (blob == (unsigned char *) NULL)
        {
          buff=DestroyImage(buff);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
      AttachBlob(buff->blob,blob,length);
      CopyBlob(image,buff);
      profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
        GetBlobSize(buff));
      if (profile == (StringInfo *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      (void) SetImageProfile(image,"iptc",profile);
      profile=DestroyStringInfo(profile);
      blob=DetachBlob(buff->blob);
      blob=(unsigned char *) RelinquishMagickMemory(blob);
      buff=DestroyImage(buff);
    }
  if (LocaleCompare(image_info->magick,"XMP") == 0)
    {
      buff=AcquireImage((ImageInfo *) NULL);
      if (buff == (Image *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
      if (blob == (unsigned char *) NULL)
        {
          buff=DestroyImage(buff);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
      AttachBlob(buff->blob,blob,length);
      CopyBlob(image,buff);
      profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
        GetBlobSize(buff));
      if (profile == (StringInfo *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      (void) SetImageProfile(image,"xmp",profile);
      profile=DestroyStringInfo(profile);
      blob=DetachBlob(buff->blob);
      blob=(unsigned char *) RelinquishMagickMemory(blob);
      buff=DestroyImage(buff);
    }
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}

Project: ImageMagick6
File: meta.c

static int format8BIM(Image *ifile, Image *ofile)
{
  char
    temp[MaxTextExtent];

  unsigned int
    foundOSType;

  int
    ID,
    resCount,
    i,
    c;

  ssize_t
    count;

  unsigned char
    *PString,
    *str;

  resCount=0;
  foundOSType=0; /* found the OSType */
  (void) foundOSType;
  c=ReadBlobByte(ifile);
  while (c != EOF)
  {
    if (c == '8')
      {
        unsigned char
          buffer[5];

        buffer[0]=(unsigned char) c;
        for (i=1; i<4; i++)
        {
          c=ReadBlobByte(ifile);
          if (c == EOF)
            return(-1);
          buffer[i] = (unsigned char) c;
        }
        buffer[4]=0;
        if (strcmp((const char *)buffer, "8BIM") == 0)
          foundOSType=1;
        else
          continue;
      }
    else
      {
        c=ReadBlobByte(ifile);
        continue;
      }
    /*
      We found the OSType (8BIM) and now grab the ID, PString, and Size fields.
    */
    ID=ReadBlobMSBSignedShort(ifile);
    if (ID < 0)
      return(-1);
    {
      unsigned char
        plen;

      c=ReadBlobByte(ifile);
      if (c == EOF)
        return(-1);
      plen = (unsigned char) c;
      PString=(unsigned char *) AcquireQuantumMemory((size_t) (plen+
        MaxTextExtent),sizeof(*PString));
      if (PString == (unsigned char *) NULL)
        {
          printf("MemoryAllocationFailed");
          return 0;
        }
      for (i=0; i<plen; i++)
      {
        c=ReadBlobByte(ifile);
        if (c == EOF)
          {
            PString=(unsigned char *) RelinquishMagickMemory(PString);
            return(-1);
          }
        PString[i] = (unsigned char) c;
      }
      PString[ plen ] = 0;
      if ((plen & 0x01) == 0)
      {
        c=ReadBlobByte(ifile);
        if (c == EOF)
          {
            PString=(unsigned char *) RelinquishMagickMemory(PString);
            return(-1);
          }
      }
    }
    count=(ssize_t) ReadBlobMSBSignedLong(ifile);
    if (count < 0)
      {
        PString=(unsigned char *) RelinquishMagickMemory(PString);
        return(-1);
      }
    /* make a buffer to hold the datand snag it from the input stream */
    str=(unsigned char *) AcquireQuantumMemory((size_t) count,sizeof(*str));
    if (str == (unsigned char *) NULL)
      {
        PString=(unsigned char *) RelinquishMagickMemory(PString);
        printf("MemoryAllocationFailed");
        return 0;
      }
    for (i=0; i < (ssize_t) count; i++)
    {
      c=ReadBlobByte(ifile);
      if (c == EOF)
        {
          PString=(unsigned char *) RelinquishMagickMemory(PString);
          return(-1);
        }
      str[i]=(unsigned char) c;
    }

    /* we currently skip thumbnails, since it does not make
     * any sense preserving them in a real world application
     */
    if (ID != THUMBNAIL_ID)
      {
        /* now finish up by formatting this binary data into
         * ASCII equivalent
         */
        if (strlen((const char *)PString) > 0)
          (void) FormatLocaleString(temp,MaxTextExtent,"8BIM#%d#%s=",ID,
            PString);
        else
          (void) FormatLocaleString(temp,MaxTextExtent,"8BIM#%d=",ID);
        (void) WriteBlobString(ofile,temp);
        if (ID == IPTC_ID)
          {
            formatString(ofile, "IPTC", 4);
            formatIPTCfromBuffer(ofile, (char *)str, (ssize_t) count);
          }
        else
          formatString(ofile, (char *)str, (ssize_t) count);
      }
    str=(unsigned char *) RelinquishMagickMemory(str);
    PString=(unsigned char *) RelinquishMagickMemory(PString);
    resCount++;
    c=ReadBlobByte(ifile);
  }
  return resCount;
}