Commit message: None
CVE: CVE-2017-13658
CWE: CWE-617
Vulnerability description: In ImageMagick before 6.9.9-3 and 7.x before 7.0.6-3, there is a missing NULL check in the ReadMATImage function in coders/mat.c, leading to a denial of service (assertion failure and application exit) in the DestroyImageInfo function in MagickCore/image.c.

Project: ImageMagick
File: mat.c

static void FixSignedValues(PixelPacket *q, int y)
{
  while(y-->0)
  {
     /* Please note that negative values will overflow
        Q=8; QuantumRange=255: <0;127> + 127+1 = <128; 255>
           <-1;-128> + 127+1 = <0; 127> */
    SetPixelRed(q,GetPixelRed(q)+QuantumRange/2+1);
    SetPixelGreen(q,GetPixelGreen(q)+QuantumRange/2+1);
    SetPixelBlue(q,GetPixelBlue(q)+QuantumRange/2+1);
    q++;
  }
}

Project: ImageMagick
File: mat.c

static void InsertComplexDoubleRow(double *p, int y, Image * image, double MinVal,
                                  double MaxVal)
{
  ExceptionInfo
    *exception;

  double f;
  int x;
  register PixelPacket *q;

  if (MinVal == 0)
    MinVal = -1;
  if (MaxVal == 0)
    MaxVal = 1;

  exception=(&image->exception);
  q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
  if (q == (PixelPacket *) NULL)
    return;
  for (x = 0; x < (ssize_t) image->columns; x++)
  {
    if (*p > 0)
    {
      f = (*p / MaxVal) * (QuantumRange - GetPixelRed(q));
      if (f + GetPixelRed(q) > QuantumRange)
        SetPixelRed(q,QuantumRange);
      else
        SetPixelRed(q,GetPixelRed(q)+(int) f);
      if ((int) f / 2.0 > GetPixelGreen(q))
        {
          SetPixelGreen(q,0);
          SetPixelBlue(q,0);
        }
      else
        {
          SetPixelBlue(q,GetPixelBlue(q)-(int) (f/2.0));
          SetPixelGreen(q,GetPixelBlue(q));
        }
    }
    if (*p < 0)
    {
      f = (*p / MaxVal) * (QuantumRange - GetPixelBlue(q));
      if (f + GetPixelBlue(q) > QuantumRange)
        SetPixelBlue(q,QuantumRange);
      else
        SetPixelBlue(q,GetPixelBlue(q)+(int) f);
      if ((int) f / 2.0 > q->green)
        {
          SetPixelRed(q,0);
          SetPixelGreen(q,0);
        }
      else
        {
          SetPixelRed(q,GetPixelRed(q)-(int) (f/2.0));
          SetPixelGreen(q,GetPixelRed(q));
        }
    }
    p++;
    q++;
  }
  if (!SyncAuthenticPixels(image,exception))
    return;
  return;
}

Project: ImageMagick
File: mat.c

static void InsertComplexFloatRow(float *p, int y, Image * image, double MinVal,
                                  double MaxVal)
{
  ExceptionInfo
    *exception;

  double f;
  int x;
  register PixelPacket *q;

  if (MinVal == 0)
    MinVal = -1;
  if (MaxVal == 0)
    MaxVal = 1;

  exception=(&image->exception);
  q = QueueAuthenticPixels(image, 0, y, image->columns, 1,exception);
  if (q == (PixelPacket *) NULL)
    return;
  for (x = 0; x < (ssize_t) image->columns; x++)
  {
    if (*p > 0)
    {
      f = (*p / MaxVal) * (QuantumRange - GetPixelRed(q));
      if (f + GetPixelRed(q) > QuantumRange)
        SetPixelRed(q,QuantumRange);
      else
        SetPixelRed(q,GetPixelRed(q)+(int) f);
      if ((int) f / 2.0 > GetPixelGreen(q))
        {
          SetPixelGreen(q,0);
          SetPixelBlue(q,0);
        }
      else
        {
          SetPixelBlue(q,GetPixelBlue(q)-(int) (f/2.0));
          SetPixelGreen(q,GetPixelBlue(q));
        }
    }
    if (*p < 0)
    {
      f = (*p / MaxVal) * (QuantumRange - GetPixelBlue(q));
      if (f + GetPixelBlue(q) > QuantumRange)
        SetPixelBlue(q,QuantumRange);
      else
        SetPixelBlue(q,GetPixelBlue(q)+(int) f);
      if ((int) f / 2.0 > q->green)
        {
          SetPixelGreen(q,0);
          SetPixelRed(q,0);
        }
      else
        {
          SetPixelRed(q,GetPixelRed(q)-(int) (f/2.0));
          SetPixelGreen(q,GetPixelRed(q));
        }
    }
    p++;
    q++;
  }
  if (!SyncAuthenticPixels(image,exception))
    return;
  return;
}

Project: ImageMagick
File: mat.c

static void ReadBlobDoublesLSB(Image * image, size_t len, double *data)
{
  while (len >= 8)
  {
    *data++ = ReadBlobDouble(image);
    len -= sizeof(double);
  }
  if (len > 0)
    (void) SeekBlob(image, len, SEEK_CUR);
}

Project: ImageMagick
File: mat.c

static void ReadBlobFloatsLSB(Image * image, size_t len, float *data)
{
  while (len >= 4)
  {
    *data++ = ReadBlobFloat(image);
    len -= sizeof(float);
  }
  if (len > 0)
    (void) SeekBlob(image, len, SEEK_CUR);
}

Project: ImageMagick
File: mat.c

static void ReadBlobFloatsMSB(Image * image, size_t len, float *data)
{
  while (len >= 4)
  {
    *data++ = ReadBlobFloat(image);
    len -= sizeof(float);
  }
  if (len > 0)
    (void) SeekBlob(image, len, SEEK_CUR);
}

Project: ImageMagick

static Image *ReadMATImageV4(const ImageInfo *image_info,Image *image,
  ExceptionInfo *exception)
{
  typedef struct {
    unsigned char Type[4];
    unsigned int nRows;
    unsigned int nCols;
    unsigned int imagf;
    unsigned int nameLen;
  } MAT4_HDR;

  long
    ldblk;

  EndianType
    endian;

  Image
    *rotate_image;

  MagickBooleanType
    status;

  MAT4_HDR
    HDR;

  QuantumInfo
    *quantum_info;

  QuantumFormatType
    format_type;

  register ssize_t
    i;

  ssize_t
    count,
    y;

  unsigned char
    *pixels;

  unsigned int
    depth;

  (void) SeekBlob(image,0,SEEK_SET);
  while (EOFBlob(image) != MagickFalse)
  {
    /*
      Object parser.
    */
    ldblk=ReadBlobLSBLong(image);
    if (EOFBlob(image) != MagickFalse)
      break;
    if ((ldblk > 9999) || (ldblk < 0))
      break;
    HDR.Type[3]=ldblk % 10; ldblk /= 10;  /* T digit */
    HDR.Type[2]=ldblk % 10; ldblk /= 10;  /* P digit */
    HDR.Type[1]=ldblk % 10; ldblk /= 10;  /* O digit */
    HDR.Type[0]=ldblk;  /* M digit */
    if (HDR.Type[3] != 0)
      break;    /* Data format */
    if (HDR.Type[2] != 0)
      break;    /* Always 0 */
    if (HDR.Type[0] == 0)
      {
        HDR.nRows=ReadBlobLSBLong(image);
        HDR.nCols=ReadBlobLSBLong(image);
        HDR.imagf=ReadBlobLSBLong(image);
        HDR.nameLen=ReadBlobLSBLong(image);
        endian=LSBEndian;
      }
    else
      {
        HDR.nRows=ReadBlobMSBLong(image);
        HDR.nCols=ReadBlobMSBLong(image);
        HDR.imagf=ReadBlobMSBLong(image);
        HDR.nameLen=ReadBlobMSBLong(image);
        endian=MSBEndian;
      }
    if ((HDR.imagf !=0) && (HDR.imagf !=1))
      break;
    if (HDR.nameLen > 0xFFFF)
      break;
    for (i=0; i < (ssize_t) HDR.nameLen; i++)
    {
      int
        byte;

      /*
        Skip matrix name.
      */
      byte=ReadBlobByte(image);
      if (byte == EOF)
        {
          ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
            image->filename);
          break;
        }
    }
    image->columns=(size_t) HDR.nRows;
    image->rows=(size_t) HDR.nCols;
    SetImageColorspace(image,GRAYColorspace);
    if (image_info->ping != MagickFalse)
      {
        Swap(image->columns,image->rows);
        return(image);
      }
    status=SetImageExtent(image,image->columns,image->rows);
    if (status == MagickFalse)
      return((Image *) NULL);
    quantum_info=AcquireQuantumInfo(image_info,image);
    if (quantum_info == (QuantumInfo *) NULL)
      return((Image *) NULL);
    switch(HDR.Type[1])
    {
      case 0:
        format_type=FloatingPointQuantumFormat;
        depth=64;
        break;
      case 1:
        format_type=FloatingPointQuantumFormat;
        depth=32;
        break;
      case 2:
        format_type=UnsignedQuantumFormat;
        depth=16;
        break;
      case 3:
        format_type=SignedQuantumFormat;
        depth=16;
        break;
      case 4:
        format_type=UnsignedQuantumFormat;
        depth=8;
        break;
      default:
        format_type=UnsignedQuantumFormat;
        depth=8;
        break;
    }
    image->depth=depth;
    if (HDR.Type[0] != 0)
      SetQuantumEndian(image,quantum_info,MSBEndian);
    status=SetQuantumFormat(image,quantum_info,format_type);
    status=SetQuantumDepth(image,quantum_info,depth);
    status=SetQuantumEndian(image,quantum_info,endian);
    SetQuantumScale(quantum_info,1.0);
    pixels=(unsigned char *) GetQuantumPixels(quantum_info);
    for (y=0; y < (ssize_t) image->rows; y++)
    {
      register PixelPacket
        *magick_restrict q;

      count=ReadBlob(image,depth/8*image->columns,(unsigned char *) pixels);
      if (count == -1)
        break;
      q=QueueAuthenticPixels(image,0,image->rows-y-1,image->columns,1,
        exception);
      if (q == (PixelPacket *) NULL)
        break;
      (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
        GrayQuantum,pixels,exception);
      if ((HDR.Type[1] == 2) || (HDR.Type[1] == 3))
        FixSignedValues(q,image->columns);
      if (SyncAuthenticPixels(image,exception) == MagickFalse)
        break;
      if (image->previous == (Image *) NULL)
        {
          status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
            image->rows);
          if (status == MagickFalse)
            break;
        }
    }
    if (HDR.imagf == 1)
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        /*
          Read complex pixels.
        */
        count=ReadBlob(image,depth/8*image->columns,(unsigned char *) pixels);
        if (count == -1)
          break;
        if (HDR.Type[1] == 0)
          InsertComplexDoubleRow((double *) pixels,y,image,0,0);
        else
          InsertComplexFloatRow((float *) pixels,y,image,0,0);
      }
    quantum_info=DestroyQuantumInfo(quantum_info);
    rotate_image=RotateImage(image,90.0,exception);
    if (rotate_image != (Image *) NULL)
      {
        image=DestroyImage(image);
        image=rotate_image;
      }
    if (EOFBlob(image) != MagickFalse)
      {
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
        break;
      }
    /*
      Proceed to next image.
    */
    if (image_info->number_scenes != 0)
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    /*
      Allocate next image structure.
    */
    AcquireNextImage(image_info,image);
    if (GetNextImageInList(image) == (Image *) NULL)
      {
        image=DestroyImageList(image);
        return((Image *) NULL);
      }
    image=SyncNextImageInList(image);
    status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
      GetBlobSize(image));
    if (status == MagickFalse)
      break;
  }
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}

Project: ImageMagick
File: mat.c

ModuleExport size_t RegisterMATImage(void)
{
  MagickInfo
    *entry;

  entry=SetMagickInfo("MAT");
  entry->decoder=(DecodeImageHandler *) ReadMATImage;
  entry->encoder=(EncodeImageHandler *) WriteMATImage;
  entry->blob_support=MagickFalse;
  entry->seekable_stream=MagickTrue;
  entry->description=AcquireString("MATLAB level 5 image format");
  entry->module=AcquireString("MAT");
  (void) RegisterMagickInfo(entry);
  return(MagickImageCoderSignature);
}

Project: ImageMagick
File: mat.c

static void RelinquishZIPMemory(voidpf context,voidpf memory)
{
  (void) context;
  memory=RelinquishMagickMemory(memory);
}

Project: ImageMagick
File: mat.c

ModuleExport void UnregisterMATImage(void)
{
  (void) UnregisterMagickInfo("MAT");
}

Project: ImageMagick

static MagickBooleanType WriteMATImage(const ImageInfo *image_info,Image *image)
{
  char
    MATLAB_HDR[0x80];

  ExceptionInfo
    *exception;

  MagickBooleanType
    status;

  MagickOffsetType
    scene;

  struct tm
    local_time;

  time_t
    current_time;

  /*
    Open output image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"enter MAT");
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
  if (status == MagickFalse)
    return(MagickFalse);
  image->depth=8;

  current_time=time((time_t *) NULL);
#if defined(MAGICKCORE_HAVE_LOCALTIME_R)
  (void) localtime_r(&current_time,&local_time);
#else
  (void) memcpy(&local_time,localtime(&current_time),sizeof(local_time));
#endif
  (void) memset(MATLAB_HDR,' ',MagickMin(sizeof(MATLAB_HDR),124));
  FormatLocaleString(MATLAB_HDR,sizeof(MATLAB_HDR),
    "MATLAB 5.0 MAT-file, Platform: %s, Created on: %s %s %2d %2d:%2d:%2d %d",
    OsDesc,DayOfWTab[local_time.tm_wday],MonthsTab[local_time.tm_mon],
    local_time.tm_mday,local_time.tm_hour,local_time.tm_min,
    local_time.tm_sec,local_time.tm_year+1900);
  MATLAB_HDR[0x7C]=0;
  MATLAB_HDR[0x7D]=1;
  MATLAB_HDR[0x7E]='I';
  MATLAB_HDR[0x7F]='M';
  (void) WriteBlob(image,sizeof(MATLAB_HDR),(unsigned char *) MATLAB_HDR);
  scene=0;
  do
  {
    char
      padding;

    MagickBooleanType
      is_gray;

    QuantumInfo
      *quantum_info;

    size_t
      data_size;

    unsigned char
      *pixels;

    unsigned int
      z;

    (void) TransformImageColorspace(image,sRGBColorspace);
    is_gray=SetImageGray(image,&image->exception);
    z=(is_gray != MagickFalse) ? 0 : 3;

    /*
      Store MAT header.
    */
    data_size=image->rows*image->columns;
    if (is_gray == MagickFalse)
      data_size*=3;
    padding=((unsigned char)(data_size-1) & 0x7) ^ 0x7;

    (void) WriteBlobLSBLong(image,miMATRIX);
    (void) WriteBlobLSBLong(image,(unsigned int) data_size+padding+
      ((is_gray != MagickFalse) ? 48 : 56));
    (void) WriteBlobLSBLong(image,0x6); /* 0x88 */
    (void) WriteBlobLSBLong(image,0x8); /* 0x8C */
    (void) WriteBlobLSBLong(image,0x6); /* 0x90 */
    (void) WriteBlobLSBLong(image,0);
    (void) WriteBlobLSBLong(image,0x5); /* 0x98 */
    (void) WriteBlobLSBLong(image,(is_gray != MagickFalse) ? 0x8 : 0xC); /* 0x9C - DimFlag */
    (void) WriteBlobLSBLong(image,(unsigned int) image->rows);    /* x: 0xA0 */
    (void) WriteBlobLSBLong(image,(unsigned int) image->columns); /* y: 0xA4 */
    if (is_gray == MagickFalse)
      {
        (void) WriteBlobLSBLong(image,3); /* z: 0xA8 */
        (void) WriteBlobLSBLong(image,0);
      }
    (void) WriteBlobLSBShort(image,1);  /* 0xB0 */
    (void) WriteBlobLSBShort(image,1);  /* 0xB2 */
    (void) WriteBlobLSBLong(image,'M'); /* 0xB4 */
    (void) WriteBlobLSBLong(image,0x2); /* 0xB8 */
    (void) WriteBlobLSBLong(image,(unsigned int) data_size); /* 0xBC */

    /*
      Store image data.
    */
    exception=(&image->exception);
    quantum_info=AcquireQuantumInfo(image_info,image);
    if (quantum_info == (QuantumInfo *) NULL)
      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    pixels=GetQuantumPixels(quantum_info);
    do
    {
      const PixelPacket
        *p;

      ssize_t
        y;

      for (y=0; y < (ssize_t)image->columns; y++)
      {
        p=GetVirtualPixels(image,y,0,1,image->rows,&image->exception);
        if (p == (const PixelPacket *) NULL)
          break;
        (void) ExportQuantumPixels(image,(const CacheView *) NULL,quantum_info,
          z2qtype[z],pixels,exception);
        (void) WriteBlob(image,image->rows,pixels);
      }
      if (!SyncAuthenticPixels(image,exception))
        break;
    } while (z-- >= 2);
    while (padding-- > 0)
      (void) WriteBlobByte(image,0);
    quantum_info=DestroyQuantumInfo(quantum_info);
    if (GetNextImageInList(image) == (Image *) NULL)
      break;
    image=SyncNextImageInList(image);
    status=SetImageProgress(image,SaveImagesTag,scene++,
      GetImageListLength(image));
    if (status == MagickFalse)
      break;
  } while (image_info->adjoin != MagickFalse);
  (void) CloseBlob(image);
  return(status);
}

Project: ImageMagick

static Image *decompress_block(Image *orig, unsigned int *Size, ImageInfo *clone_info, ExceptionInfo *exception)
{

Image *image2;
void *cache_block, *decompress_block;
z_stream zip_info;
FILE *mat_file;
size_t magick_size;
size_t extent;
int file;

int status;
int zip_status;
ssize_t TotalSize = 0;

  if(clone_info==NULL) return NULL;
  if(clone_info->file)    /* Close file opened from previous transaction. */
  {
    fclose(clone_info->file);
    clone_info->file = NULL;
    (void) remove_utf8(clone_info->filename);
  }

  cache_block = AcquireQuantumMemory((size_t)(*Size< 16384) ? *Size: 16384,sizeof(unsigned char *));
  if(cache_block==NULL) return NULL;
  decompress_block = AcquireQuantumMemory((size_t)(4096),sizeof(unsigned char *));
  if(decompress_block==NULL)
  {
    RelinquishMagickMemory(cache_block);
    return NULL;
  }

  mat_file=0;
  file = AcquireUniqueFileResource(clone_info->filename);
  if (file != -1)
    mat_file = fdopen(file,"w");
  if(!mat_file)
  {
    RelinquishMagickMemory(cache_block);
    RelinquishMagickMemory(decompress_block);
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Cannot create file stream for decompressed image");
    return NULL;
  }

  zip_info.zalloc=AcquireZIPMemory;
  zip_info.zfree=RelinquishZIPMemory;
  zip_info.opaque = (voidpf) NULL;
  zip_status = inflateInit(&zip_info);
  if (zip_status != Z_OK)
    {
      RelinquishMagickMemory(cache_block);
      RelinquishMagickMemory(decompress_block);
      (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
        "UnableToUncompressImage","`%s'",clone_info->filename);
      (void) fclose(mat_file);
      RelinquishUniqueFileResource(clone_info->filename);
      return NULL;
    }
  /* zip_info.next_out = 8*4;*/

  zip_info.avail_in = 0;
  zip_info.total_out = 0;
  while(*Size>0 && !EOFBlob(orig))
  {
    magick_size = ReadBlob(orig, (*Size < 16384) ? *Size : 16384, (unsigned char *) cache_block);
    zip_info.next_in = (Bytef *) cache_block;
    zip_info.avail_in = (uInt) magick_size;

    while(zip_info.avail_in>0)
    {
      zip_info.avail_out = 4096;
      zip_info.next_out = (Bytef *) decompress_block;
      zip_status = inflate(&zip_info,Z_NO_FLUSH);
			if ((zip_status != Z_OK) && (zip_status != Z_STREAM_END))
        break;
      extent=fwrite(decompress_block, 4096-zip_info.avail_out, 1, mat_file);
      (void) extent;
      TotalSize += 4096-zip_info.avail_out;

      if(zip_status == Z_STREAM_END) goto DblBreak;
    }
 	  if ((zip_status != Z_OK) && (zip_status != Z_STREAM_END))
      break;

    *Size -= magick_size;
  }
DblBreak:

  inflateEnd(&zip_info);
  (void)fclose(mat_file);
  RelinquishMagickMemory(cache_block);
  RelinquishMagickMemory(decompress_block);
  *Size = TotalSize;

  if((clone_info->file=fopen(clone_info->filename,"rb"))==NULL) goto UnlinkFile;
  if( (image2 = AcquireImage(clone_info))==NULL ) goto EraseFile;
  status = OpenBlob(clone_info,image2,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
  {
    DeleteImageFromList(&image2);
EraseFile:
    fclose(clone_info->file);
    clone_info->file = NULL;
UnlinkFile:
    RelinquishUniqueFileResource(clone_info->filename);
    return NULL;
  }

  return image2;
}