Commit message: https://github.com/ImageMagick/ImageMagick/issues/85
CVE: CVE-2017-16546
CWE: CWE-119
Vulnerability description: The ReadWPGImage function in coders/wpg.c in ImageMagick 7.0.7-9 does not properly validate the colormap index in a WPG palette, which allows remote attackers to cause a denial of service (use of uninitialized data or invalid memory allocation) or possibly have unspecified other impact via a malformed WPG file.

Project: ImageMagick
File: wpg.c

static Image *ExtractPostscript(Image *image,const ImageInfo *image_info,
  MagickOffsetType PS_Offset,ssize_t PS_Size,ExceptionInfo *exception)
{
  char
    postscript_file[MagickPathExtent];

  const MagicInfo
    *magic_info;    

  FILE
    *ps_file;

  ImageInfo
    *clone_info;
    
  Image
    *image2;
    
  unsigned char
    magick[2*MagickPathExtent];    
    

  if ((clone_info=CloneImageInfo(image_info)) == NULL)
    return(image);
  clone_info->blob=(void *) NULL;
  clone_info->length=0;

  /* Obtain temporary file */
  (void) AcquireUniqueFilename(postscript_file);
  ps_file=fopen_utf8(postscript_file,"wb");
  if (ps_file == (FILE *) NULL)
    goto FINISH;

  /* Copy postscript to temporary file */
  (void) SeekBlob(image,PS_Offset,SEEK_SET);
  (void) ReadBlob(image, 2*MagickPathExtent, magick);
  
  (void) SeekBlob(image,PS_Offset,SEEK_SET);
  while(PS_Size-- > 0)
    {
      (void) fputc(ReadBlobByte(image),ps_file);
    }
  (void) fclose(ps_file);
  
    /* Detect file format - Check magic.mgk configuration file. */
  magic_info=GetMagicInfo(magick,2*MagickPathExtent,exception);
  if(magic_info == (const MagicInfo *) NULL) goto FINISH_UNL;
  /*     printf("Detected:%s  \n",magic_info->name); */
  if(exception->severity != UndefinedException) goto FINISH_UNL;     
  if(magic_info->name == (char *) NULL) goto FINISH_UNL;
    
  (void) strncpy(clone_info->magick,magic_info->name,MagickPathExtent-1);
  
    /* Read nested image */
  /*FormatString(clone_info->filename,"%s:%s",magic_info->name,postscript_file);*/
  FormatLocaleString(clone_info->filename,MagickPathExtent,"%s",postscript_file);
  image2=ReadImage(clone_info,exception);

  if (!image2)
    goto FINISH_UNL;

  /*
    Replace current image with new image while copying base image
    attributes.
  */
  (void) CopyMagickString(image2->filename,image->filename,MagickPathExtent);
  (void) CopyMagickString(image2->magick_filename,image->magick_filename,MagickPathExtent);
  (void) CopyMagickString(image2->magick,image->magick,MagickPathExtent);
  image2->depth=image->depth;
  DestroyBlob(image2);
  image2->blob=ReferenceBlob(image->blob);

  if ((image->rows == 0) || (image->columns == 0))
    DeleteImageFromList(&image);

  AppendImageToList(&image,image2);

 FINISH_UNL:    
  (void) RelinquishUniqueFileResource(postscript_file);
 FINISH:
  DestroyImageInfo(clone_info);
  return(image);
}

Project: ImageMagick
File: wpg.c

static MagickBooleanType InsertRow(Image *image,unsigned char *p,ssize_t y,
  int bpp,ExceptionInfo *exception)
{
  int
    bit;

  Quantum
    index;

  register Quantum
    *q;

  ssize_t
    x;

  q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
  if (q == (Quantum *) NULL)
    return(MagickFalse);
  switch (bpp)
    {
    case 1:  /* Convert bitmap scanline. */
      {
        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
        {
          for (bit=0; bit < 8; bit++)
          {
            index=((*p) & (0x80 >> bit) ? 0x01 : 0x00);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
          }
          p++;
        }
        if ((image->columns % 8) != 0)
          {
            for (bit=0; bit < (ssize_t) (image->columns % 8); bit++)
            {
              index=((*p) & (0x80 >> bit) ? 0x01 : 0x00);
              SetPixelIndex(image,index,q);
              SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
              q+=GetPixelChannels(image);
            }
            p++;
          }
        break;
      }
    case 2:  /* Convert PseudoColor scanline. */
      {
        for (x=0; x < ((ssize_t) image->columns-3); x+=4)
        {
            index=ConstrainColormapIndex(image,(*p >> 6) & 0x3,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
            index=ConstrainColormapIndex(image,(*p >> 4) & 0x3,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
            index=ConstrainColormapIndex(image,(*p >> 2) & 0x3,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
            index=ConstrainColormapIndex(image,(*p) & 0x3,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
            p++;
        }
       if ((image->columns % 4) != 0)
          {
            index=ConstrainColormapIndex(image,(*p >> 6) & 0x3,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
            if ((image->columns % 4) > 1)
              {
                index=ConstrainColormapIndex(image,(*p >> 4) & 0x3,exception);
                SetPixelIndex(image,index,q);
                SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
                q+=GetPixelChannels(image);
                if ((image->columns % 4) > 2)
                  {
                    index=ConstrainColormapIndex(image,(*p >> 2) & 0x3,
                      exception);
                    SetPixelIndex(image,index,q);
                    SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
                    q+=GetPixelChannels(image);
                  }
              }
            p++;
          }
        break;
      }
 
    case 4:  /* Convert PseudoColor scanline. */
      {
        for (x=0; x < ((ssize_t) image->columns-1); x+=2)
          { 
            index=ConstrainColormapIndex(image,(*p >> 4) & 0x0f,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
            index=ConstrainColormapIndex(image,(*p) & 0x0f,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            p++;
            q+=GetPixelChannels(image);
          }
        if ((image->columns % 2) != 0)
          {
            index=ConstrainColormapIndex(image,(*p >> 4) & 0x0f,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            p++;
            q+=GetPixelChannels(image);
          }
        break;
      }
    case 8: /* Convert PseudoColor scanline. */
      {
        for (x=0; x < (ssize_t) image->columns; x++)
          {
            index=ConstrainColormapIndex(image,*p,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            p++;
            q+=GetPixelChannels(image);
          }
      }
      break;
     
    case 24:     /*  Convert DirectColor scanline.  */
      for (x=0; x < (ssize_t) image->columns; x++)
        {
          SetPixelRed(image,ScaleCharToQuantum(*p++),q);
          SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
          SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
          q+=GetPixelChannels(image);
        }
      break;
    }
  if (!SyncAuthenticPixels(image,exception))
    return(MagickFalse);
  return(MagickTrue);
}

Project: ImageMagick
File: wpg.c

static unsigned LoadWPG2Flags(Image *image,char Precision,float *Angle,tCTM *CTM)
{
const unsigned char TPR=1,TRN=2,SKW=4,SCL=8,ROT=0x10,OID=0x20,LCK=0x80;
ssize_t x;
unsigned DenX;
unsigned Flags;

 (void) memset(*CTM,0,sizeof(*CTM));     /*CTM.erase();CTM.resize(3,3);*/
 (*CTM)[0][0]=1;
 (*CTM)[1][1]=1;
 (*CTM)[2][2]=1;

 Flags=ReadBlobLSBShort(image);
 if(Flags & LCK) (void) ReadBlobLSBLong(image);  /*Edit lock*/
 if(Flags & OID)
  {
  if(Precision==0)
    {(void) ReadBlobLSBShort(image);}  /*ObjectID*/
  else
    {(void) ReadBlobLSBLong(image);}  /*ObjectID (Double precision)*/
  }
 if(Flags & ROT)
  {
  x=ReadBlobLSBLong(image);  /*Rot Angle*/
  if(Angle) *Angle=x/65536.0;
  }
 if(Flags & (ROT|SCL))
  {
  x=ReadBlobLSBLong(image);  /*Sx*cos()*/
  (*CTM)[0][0] = (float)x/0x10000;
  x=ReadBlobLSBLong(image);  /*Sy*cos()*/
  (*CTM)[1][1] = (float)x/0x10000;
  }
 if(Flags & (ROT|SKW))
  {
  x=ReadBlobLSBLong(image);       /*Kx*sin()*/
  (*CTM)[1][0] = (float)x/0x10000;
  x=ReadBlobLSBLong(image);       /*Ky*sin()*/
  (*CTM)[0][1] = (float)x/0x10000;
  }
 if(Flags & TRN)
  {
  x=ReadBlobLSBLong(image); DenX=ReadBlobLSBShort(image);  /*Tx*/
        if(x>=0) (*CTM)[0][2] = (float)x+(float)DenX/0x10000;
            else (*CTM)[0][2] = (float)x-(float)DenX/0x10000;
  x=ReadBlobLSBLong(image); DenX=ReadBlobLSBShort(image);  /*Ty*/
  (*CTM)[1][2]=(float)x + ((x>=0)?1:-1)*(float)DenX/0x10000;
        if(x>=0) (*CTM)[1][2] = (float)x+(float)DenX/0x10000;
            else (*CTM)[1][2] = (float)x-(float)DenX/0x10000;
  }
 if(Flags & TPR)
  {
  x=ReadBlobLSBShort(image); DenX=ReadBlobLSBShort(image);  /*Px*/
  (*CTM)[2][0] = x + (float)DenX/0x10000;;
  x=ReadBlobLSBShort(image);  DenX=ReadBlobLSBShort(image); /*Py*/
  (*CTM)[2][1] = x + (float)DenX/0x10000;
  }
 return(Flags);
}

Project: ImageMagick
File: wpg.c

static void Rd_WP_DWORD(Image *image,size_t *d)
{
  unsigned char
    b;

  b=ReadBlobByte(image);
  *d=b;
  if (b < 0xFFU)
    return;
  b=ReadBlobByte(image);
  *d=(size_t) b;
  b=ReadBlobByte(image);
  *d+=(size_t) b*256l;
  if (*d < 0x8000)
    return;
  *d=(*d & 0x7FFF) << 16;
  b=ReadBlobByte(image);
  *d+=(size_t) b;
  b=ReadBlobByte(image);
  *d+=(size_t) b*256l;
  return;
}

Project: ImageMagick
File: wpg.c

ModuleExport size_t RegisterWPGImage(void)
{
  MagickInfo
    *entry;

  entry=AcquireMagickInfo("WPG","WPG","Word Perfect Graphics");
  entry->decoder=(DecodeImageHandler *) ReadWPGImage;
  entry->magick=(IsImageFormatHandler *) IsWPG;
  entry->flags|=CoderDecoderSeekableStreamFlag;
  (void) RegisterMagickInfo(entry);
  return(MagickImageCoderSignature);
}

Project: ImageMagick
File: wpg.c

static int UnpackWPG2Raster(Image *image,int bpp,ExceptionInfo *exception)
{
  int
    RunCount,
    XorMe = 0;

  size_t
    x,
    y;

  ssize_t
    i,
    ldblk;

  unsigned int
    SampleSize=1;

  unsigned char
    bbuf,
    *BImgBuff,
    SampleBuffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

  x=0;
  y=0;
  ldblk=(ssize_t) ((bpp*image->columns+7)/8);
  BImgBuff=(unsigned char *) AcquireQuantumMemory((size_t) ldblk,
    sizeof(*BImgBuff));
  if(BImgBuff==NULL)
    return(-2);

  while( y< image->rows)
    {
      bbuf=ReadBlobByte(image);

      switch(bbuf)
        {
        case 0x7D:
          SampleSize=ReadBlobByte(image);  /* DSZ */
          if(SampleSize>8)
            {
              BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
              return(-2);
            }
          if(SampleSize<1)
            {
              BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
              return(-2);
            }
          break;
        case 0x7E:
          (void) FormatLocaleFile(stderr,
            "\nUnsupported WPG token XOR, please report!");
          XorMe=!XorMe;
          break;
        case 0x7F:
          RunCount=ReadBlobByte(image);   /* BLK */
          if (RunCount < 0)
            break;
          for(i=0; i < SampleSize*(RunCount+1); i++)
            {
              InsertByte6(0);
            }
          break;
        case 0xFD:
          RunCount=ReadBlobByte(image);   /* EXT */
          if (RunCount < 0)
            break;
          for(i=0; i<= RunCount;i++)
            for(bbuf=0; bbuf < SampleSize; bbuf++)
              InsertByte6(SampleBuffer[bbuf]);          
          break;
        case 0xFE:
          RunCount=ReadBlobByte(image);  /* RST */
          if (RunCount < 0)
            break;
          if(x!=0)
            {
              (void) FormatLocaleFile(stderr,
                "\nUnsupported WPG2 unaligned token RST x=%.20g, please report!\n"
                ,(double) x);
              BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
              return(-3);
            }
          {
            /* duplicate the previous row RunCount x */
            for(i=0;i<=RunCount;i++)
              {      
                if (InsertRow(image,BImgBuff,(ssize_t) (image->rows >= y ? y : image->rows-1),
                          bpp,exception) != MagickFalse)
                  y++;
              }    
          }
          break;
        case 0xFF:
          RunCount=ReadBlobByte(image);   /* WHT */
          if (RunCount < 0)
            break;
          for(i=0; i < SampleSize*(RunCount+1); i++)
            {
              InsertByte6(0xFF);
            }
          break;
        default:
          RunCount=bbuf & 0x7F;

          if(bbuf & 0x80)     /* REP */
            {  
              for(i=0; i < SampleSize; i++)
                SampleBuffer[i]=ReadBlobByte(image);
              for(i=0;i<=RunCount;i++)
                for(bbuf=0;bbuf<SampleSize;bbuf++)
                  InsertByte6(SampleBuffer[bbuf]);
            }
          else {      /* NRP */
            for(i=0; i< SampleSize*(RunCount+1);i++)
              {
                bbuf=ReadBlobByte(image);
                InsertByte6(bbuf);
              }
          }
        }
      if (EOFBlob(image) != MagickFalse)
        break;
    }
  BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
  return(0);
}

Project: ImageMagick
File: wpg.c

static int UnpackWPGRaster(Image *image,int bpp,ExceptionInfo *exception)
{
  int
    x,
    y,
    i;

  unsigned char
    bbuf,
    *BImgBuff,
    RunCount;

  ssize_t
    ldblk;

  x=0;
  y=0;

  ldblk=(ssize_t) ((bpp*image->columns+7)/8);
  BImgBuff=(unsigned char *) AcquireQuantumMemory((size_t) ldblk,
    8*sizeof(*BImgBuff));
  if(BImgBuff==NULL) return(-2);

  while(y<(ssize_t) image->rows)
    {
      int
        c;

      c=ReadBlobByte(image);
      if (c == EOF)
        break;
      bbuf=(unsigned char) c;
      RunCount=bbuf & 0x7F;
      if(bbuf & 0x80)
        {
          if(RunCount)  /* repeat next byte runcount * */
            {
              bbuf=ReadBlobByte(image);
              for(i=0;i<(int) RunCount;i++) InsertByte(bbuf);
            }
          else {  /* read next byte as RunCount; repeat 0xFF runcount* */
            c=ReadBlobByte(image);
            if (c < 0)
              break;
            RunCount=(unsigned char) c;
            for(i=0;i<(int) RunCount;i++) InsertByte(0xFF);
          }
        }
      else {
        if(RunCount)   /* next runcount byte are readed directly */
          {
            for(i=0;i < (int) RunCount;i++)
              {
                bbuf=ReadBlobByte(image);
                InsertByte(bbuf);
              }
          }
        else {  /* repeat previous line runcount* */
          c=ReadBlobByte(image);
          if (c < 0)
            break;
          RunCount=(unsigned char) c;
          if(x) {    /* attempt to duplicate row from x position: */
            /* I do not know what to do here */
            BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
            return(-3);
          }
          for(i=0;i < (int) RunCount;i++)
            {
              x=0;
              y++;    /* Here I need to duplicate previous row RUNCOUNT* */
              if(y<2) continue;
              if(y>(ssize_t) image->rows)
                {
                  BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
                  return(-4);
                }
              InsertRow(image,BImgBuff,y-1,bpp,exception);
            }
        }
      }
      if (EOFBlob(image) != MagickFalse)
        break;
    }
  BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
  return(y <(ssize_t) image->rows ? -5 : 0);
}