static Image *ReadDIBImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  DIBInfo
    dib_info;

  Image
    *image;

  MagickBooleanType
    status;

  MemoryInfo
    *pixel_info;

  Quantum
    index;

  register ssize_t
    x;

  register Quantum
    *q;

  register ssize_t
    i;

  register unsigned char
    *p;

  size_t
    bytes_per_line,
    length;

  ssize_t
    bit,
    count,
    y;

  unsigned char
    *pixels;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Determine if this a DIB file.
  */
  (void) memset(&dib_info,0,sizeof(dib_info));
  dib_info.size=ReadBlobLSBLong(image);
  if (dib_info.size != 40)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  /*
    Microsoft Windows 3.X DIB image file.
  */
  dib_info.width=ReadBlobLSBSignedLong(image);
  dib_info.height=ReadBlobLSBSignedLong(image);
  dib_info.planes=ReadBlobLSBShort(image);
  dib_info.bits_per_pixel=ReadBlobLSBShort(image);
  if (dib_info.bits_per_pixel > 32)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  dib_info.compression=ReadBlobLSBLong(image);
  dib_info.image_size=ReadBlobLSBLong(image);
  dib_info.x_pixels=ReadBlobLSBLong(image);
  dib_info.y_pixels=ReadBlobLSBLong(image);
  dib_info.number_colors=ReadBlobLSBLong(image);
  dib_info.colors_important=ReadBlobLSBLong(image);
  if ((dib_info.bits_per_pixel != 1) && (dib_info.bits_per_pixel != 4) &&
      (dib_info.bits_per_pixel != 8) && (dib_info.bits_per_pixel != 16) &&
      (dib_info.bits_per_pixel != 24) && (dib_info.bits_per_pixel != 32))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  if ((dib_info.compression == BI_BITFIELDS) &&
      ((dib_info.bits_per_pixel == 16) || (dib_info.bits_per_pixel == 32)))
    {
      dib_info.red_mask=(unsigned short) ReadBlobLSBLong(image);
      dib_info.green_mask=(unsigned short) ReadBlobLSBLong(image);
      dib_info.blue_mask=(unsigned short) ReadBlobLSBLong(image);
    }
  if (EOFBlob(image) != MagickFalse)
    ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
  if (dib_info.width <= 0)
    ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
  if (dib_info.height == 0)
    ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
  if (dib_info.planes != 1)
    ThrowReaderException(CorruptImageError,"StaticPlanesValueNotEqualToOne");
  if ((dib_info.bits_per_pixel != 1) && (dib_info.bits_per_pixel != 4) &&
      (dib_info.bits_per_pixel != 8) && (dib_info.bits_per_pixel != 16) &&
      (dib_info.bits_per_pixel != 24) && (dib_info.bits_per_pixel != 32))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  if ((dib_info.bits_per_pixel < 16) &&
      (dib_info.number_colors > (unsigned int) (1UL << dib_info.bits_per_pixel)))
    ThrowReaderException(CorruptImageError,"UnrecognizedNumberOfColors");
  if ((dib_info.compression == 1) && (dib_info.bits_per_pixel != 8))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  if ((dib_info.compression == 2) && (dib_info.bits_per_pixel != 4))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  if ((dib_info.compression == 3) && (dib_info.bits_per_pixel < 16))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  switch (dib_info.compression)
  {
    case BI_RGB:
    case BI_RLE8:
    case BI_RLE4:
    case BI_BITFIELDS:
      break;
    case BI_JPEG:
      ThrowReaderException(CoderError,"JPEGCompressNotSupported");
    case BI_PNG:
      ThrowReaderException(CoderError,"PNGCompressNotSupported");
    default:
      ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
  }
  image->columns=(size_t) MagickAbsoluteValue((ssize_t) dib_info.width);
  image->rows=(size_t) MagickAbsoluteValue((ssize_t) dib_info.height);
  image->depth=8;
  image->alpha_trait=dib_info.bits_per_pixel == 32 ? BlendPixelTrait :
    UndefinedPixelTrait;
  if ((dib_info.number_colors > 256) || (dib_info.colors_important > 256))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  if ((dib_info.number_colors != 0) || (dib_info.bits_per_pixel < 16))
    {
      size_t
        one;

      image->storage_class=PseudoClass;
      image->colors=dib_info.number_colors;
      one=1;
      if (image->colors == 0)
        image->colors=one << dib_info.bits_per_pixel;
    }
  if (image_info->size)
    {
      RectangleInfo
        geometry;

      MagickStatusType
        flags;

      flags=ParseAbsoluteGeometry(image_info->size,&geometry);
      if (flags & WidthValue)
        if ((geometry.width != 0) && (geometry.width < image->columns))
          image->columns=geometry.width;
      if (flags & HeightValue)
        if ((geometry.height != 0) && (geometry.height < image->rows))
          image->rows=geometry.height;
    }
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  if (image->storage_class == PseudoClass)
    {
      size_t
        packet_size;

      unsigned char
        *dib_colormap;

      /*
        Read DIB raster colormap.
      */
      if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      length=(size_t) image->colors;
      dib_colormap=(unsigned char *) AcquireQuantumMemory(length,
        4*sizeof(*dib_colormap));
      if (dib_colormap == (unsigned char *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      packet_size=4;
      count=ReadBlob(image,packet_size*image->colors,dib_colormap);
      if (count != (ssize_t) (packet_size*image->colors))
        {
          dib_colormap=(unsigned char *) RelinquishMagickMemory(dib_colormap);
          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
        }
      p=dib_colormap;
      for (i=0; i < (ssize_t) image->colors; i++)
      {
        image->colormap[i].blue=(MagickRealType) ScaleCharToQuantum(*p++);
        image->colormap[i].green=(MagickRealType) ScaleCharToQuantum(*p++);
        image->colormap[i].red=(MagickRealType) ScaleCharToQuantum(*p++);
        if (packet_size == 4)
          p++;
      }
      dib_colormap=(unsigned char *) RelinquishMagickMemory(dib_colormap);
    }
  /*
    Read image data.
  */
  if (dib_info.compression == BI_RLE4)
    dib_info.bits_per_pixel<<=1;
  bytes_per_line=4*((image->columns*dib_info.bits_per_pixel+31)/32);
  length=bytes_per_line*image->rows;
  pixel_info=AcquireVirtualMemory(image->rows,MagickMax(bytes_per_line,
    image->columns+256UL)*sizeof(*pixels));
  if (pixel_info == (MemoryInfo *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
  pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
  if ((dib_info.compression == BI_RGB) ||
      (dib_info.compression == BI_BITFIELDS))
    {
      count=ReadBlob(image,length,pixels);
      if (count != (ssize_t) (length))
        {
          pixel_info=RelinquishVirtualMemory(pixel_info);
          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
        }
    }
  else
    {
      /*
        Convert run-length encoded raster pixels.
      */
      status=DecodeImage(image,dib_info.compression ? MagickTrue : MagickFalse,
        pixels,image->columns*image->rows);
      if (status == MagickFalse)
        {
          pixel_info=RelinquishVirtualMemory(pixel_info);
          ThrowReaderException(CorruptImageError,
            "UnableToRunlengthDecodeImage");
        }
    }
  /*
    Initialize image structure.
  */
  image->units=PixelsPerCentimeterResolution;
  image->resolution.x=(double) dib_info.x_pixels/100.0;
  image->resolution.y=(double) dib_info.y_pixels/100.0;
  /*
    Convert DIB raster image to pixel packets.
  */
  switch (dib_info.bits_per_pixel)
  {
    case 1:
    {
      /*
        Convert bitmap scanline.
      */
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
        {
          for (bit=0; bit < 8; bit++)
          {
            index=(Quantum) ((*p) & (0x80 >> bit) ? 0x01 : 0x00);
            SetPixelIndex(image,index,q);
            q+=GetPixelChannels(image);
          }
          p++;
        }
        if ((image->columns % 8) != 0)
          {
            for (bit=0; bit < (ssize_t) (image->columns % 8); bit++)
            {
              index=(Quantum) ((*p) & (0x80 >> bit) ? 0x01 : 0x00);
              SetPixelIndex(image,index,q);
              q+=GetPixelChannels(image);
            }
            p++;
          }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      (void) SyncImage(image,exception);
      break;
    }
    case 4:
    {
      /*
        Convert PseudoColor scanline.
      */
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < ((ssize_t) image->columns-1); x+=2)
        {
          index=(Quantum) ConstrainColormapIndex(image,(ssize_t) (*p >> 4) &
            0xf,exception);
          SetPixelIndex(image,index,q);
          q+=GetPixelChannels(image);
          index=(Quantum) ConstrainColormapIndex(image,(ssize_t) *p & 0xf,
            exception);
          SetPixelIndex(image,index,q);
          p++;
          q+=GetPixelChannels(image);
        }
        if ((image->columns % 2) != 0)
          {
            index=(Quantum) ConstrainColormapIndex(image,(ssize_t) (*p >> 4) &
              0xf,exception);
            SetPixelIndex(image,index,q);
            q+=GetPixelChannels(image);
            p++;
          }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      (void) SyncImage(image,exception);
      break;
    }
    case 8:
    {
      /*
        Convert PseudoColor scanline.
      */
      if ((dib_info.compression == BI_RLE8) ||
          (dib_info.compression == BI_RLE4))
        bytes_per_line=image->columns;
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          index=(Quantum) ConstrainColormapIndex(image,(ssize_t) *p,exception);
          SetPixelIndex(image,index,q);
          p++;
          q+=GetPixelChannels(image);
        }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      (void) SyncImage(image,exception);
      break;
    }
    case 16:
    {
      unsigned short
        word;

      /*
        Convert PseudoColor scanline.
      */
      image->storage_class=DirectClass;
      if (dib_info.compression == BI_RLE8)
        bytes_per_line=2*image->columns;
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          word=(*p++);
          word|=(*p++ << 8);
          if (dib_info.red_mask == 0)
            {
              SetPixelRed(image,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) ((word >> 10) & 0x1f))),q);
              SetPixelGreen(image,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) ((word >> 5) & 0x1f))),q);
              SetPixelBlue(image,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) (word & 0x1f))),q);
            }
          else
            {
              SetPixelRed(image,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) ((word >> 11) & 0x1f))),q);
              SetPixelGreen(image,ScaleCharToQuantum(ScaleColor6to8(
                (unsigned char) ((word >> 5) & 0x3f))),q);
              SetPixelBlue(image,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) (word & 0x1f))),q);
            }
          q+=GetPixelChannels(image);
        }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      break;
    }
    case 24:
    case 32:
    {
      /*
        Convert DirectColor scanline.
      */
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
          SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
          SetPixelRed(image,ScaleCharToQuantum(*p++),q);
          if (image->alpha_trait != UndefinedPixelTrait)
            SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
          q+=GetPixelChannels(image);
        }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      break;
    }
    default:
      pixel_info=RelinquishVirtualMemory(pixel_info);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  }
  pixel_info=RelinquishVirtualMemory(pixel_info);
  if (strcmp(image_info->magick,"ICODIB") == 0)
    {
      int
        c;

      /*
        Handle ICO mask.
      */
      image->storage_class=DirectClass;
      image->alpha_trait=BlendPixelTrait;
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        register ssize_t
          x;

        register Quantum
          *magick_restrict q;

        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
        {
          c=ReadBlobByte(image);
          for (bit=0; bit < 8; bit++)
            SetPixelAlpha(image,c & (0x80 >> bit) ? TransparentAlpha :
              OpaqueAlpha,q+x*GetPixelChannels(image)+bit);
        }
        if ((image->columns % 8) != 0)
          {
            c=ReadBlobByte(image);
            for (bit=0; bit < (ssize_t) (image->columns % 8); bit++)
              SetPixelAlpha(image,c & (0x80 >> bit) ? TransparentAlpha :
                OpaqueAlpha,q+x*GetPixelChannels(image)+bit);
          }
        if (image->columns % 32)
          for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
            c=ReadBlobByte(image);
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
      }
    }
  if (EOFBlob(image) != MagickFalse)
    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
      image->filename);
  if (dib_info.height < 0)
    {
      Image
        *flipped_image;

      /*
        Correct image orientation.
      */
      flipped_image=FlipImage(image,exception);
      if (flipped_image != (Image *) NULL)
        {
          DuplicateBlob(flipped_image,image);
          image=DestroyImage(image);
          image=flipped_image;
        }
    }
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}


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=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;
}

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.


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;
}


Check index before writing to result->chars

Fixes #595

parseChars(FileInfo *nested, CharsString *result, CharsString *token) {
	int in = 0;
	int out = 0;
	int lastOutSize = 0;
	int lastIn;
	unsigned int ch = 0;
	int numBytes = 0;
	unsigned int utf32 = 0;
	int k;
	while (in < token->length) {
		ch = token->chars[in++] & 0xff;
		if (ch < 128) {
			if (ch == '\\') { /* escape sequence */
				switch (ch = token->chars[in]) {
				case '\\':
					break;
				case 'e':
					ch = 0x1b;
					break;
				case 'f':
					ch = 12;
					break;
				case 'n':
					ch = 10;
					break;
				case 'r':
					ch = 13;
					break;
				case 's':
					ch = ' ';
					break;
				case 't':
					ch = 9;
					break;
				case 'v':
					ch = 11;
					break;
				case 'w':
					ch = ENDSEGMENT;
					break;
				case 34:
					ch = QUOTESUB;
					break;
				case 'X':
				case 'x':
					if (token->length - in > 4) {
						ch = hexValue(nested, &token->chars[in + 1], 4);
						in += 4;
					}
					break;
				case 'y':
				case 'Y':
					if (CHARSIZE == 2) {
					not32:
						compileError(nested,
								"liblouis has not been compiled for 32-bit Unicode");
						break;
					}
					if (token->length - in > 5) {
						ch = hexValue(nested, &token->chars[in + 1], 5);
						in += 5;
					}
					break;
				case 'z':
				case 'Z':
					if (CHARSIZE == 2) goto not32;
					if (token->length - in > 8) {
						ch = hexValue(nested, &token->chars[in + 1], 8);
						in += 8;
					}
					break;
				default:
					compileError(nested, "invalid escape sequence '\\%c'", ch);
					break;
				}
				in++;
			}
			result->chars[out++] = (widechar)ch;
			if (out >= MAXSTRING) {
				result->length = out;
				return 1;
			}
			continue;
		}
		lastOutSize = out;
		lastIn = in;
		for (numBytes = MAXBYTES - 1; numBytes > 0; numBytes--)
			if (ch >= first0Bit[numBytes]) break;
		utf32 = ch & (0XFF - first0Bit[numBytes]);
		for (k = 0; k < numBytes; k++) {
			if (in >= MAXSTRING) break;
			if (out >= MAXSTRING) {
				result->length = lastOutSize;
				return 1;
			}
			if (token->chars[in] < 128 || (token->chars[in] & 0x0040)) {
				compileWarning(nested, "invalid UTF-8. Assuming Latin-1.");
				result->chars[out++] = token->chars[lastIn];
				in = lastIn + 1;
				continue;
			}
			utf32 = (utf32 << 6) + (token->chars[in++] & 0x3f);
		}
		if (out >= MAXSTRING) {
			result->length = lastOutSize;
			return 1;
		}
		if (CHARSIZE == 2 && utf32 > 0xffff) utf32 = 0xffff;
		result->chars[out++] = (widechar)utf32;
	}
	result->length = out;
	return 1;
}

Liblouis 3.6.0 has a stack-based Buffer Overflow in the function parseChars in compileTranslationTable.c, a different vulnerability than CVE-2018-11440.


parseChars(FileInfo *nested, CharsString *result, CharsString *token) {
	int in = 0;
	int out = 0;
	int lastOutSize = 0;
	int lastIn;
	unsigned int ch = 0;
	int numBytes = 0;
	unsigned int utf32 = 0;
	int k;
	while (in < token->length) {
		ch = token->chars[in++] & 0xff;
		if (ch < 128) {
			if (ch == '\\') { /* escape sequence */
				switch (ch = token->chars[in]) {
				case '\\':
					break;
				case 'e':
					ch = 0x1b;
					break;
				case 'f':
					ch = 12;
					break;
				case 'n':
					ch = 10;
					break;
				case 'r':
					ch = 13;
					break;
				case 's':
					ch = ' ';
					break;
				case 't':
					ch = 9;
					break;
				case 'v':
					ch = 11;
					break;
				case 'w':
					ch = ENDSEGMENT;
					break;
				case 34:
					ch = QUOTESUB;
					break;
				case 'X':
				case 'x':
					if (token->length - in > 4) {
						ch = hexValue(nested, &token->chars[in + 1], 4);
						in += 4;
					}
					break;
				case 'y':
				case 'Y':
					if (CHARSIZE == 2) {
					not32:
						compileError(nested,
								"liblouis has not been compiled for 32-bit Unicode");
						break;
					}
					if (token->length - in > 5) {
						ch = hexValue(nested, &token->chars[in + 1], 5);
						in += 5;
					}
					break;
				case 'z':
				case 'Z':
					if (CHARSIZE == 2) goto not32;
					if (token->length - in > 8) {
						ch = hexValue(nested, &token->chars[in + 1], 8);
						in += 8;
					}
					break;
				default:
					compileError(nested, "invalid escape sequence '\\%c'", ch);
					break;
				}
				in++;
			}
			if (out >= MAXSTRING) {
				result->length = out;
				return 1;
			}
			result->chars[out++] = (widechar)ch;
			continue;
		}
		lastOutSize = out;
		lastIn = in;
		for (numBytes = MAXBYTES - 1; numBytes > 0; numBytes--)
			if (ch >= first0Bit[numBytes]) break;
		utf32 = ch & (0XFF - first0Bit[numBytes]);
		for (k = 0; k < numBytes; k++) {
			if (in >= MAXSTRING) break;
			if (out >= MAXSTRING) {
				result->length = lastOutSize;
				return 1;
			}
			if (token->chars[in] < 128 || (token->chars[in] & 0x0040)) {
				compileWarning(nested, "invalid UTF-8. Assuming Latin-1.");
				result->chars[out++] = token->chars[lastIn];
				in = lastIn + 1;
				continue;
			}
			utf32 = (utf32 << 6) + (token->chars[in++] & 0x3f);
		}
		if (out >= MAXSTRING) {
			result->length = lastOutSize;
			return 1;
		}
		if (CHARSIZE == 2 && utf32 > 0xffff) utf32 = 0xffff;
		result->chars[out++] = (widechar)utf32;
	}
	result->length = out;
	return 1;
}


static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xmlNodePtr parent, int check_class_map TSRMLS_DC)
{
	xmlNodePtr node = NULL;
	int add_type = 0;

	/* Special handling of class SoapVar */
	if (data &&
	    Z_TYPE_P(data) == IS_OBJECT &&
	    Z_OBJCE_P(data) == soap_var_class_entry) {
		zval **ztype, **zdata, **zns, **zstype, **zname, **znamens;
		encodePtr enc = NULL;
		HashTable *ht = Z_OBJPROP_P(data);

		if (zend_hash_find(ht, "enc_type", sizeof("enc_type"), (void **)&ztype) == FAILURE ||
		    Z_TYPE_PP(ztype) != IS_LONG) {
			soap_error0(E_ERROR, "Encoding: SoapVar has no 'enc_type' property");
		}

		if (zend_hash_find(ht, "enc_stype", sizeof("enc_stype"), (void **)&zstype) == SUCCESS &&
		    Z_TYPE_PP(zstype) == IS_STRING) {
			if (zend_hash_find(ht, "enc_ns", sizeof("enc_ns"), (void **)&zns) == SUCCESS &&
			    Z_TYPE_PP(zns) == IS_STRING) {
				enc = get_encoder(SOAP_GLOBAL(sdl), Z_STRVAL_PP(zns), Z_STRVAL_PP(zstype));
			} else {
				zns = NULL;
				enc = get_encoder_ex(SOAP_GLOBAL(sdl), Z_STRVAL_PP(zstype), Z_STRLEN_PP(zstype));
			}
			if (enc == NULL && SOAP_GLOBAL(typemap)) {
				encodePtr *new_enc;
				smart_str nscat = {0};

				if (zns != NULL) {
					smart_str_appendl(&nscat, Z_STRVAL_PP(zns), Z_STRLEN_PP(zns));
					smart_str_appendc(&nscat, ':');
				}
				smart_str_appendl(&nscat, Z_STRVAL_PP(zstype), Z_STRLEN_PP(zstype));
				smart_str_0(&nscat);
				if (zend_hash_find(SOAP_GLOBAL(typemap), nscat.c, nscat.len + 1, (void**)&new_enc) == SUCCESS) {
					enc = *new_enc;
				}
				smart_str_free(&nscat);
			}
		}
		if (enc == NULL) {
			enc = get_conversion(Z_LVAL_P(*ztype));
		}
		if (enc == NULL) {
			enc = encode;
		}

		if (zend_hash_find(ht, "enc_value", sizeof("enc_value"), (void **)&zdata) == FAILURE) {
			node = master_to_xml(enc, NULL, style, parent TSRMLS_CC);
		} else {
			node = master_to_xml(enc, *zdata, style, parent TSRMLS_CC);
		}

		if (style == SOAP_ENCODED || (SOAP_GLOBAL(sdl) && encode != enc)) {
			if (zend_hash_find(ht, "enc_stype", sizeof("enc_stype"), (void **)&zstype) == SUCCESS &&
			    Z_TYPE_PP(zstype) == IS_STRING) {
				if (zend_hash_find(ht, "enc_ns", sizeof("enc_ns"), (void **)&zns) == SUCCESS &&
				    Z_TYPE_PP(zns) == IS_STRING) {
					set_ns_and_type_ex(node, Z_STRVAL_PP(zns), Z_STRVAL_PP(zstype));
				} else {
					set_ns_and_type_ex(node, NULL, Z_STRVAL_PP(zstype));
				}
			}
		}

		if (zend_hash_find(ht, "enc_name", sizeof("enc_name"), (void **)&zname) == SUCCESS &&
		    Z_TYPE_PP(zname) == IS_STRING) {
			xmlNodeSetName(node, BAD_CAST(Z_STRVAL_PP(zname)));
		}
		if (zend_hash_find(ht, "enc_namens", sizeof("enc_namens"), (void **)&znamens) == SUCCESS &&
		    Z_TYPE_PP(zname) == IS_STRING) {
			xmlNsPtr nsp = encode_add_ns(node, Z_STRVAL_PP(znamens));
			xmlSetNs(node, nsp);
		}
	} else {
		if (check_class_map && SOAP_GLOBAL(class_map) && data &&
		    Z_TYPE_P(data) == IS_OBJECT &&
		    !Z_OBJPROP_P(data)->nApplyCount) {
			zend_class_entry *ce = Z_OBJCE_P(data);
			HashPosition pos;
			zval **tmp;
			char *type_name = NULL;
			uint type_len;
			ulong idx;

			for (zend_hash_internal_pointer_reset_ex(SOAP_GLOBAL(class_map), &pos);
			     zend_hash_get_current_data_ex(SOAP_GLOBAL(class_map), (void **) &tmp, &pos) == SUCCESS;
			     zend_hash_move_forward_ex(SOAP_GLOBAL(class_map), &pos)) {
				if (Z_TYPE_PP(tmp) == IS_STRING &&
				    ce->name_length == Z_STRLEN_PP(tmp) &&
				    zend_binary_strncasecmp(ce->name, ce->name_length, Z_STRVAL_PP(tmp), ce->name_length, ce->name_length) == 0 &&
				    zend_hash_get_current_key_ex(SOAP_GLOBAL(class_map), &type_name, &type_len, &idx, 0, &pos) == HASH_KEY_IS_STRING) {

					/* TODO: namespace isn't stored */
					encodePtr enc = NULL;
					if (SOAP_GLOBAL(sdl)) {
						enc = get_encoder(SOAP_GLOBAL(sdl), SOAP_GLOBAL(sdl)->target_ns, type_name);
						if (!enc) {
							enc = find_encoder_by_type_name(SOAP_GLOBAL(sdl), type_name);
						}
					}
					if (enc) {
						if (encode != enc && style == SOAP_LITERAL) {
							add_type = 1;
						}
						encode = enc;
					}
					break;
				}
			}
		}

		if (encode == NULL) {
			encode = get_conversion(UNKNOWN_TYPE);
		}
		if (SOAP_GLOBAL(typemap) && encode->details.type_str) {
			smart_str nscat = {0};
			encodePtr *new_enc;

			if (encode->details.ns) {
				smart_str_appends(&nscat, encode->details.ns);
				smart_str_appendc(&nscat, ':');
			}
			smart_str_appends(&nscat, encode->details.type_str);
			smart_str_0(&nscat);
			if (zend_hash_find(SOAP_GLOBAL(typemap), nscat.c, nscat.len + 1, (void**)&new_enc) == SUCCESS) {
				encode = *new_enc;
			}
			smart_str_free(&nscat);
		}
		if (encode->to_xml) {
			node = encode->to_xml(&encode->details, data, style, parent TSRMLS_CC);
			if (add_type) {
				set_ns_and_type(node, &encode->details);
			}
		}
	}
	return node;
}

The SoapClient::__call method in ext/soap/soap.c in PHP before 5.4.39, 5.5.x before 5.5.23, and 5.6.x before 5.6.7 does not verify that __default_headers is an array, which allows remote attackers to execute arbitrary code by providing crafted serialized data with an unexpected data type, related to a "type confusion" issue.


static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xmlNodePtr parent, int check_class_map TSRMLS_DC)
{
	xmlNodePtr node = NULL;
	int add_type = 0;

	/* Special handling of class SoapVar */
	if (data &&
	    Z_TYPE_P(data) == IS_OBJECT &&
	    Z_OBJCE_P(data) == soap_var_class_entry) {
		zval **ztype, **zdata, **zns, **zstype, **zname, **znamens;
		encodePtr enc = NULL;
		HashTable *ht = Z_OBJPROP_P(data);

		if (zend_hash_find(ht, "enc_type", sizeof("enc_type"), (void **)&ztype) == FAILURE ||
		    Z_TYPE_PP(ztype) != IS_LONG) {
			soap_error0(E_ERROR, "Encoding: SoapVar has no 'enc_type' property");
		}

		if (zend_hash_find(ht, "enc_stype", sizeof("enc_stype"), (void **)&zstype) == SUCCESS &&
		    Z_TYPE_PP(zstype) == IS_STRING) {
			if (zend_hash_find(ht, "enc_ns", sizeof("enc_ns"), (void **)&zns) == SUCCESS &&
			    Z_TYPE_PP(zns) == IS_STRING) {
				enc = get_encoder(SOAP_GLOBAL(sdl), Z_STRVAL_PP(zns), Z_STRVAL_PP(zstype));
			} else {
				zns = NULL;
				enc = get_encoder_ex(SOAP_GLOBAL(sdl), Z_STRVAL_PP(zstype), Z_STRLEN_PP(zstype));
			}
			if (enc == NULL && SOAP_GLOBAL(typemap)) {
				encodePtr *new_enc;
				smart_str nscat = {0};

				if (zns != NULL) {
					smart_str_appendl(&nscat, Z_STRVAL_PP(zns), Z_STRLEN_PP(zns));
					smart_str_appendc(&nscat, ':');
				}
				smart_str_appendl(&nscat, Z_STRVAL_PP(zstype), Z_STRLEN_PP(zstype));
				smart_str_0(&nscat);
				if (zend_hash_find(SOAP_GLOBAL(typemap), nscat.c, nscat.len + 1, (void**)&new_enc) == SUCCESS) {
					enc = *new_enc;
				}
				smart_str_free(&nscat);
			}
		}
		if (enc == NULL) {
			enc = get_conversion(Z_LVAL_P(*ztype));
		}
		if (enc == NULL) {
			enc = encode;
		}

		if (zend_hash_find(ht, "enc_value", sizeof("enc_value"), (void **)&zdata) == FAILURE) {
			node = master_to_xml(enc, NULL, style, parent TSRMLS_CC);
		} else {
			node = master_to_xml(enc, *zdata, style, parent TSRMLS_CC);
		}

		if (style == SOAP_ENCODED || (SOAP_GLOBAL(sdl) && encode != enc)) {
			if (zend_hash_find(ht, "enc_stype", sizeof("enc_stype"), (void **)&zstype) == SUCCESS &&
			    Z_TYPE_PP(zstype) == IS_STRING) {
				if (zend_hash_find(ht, "enc_ns", sizeof("enc_ns"), (void **)&zns) == SUCCESS &&
				    Z_TYPE_PP(zns) == IS_STRING) {
					set_ns_and_type_ex(node, Z_STRVAL_PP(zns), Z_STRVAL_PP(zstype));
				} else {
					set_ns_and_type_ex(node, NULL, Z_STRVAL_PP(zstype));
				}
			}
		}

		if (zend_hash_find(ht, "enc_name", sizeof("enc_name"), (void **)&zname) == SUCCESS &&
		    Z_TYPE_PP(zname) == IS_STRING) {
			xmlNodeSetName(node, BAD_CAST(Z_STRVAL_PP(zname)));
		}
		if (zend_hash_find(ht, "enc_namens", sizeof("enc_namens"), (void **)&znamens) == SUCCESS &&
		    Z_TYPE_PP(znamens) == IS_STRING) {
			xmlNsPtr nsp = encode_add_ns(node, Z_STRVAL_PP(znamens));
			xmlSetNs(node, nsp);
		}
	} else {
		if (check_class_map && SOAP_GLOBAL(class_map) && data &&
		    Z_TYPE_P(data) == IS_OBJECT &&
		    !Z_OBJPROP_P(data)->nApplyCount) {
			zend_class_entry *ce = Z_OBJCE_P(data);
			HashPosition pos;
			zval **tmp;
			char *type_name = NULL;
			uint type_len;
			ulong idx;

			for (zend_hash_internal_pointer_reset_ex(SOAP_GLOBAL(class_map), &pos);
			     zend_hash_get_current_data_ex(SOAP_GLOBAL(class_map), (void **) &tmp, &pos) == SUCCESS;
			     zend_hash_move_forward_ex(SOAP_GLOBAL(class_map), &pos)) {
				if (Z_TYPE_PP(tmp) == IS_STRING &&
				    ce->name_length == Z_STRLEN_PP(tmp) &&
				    zend_binary_strncasecmp(ce->name, ce->name_length, Z_STRVAL_PP(tmp), ce->name_length, ce->name_length) == 0 &&
				    zend_hash_get_current_key_ex(SOAP_GLOBAL(class_map), &type_name, &type_len, &idx, 0, &pos) == HASH_KEY_IS_STRING) {

					/* TODO: namespace isn't stored */
					encodePtr enc = NULL;
					if (SOAP_GLOBAL(sdl)) {
						enc = get_encoder(SOAP_GLOBAL(sdl), SOAP_GLOBAL(sdl)->target_ns, type_name);
						if (!enc) {
							enc = find_encoder_by_type_name(SOAP_GLOBAL(sdl), type_name);
						}
					}
					if (enc) {
						if (encode != enc && style == SOAP_LITERAL) {
							add_type = 1;
						}
						encode = enc;
					}
					break;
				}
			}
		}

		if (encode == NULL) {
			encode = get_conversion(UNKNOWN_TYPE);
		}
		if (SOAP_GLOBAL(typemap) && encode->details.type_str) {
			smart_str nscat = {0};
			encodePtr *new_enc;

			if (encode->details.ns) {
				smart_str_appends(&nscat, encode->details.ns);
				smart_str_appendc(&nscat, ':');
			}
			smart_str_appends(&nscat, encode->details.type_str);
			smart_str_0(&nscat);
			if (zend_hash_find(SOAP_GLOBAL(typemap), nscat.c, nscat.len + 1, (void**)&new_enc) == SUCCESS) {
				encode = *new_enc;
			}
			smart_str_free(&nscat);
		}
		if (encode->to_xml) {
			node = encode->to_xml(&encode->details, data, style, parent TSRMLS_CC);
			if (add_type) {
				set_ns_and_type(node, &encode->details);
			}
		}
	}
	return node;
}


Don't interpret file display names as format strings

This avoids a segfault when copying/moving files containing "%" formatters
in their name.

Signed-off-by: Jannis Pohlmann <jannis@xfce.org>

thunar_transfer_job_copy_node (ThunarTransferJob  *job,
                               ThunarTransferNode *node,
                               GFile              *target_file,
                               GFile              *target_parent_file,
                               GList             **target_file_list_return,
                               GError            **error)
{
  ThunarThumbnailCache *thumbnail_cache;
  ThunarApplication    *application;
  ThunarJobResponse     response;
  GFileInfo            *info;
  GError               *err = NULL;
  GFile                *real_target_file = NULL;
  gchar                *base_name;

  _thunar_return_if_fail (THUNAR_IS_TRANSFER_JOB (job));
  _thunar_return_if_fail (node != NULL && G_IS_FILE (node->source_file));
  _thunar_return_if_fail (target_file == NULL || node->next == NULL);
  _thunar_return_if_fail ((target_file == NULL && target_parent_file != NULL) || (target_file != NULL && target_parent_file == NULL));
  _thunar_return_if_fail (error == NULL || *error == NULL);

  /* The caller can either provide a target_file or a target_parent_file, but not both. The toplevel
   * transfer_nodes (for which next is NULL) should be called with target_file, to get proper behavior
   * wrt restoring files from the trash. Other transfer_nodes will be called with target_parent_file.
   */

  /* take a reference on the thumbnail cache */
  application = thunar_application_get ();
  thumbnail_cache = thunar_application_get_thumbnail_cache (application);
  g_object_unref (application);

  for (; err == NULL && node != NULL; node = node->next)
    {
      /* guess the target file for this node (unless already provided) */
      if (G_LIKELY (target_file == NULL))
        {
          base_name = g_file_get_basename (node->source_file);
          target_file = g_file_get_child (target_parent_file, base_name);
          g_free (base_name);
        }
      else
        target_file = g_object_ref (target_file);

      /* query file info */
      info = g_file_query_info (node->source_file,
                                G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
                                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                exo_job_get_cancellable (EXO_JOB (job)),
                                &err);

      /* abort on error or cancellation */
      if (info == NULL)
        {
          g_object_unref (target_file);
          break;
        }

      /* update progress information */
      exo_job_info_message (EXO_JOB (job), g_file_info_get_display_name (info));

retry_copy:
      /* copy the item specified by this node (not recursively) */
      real_target_file = thunar_transfer_job_copy_file (job, node->source_file,
                                                        target_file, &err);
      if (G_LIKELY (real_target_file != NULL))
        {
          /* node->source_file == real_target_file means to skip the file */
          if (G_LIKELY (node->source_file != real_target_file))
            {
              /* notify the thumbnail cache of the copy operation */
              thunar_thumbnail_cache_copy_file (thumbnail_cache,
                                                node->source_file,
                                                real_target_file);

              /* check if we have children to copy */
              if (node->children != NULL)
                {
                  /* copy all children of this node */
                  thunar_transfer_job_copy_node (job, node->children, NULL, real_target_file, NULL, &err);

                  /* free resources allocted for the children */
                  thunar_transfer_node_free (node->children);
                  node->children = NULL;
                }

              /* check if the child copy failed */
              if (G_UNLIKELY (err != NULL))
                {
                  /* outa here, freeing the target paths */
                  g_object_unref (real_target_file);
                  g_object_unref (target_file);
                  break;
                }

              /* add the real target file to the return list */
              if (G_LIKELY (target_file_list_return != NULL))
                {
                  *target_file_list_return =
                    thunar_g_file_list_prepend (*target_file_list_return,
                                                real_target_file);
                }

retry_remove:
              /* try to remove the source directory if we are on copy+remove fallback for move */
              if (job->type == THUNAR_TRANSFER_JOB_MOVE)
                {
                  if (g_file_delete (node->source_file,
                                     exo_job_get_cancellable (EXO_JOB (job)),
                                     &err))
                    {
                      /* notify the thumbnail cache of the delete operation */
                      thunar_thumbnail_cache_delete_file (thumbnail_cache,
                                                          node->source_file);
                    }
                  else
                    {
                      /* ask the user to retry */
                      response = thunar_job_ask_skip (THUNAR_JOB (job), "%s",
                                                      err->message);

                      /* reset the error */
                      g_clear_error (&err);

                      /* check whether to retry */
                      if (G_UNLIKELY (response == THUNAR_JOB_RESPONSE_RETRY))
                        goto retry_remove;
                    }
                }
            }

          g_object_unref (real_target_file);
        }
      else if (err != NULL)
        {
          /* we can only skip if there is space left on the device */
          if (err->domain != G_IO_ERROR || err->code != G_IO_ERROR_NO_SPACE)
            {
              /* ask the user to skip this node and all subnodes */
              response = thunar_job_ask_skip (THUNAR_JOB (job), "%s", err->message);

              /* reset the error */
              g_clear_error (&err);

              /* check whether to retry */
              if (G_UNLIKELY (response == THUNAR_JOB_RESPONSE_RETRY))
                goto retry_copy;
            }
        }

      /* release the guessed target file */
      g_object_unref (target_file);
      target_file = NULL;

      /* release file info */
      g_object_unref (info);
    }

  /* release the thumbnail cache */
  g_object_unref (thumbnail_cache);

  /* propagate error if we failed or the job was cancelled */
  if (G_UNLIKELY (err != NULL))
    g_propagate_error (error, err);
}

Thunar before 1.3.1 could crash when copy and pasting a file name with % format characters due to a format string error.


thunar_transfer_job_copy_node (ThunarTransferJob  *job,
                               ThunarTransferNode *node,
                               GFile              *target_file,
                               GFile              *target_parent_file,
                               GList             **target_file_list_return,
                               GError            **error)
{
  ThunarThumbnailCache *thumbnail_cache;
  ThunarApplication    *application;
  ThunarJobResponse     response;
  GFileInfo            *info;
  GError               *err = NULL;
  GFile                *real_target_file = NULL;
  gchar                *base_name;

  _thunar_return_if_fail (THUNAR_IS_TRANSFER_JOB (job));
  _thunar_return_if_fail (node != NULL && G_IS_FILE (node->source_file));
  _thunar_return_if_fail (target_file == NULL || node->next == NULL);
  _thunar_return_if_fail ((target_file == NULL && target_parent_file != NULL) || (target_file != NULL && target_parent_file == NULL));
  _thunar_return_if_fail (error == NULL || *error == NULL);

  /* The caller can either provide a target_file or a target_parent_file, but not both. The toplevel
   * transfer_nodes (for which next is NULL) should be called with target_file, to get proper behavior
   * wrt restoring files from the trash. Other transfer_nodes will be called with target_parent_file.
   */

  /* take a reference on the thumbnail cache */
  application = thunar_application_get ();
  thumbnail_cache = thunar_application_get_thumbnail_cache (application);
  g_object_unref (application);

  for (; err == NULL && node != NULL; node = node->next)
    {
      /* guess the target file for this node (unless already provided) */
      if (G_LIKELY (target_file == NULL))
        {
          base_name = g_file_get_basename (node->source_file);
          target_file = g_file_get_child (target_parent_file, base_name);
          g_free (base_name);
        }
      else
        target_file = g_object_ref (target_file);

      /* query file info */
      info = g_file_query_info (node->source_file,
                                G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
                                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                exo_job_get_cancellable (EXO_JOB (job)),
                                &err);

      /* abort on error or cancellation */
      if (info == NULL)
        {
          g_object_unref (target_file);
          break;
        }

      /* update progress information */
      exo_job_info_message (EXO_JOB (job), "%s", g_file_info_get_display_name (info));

retry_copy:
      /* copy the item specified by this node (not recursively) */
      real_target_file = thunar_transfer_job_copy_file (job, node->source_file,
                                                        target_file, &err);
      if (G_LIKELY (real_target_file != NULL))
        {
          /* node->source_file == real_target_file means to skip the file */
          if (G_LIKELY (node->source_file != real_target_file))
            {
              /* notify the thumbnail cache of the copy operation */
              thunar_thumbnail_cache_copy_file (thumbnail_cache,
                                                node->source_file,
                                                real_target_file);

              /* check if we have children to copy */
              if (node->children != NULL)
                {
                  /* copy all children of this node */
                  thunar_transfer_job_copy_node (job, node->children, NULL, real_target_file, NULL, &err);

                  /* free resources allocted for the children */
                  thunar_transfer_node_free (node->children);
                  node->children = NULL;
                }

              /* check if the child copy failed */
              if (G_UNLIKELY (err != NULL))
                {
                  /* outa here, freeing the target paths */
                  g_object_unref (real_target_file);
                  g_object_unref (target_file);
                  break;
                }

              /* add the real target file to the return list */
              if (G_LIKELY (target_file_list_return != NULL))
                {
                  *target_file_list_return =
                    thunar_g_file_list_prepend (*target_file_list_return,
                                                real_target_file);
                }

retry_remove:
              /* try to remove the source directory if we are on copy+remove fallback for move */
              if (job->type == THUNAR_TRANSFER_JOB_MOVE)
                {
                  if (g_file_delete (node->source_file,
                                     exo_job_get_cancellable (EXO_JOB (job)),
                                     &err))
                    {
                      /* notify the thumbnail cache of the delete operation */
                      thunar_thumbnail_cache_delete_file (thumbnail_cache,
                                                          node->source_file);
                    }
                  else
                    {
                      /* ask the user to retry */
                      response = thunar_job_ask_skip (THUNAR_JOB (job), "%s",
                                                      err->message);

                      /* reset the error */
                      g_clear_error (&err);

                      /* check whether to retry */
                      if (G_UNLIKELY (response == THUNAR_JOB_RESPONSE_RETRY))
                        goto retry_remove;
                    }
                }
            }

          g_object_unref (real_target_file);
        }
      else if (err != NULL)
        {
          /* we can only skip if there is space left on the device */
          if (err->domain != G_IO_ERROR || err->code != G_IO_ERROR_NO_SPACE)
            {
              /* ask the user to skip this node and all subnodes */
              response = thunar_job_ask_skip (THUNAR_JOB (job), "%s", err->message);

              /* reset the error */
              g_clear_error (&err);

              /* check whether to retry */
              if (G_UNLIKELY (response == THUNAR_JOB_RESPONSE_RETRY))
                goto retry_copy;
            }
        }

      /* release the guessed target file */
      g_object_unref (target_file);
      target_file = NULL;

      /* release file info */
      g_object_unref (info);
    }

  /* release the thumbnail cache */
  g_object_unref (thumbnail_cache);

  /* propagate error if we failed or the job was cancelled */
  if (G_UNLIKELY (err != NULL))
    g_propagate_error (error, err);
}


userfaultfd: clear the vma->vm_userfaultfd_ctx if UFFD_EVENT_FORK fails

The previous fix in commit 384632e67e08 ("userfaultfd: non-cooperative:
fix fork use after free") corrected the refcounting in case of
UFFD_EVENT_FORK failure for the fork userfault paths.

That still didn't clear the vma->vm_userfaultfd_ctx of the vmas that
were set to point to the aborted new uffd ctx earlier in
dup_userfaultfd.

Link: http://lkml.kernel.org/r/20171223002505.593-2-aarcange@redhat.com
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Reviewed-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Eric Biggers <ebiggers3@gmail.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
					      struct userfaultfd_wait_queue *ewq)
{
	if (WARN_ON_ONCE(current->flags & PF_EXITING))
		goto out;

	ewq->ctx = ctx;
	init_waitqueue_entry(&ewq->wq, current);

	spin_lock(&ctx->event_wqh.lock);
	/*
	 * After the __add_wait_queue the uwq is visible to userland
	 * through poll/read().
	 */
	__add_wait_queue(&ctx->event_wqh, &ewq->wq);
	for (;;) {
		set_current_state(TASK_KILLABLE);
		if (ewq->msg.event == 0)
			break;
		if (READ_ONCE(ctx->released) ||
		    fatal_signal_pending(current)) {
			/*
			 * &ewq->wq may be queued in fork_event, but
			 * __remove_wait_queue ignores the head
			 * parameter. It would be a problem if it
			 * didn't.
			 */
			__remove_wait_queue(&ctx->event_wqh, &ewq->wq);
			if (ewq->msg.event == UFFD_EVENT_FORK) {
				struct userfaultfd_ctx *new;

				new = (struct userfaultfd_ctx *)
					(unsigned long)
					ewq->msg.arg.reserved.reserved1;

				userfaultfd_ctx_put(new);
			}
			break;
		}

		spin_unlock(&ctx->event_wqh.lock);

		wake_up_poll(&ctx->fd_wqh, POLLIN);
		schedule();

		spin_lock(&ctx->event_wqh.lock);
	}
	__set_current_state(TASK_RUNNING);
	spin_unlock(&ctx->event_wqh.lock);

	/*
	 * ctx may go away after this if the userfault pseudo fd is
	 * already released.
	 */
out:
	userfaultfd_ctx_put(ctx);
}

A use-after-free flaw was found in fs/userfaultfd.c in the Linux kernel before 4.13.6. The issue is related to the handling of fork failure when dealing with event messages. Failure to fork correctly can lead to a situation where a fork event will be removed from an already freed list of events with userfaultfd_ctx_put().


static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
					      struct userfaultfd_wait_queue *ewq)
{
	struct userfaultfd_ctx *release_new_ctx;

	if (WARN_ON_ONCE(current->flags & PF_EXITING))
		goto out;

	ewq->ctx = ctx;
	init_waitqueue_entry(&ewq->wq, current);
	release_new_ctx = NULL;

	spin_lock(&ctx->event_wqh.lock);
	/*
	 * After the __add_wait_queue the uwq is visible to userland
	 * through poll/read().
	 */
	__add_wait_queue(&ctx->event_wqh, &ewq->wq);
	for (;;) {
		set_current_state(TASK_KILLABLE);
		if (ewq->msg.event == 0)
			break;
		if (READ_ONCE(ctx->released) ||
		    fatal_signal_pending(current)) {
			/*
			 * &ewq->wq may be queued in fork_event, but
			 * __remove_wait_queue ignores the head
			 * parameter. It would be a problem if it
			 * didn't.
			 */
			__remove_wait_queue(&ctx->event_wqh, &ewq->wq);
			if (ewq->msg.event == UFFD_EVENT_FORK) {
				struct userfaultfd_ctx *new;

				new = (struct userfaultfd_ctx *)
					(unsigned long)
					ewq->msg.arg.reserved.reserved1;
				release_new_ctx = new;
			}
			break;
		}

		spin_unlock(&ctx->event_wqh.lock);

		wake_up_poll(&ctx->fd_wqh, POLLIN);
		schedule();

		spin_lock(&ctx->event_wqh.lock);
	}
	__set_current_state(TASK_RUNNING);
	spin_unlock(&ctx->event_wqh.lock);

	if (release_new_ctx) {
		struct vm_area_struct *vma;
		struct mm_struct *mm = release_new_ctx->mm;

		/* the various vma->vm_userfaultfd_ctx still points to it */
		down_write(&mm->mmap_sem);
		for (vma = mm->mmap; vma; vma = vma->vm_next)
			if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx)
				vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
		up_write(&mm->mmap_sem);

		userfaultfd_ctx_put(release_new_ctx);
	}

	/*
	 * ctx may go away after this if the userfault pseudo fd is
	 * already released.
	 */
out:
	userfaultfd_ctx_put(ctx);
}


f2fs: do more integrity verification for superblock

Do more sanity check for superblock during ->mount.

Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

static int sanity_check_raw_super(struct super_block *sb,
			struct f2fs_super_block *raw_super)
{
	unsigned int blocksize;

	if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) {
		f2fs_msg(sb, KERN_INFO,
			"Magic Mismatch, valid(0x%x) - read(0x%x)",
			F2FS_SUPER_MAGIC, le32_to_cpu(raw_super->magic));
		return 1;
	}

	/* Currently, support only 4KB page cache size */
	if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid page_cache_size (%lu), supports only 4KB\n",
			PAGE_CACHE_SIZE);
		return 1;
	}

	/* Currently, support only 4KB block size */
	blocksize = 1 << le32_to_cpu(raw_super->log_blocksize);
	if (blocksize != F2FS_BLKSIZE) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid blocksize (%u), supports only 4KB\n",
			blocksize);
		return 1;
	}

	/* Currently, support 512/1024/2048/4096 bytes sector size */
	if (le32_to_cpu(raw_super->log_sectorsize) >
				F2FS_MAX_LOG_SECTOR_SIZE ||
		le32_to_cpu(raw_super->log_sectorsize) <
				F2FS_MIN_LOG_SECTOR_SIZE) {
		f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize (%u)",
			le32_to_cpu(raw_super->log_sectorsize));
		return 1;
	}
	if (le32_to_cpu(raw_super->log_sectors_per_block) +
		le32_to_cpu(raw_super->log_sectorsize) !=
			F2FS_MAX_LOG_SECTOR_SIZE) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid log sectors per block(%u) log sectorsize(%u)",
			le32_to_cpu(raw_super->log_sectors_per_block),
			le32_to_cpu(raw_super->log_sectorsize));
		return 1;
	}
	return 0;
}

A elevation of privilege vulnerability in the Upstream Linux file system. Product: Android. Versions: Android kernel. Android ID: A-36817013.


static int sanity_check_raw_super(struct super_block *sb,
			struct f2fs_super_block *raw_super)
{
	unsigned int blocksize;

	if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) {
		f2fs_msg(sb, KERN_INFO,
			"Magic Mismatch, valid(0x%x) - read(0x%x)",
			F2FS_SUPER_MAGIC, le32_to_cpu(raw_super->magic));
		return 1;
	}

	/* Currently, support only 4KB page cache size */
	if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid page_cache_size (%lu), supports only 4KB\n",
			PAGE_CACHE_SIZE);
		return 1;
	}

	/* Currently, support only 4KB block size */
	blocksize = 1 << le32_to_cpu(raw_super->log_blocksize);
	if (blocksize != F2FS_BLKSIZE) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid blocksize (%u), supports only 4KB\n",
			blocksize);
		return 1;
	}

	/* check log blocks per segment */
	if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid log blocks per segment (%u)\n",
			le32_to_cpu(raw_super->log_blocks_per_seg));
		return 1;
	}

	/* Currently, support 512/1024/2048/4096 bytes sector size */
	if (le32_to_cpu(raw_super->log_sectorsize) >
				F2FS_MAX_LOG_SECTOR_SIZE ||
		le32_to_cpu(raw_super->log_sectorsize) <
				F2FS_MIN_LOG_SECTOR_SIZE) {
		f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize (%u)",
			le32_to_cpu(raw_super->log_sectorsize));
		return 1;
	}
	if (le32_to_cpu(raw_super->log_sectors_per_block) +
		le32_to_cpu(raw_super->log_sectorsize) !=
			F2FS_MAX_LOG_SECTOR_SIZE) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid log sectors per block(%u) log sectorsize(%u)",
			le32_to_cpu(raw_super->log_sectors_per_block),
			le32_to_cpu(raw_super->log_sectorsize));
		return 1;
	}

	/* check reserved ino info */
	if (le32_to_cpu(raw_super->node_ino) != 1 ||
		le32_to_cpu(raw_super->meta_ino) != 2 ||
		le32_to_cpu(raw_super->root_ino) != 3) {
		f2fs_msg(sb, KERN_INFO,
			"Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)",
			le32_to_cpu(raw_super->node_ino),
			le32_to_cpu(raw_super->meta_ino),
			le32_to_cpu(raw_super->root_ino));
		return 1;
	}

	/* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
	if (sanity_check_area_boundary(sb, raw_super))
		return 1;

	return 0;
}


Fix a buffer overflow caused by invalid images

adv_error adv_png_read_ihdr(
	unsigned* pix_width, unsigned* pix_height, unsigned* pix_pixel,
	unsigned char** dat_ptr, unsigned* dat_size,
	unsigned char** pix_ptr, unsigned* pix_scanline,
	unsigned char** pal_ptr, unsigned* pal_size,
	unsigned char** rns_ptr, unsigned* rns_size,
	adv_fz* f, const unsigned char* data, unsigned data_size)
{
	unsigned char* ptr;
	unsigned ptr_size;
	unsigned type;
	unsigned long res_size;
	unsigned pixel;
	unsigned width;
	unsigned width_align;
	unsigned height;
	unsigned depth;
	int r;
	z_stream z;
	adv_bool has_palette;

	*dat_ptr = 0;
	*pix_ptr = 0;
	*pal_ptr = 0;
	*pal_size = 0;
	*rns_ptr = 0;
	*rns_size = 0;

	if (data_size != 13) {
		error_set("Invalid IHDR size %d instead of 13", data_size);
		goto err;
	}

	*pix_width = width = be_uint32_read(data + 0);
	*pix_height = height = be_uint32_read(data + 4);

	depth = data[8];
	if (data[9] == 3 && depth == 8) {
		pixel = 1;
		width_align = width;
		has_palette = 1;
	} else if (data[9] == 3 && depth == 4) {
		pixel = 1;
		width_align = (width + 1) & ~1;
		has_palette = 1;
	} else if (data[9] == 3 && depth == 2) {
		pixel = 1;
		width_align = (width + 3) & ~3;
		has_palette = 1;
	} else if (data[9] == 3 && depth == 1) {
		pixel = 1;
		width_align = (width + 7) & ~7;
		has_palette = 1;
	} else if (data[9] == 2 && depth == 8) {
		pixel = 3;
		width_align = width;
		has_palette = 0;
	} else if (data[9] == 6 && depth == 8) {
		pixel = 4;
		width_align = width;
		has_palette = 0;
	} else {
		error_unsupported_set("Unsupported bit depth/color type, %d/%d", (unsigned)data[8], (unsigned)data[9]);
		goto err;
	}
	*pix_pixel = pixel;

	if (data[10] != 0) { /* compression */
		error_unsupported_set("Unsupported compression, %d instead of 0", (unsigned)data[10]);
		goto err;
	}
	if (data[11] != 0) { /* filter */
		error_unsupported_set("Unsupported filter, %d instead of 0", (unsigned)data[11]);
		goto err;
	}
	if (data[12] != 0) { /* interlace */
		error_unsupported_set("Unsupported interlace %d", (unsigned)data[12]);
		goto err;
	}

	if (adv_png_read_chunk(f, &ptr, &ptr_size, &type) != 0)
		goto err;

	while (type != ADV_PNG_CN_IDAT) {
		if (type == ADV_PNG_CN_PLTE) {
			if (ptr_size > 256*3) {
				error_set("Invalid palette size in PLTE chunk");
				goto err_ptr;
			}

			if (*pal_ptr) {
				error_set("Double palette specification");
				goto err_ptr;
			}

			*pal_ptr = ptr;
			*pal_size = ptr_size;
		} else if (type == ADV_PNG_CN_tRNS) {
			if (*rns_ptr) {
				error_set("Double rns specification");
				goto err_ptr;
			}

			*rns_ptr = ptr;
			*rns_size = ptr_size;
		} else {
			/* ancillary bit. bit 5 of first byte. 0 (uppercase) = critical, 1 (lowercase) = ancillary. */
			if ((type & 0x20000000) == 0) {
				char buf[4];
				be_uint32_write(buf, type);
				error_unsupported_set("Unsupported critical chunk '%c%c%c%c'", buf[0], buf[1], buf[2], buf[3]);
				goto err_ptr;
			}

			free(ptr);
		}

		if (adv_png_read_chunk(f, &ptr, &ptr_size, &type) != 0)
			goto err;
	}

	if (has_palette && !*pal_ptr) {
		error_set("Missing PLTE chunk");
		goto err_ptr;
	}

	if (!has_palette && *pal_ptr) {
		error_set("Unexpected PLTE chunk");
		goto err_ptr;
	}

	*dat_size = height * (width_align * pixel + 1);
	*dat_ptr = malloc(*dat_size);
	*pix_scanline = width_align * pixel + 1;
	*pix_ptr = *dat_ptr + 1;

	z.zalloc = 0;
	z.zfree = 0;
	z.next_out = *dat_ptr;
	z.avail_out = *dat_size;
	z.next_in = 0;
	z.avail_in = 0;

	r = inflateInit(&z);

	while (r == Z_OK && type == ADV_PNG_CN_IDAT) {
		z.next_in = ptr;
		z.avail_in = ptr_size;

		r = inflate(&z, Z_NO_FLUSH);

		free(ptr);

		if (adv_png_read_chunk(f, &ptr, &ptr_size, &type) != 0) {
			inflateEnd(&z);
			goto err;
		}
	}

	res_size = z.total_out;

	inflateEnd(&z);

	if (r != Z_STREAM_END) {
		error_set("Invalid compressed data");
		goto err_ptr;
	}

	if (depth == 8) {
		if (res_size != *dat_size) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		if (pixel == 1)
			adv_png_unfilter_8(width * pixel, height, *dat_ptr, width_align * pixel + 1);
		else if (pixel == 3)
			adv_png_unfilter_24(width * pixel, height, *dat_ptr, width_align * pixel + 1);
		else if (pixel == 4)
			adv_png_unfilter_32(width * pixel, height, *dat_ptr, width_align * pixel + 1);
		else {
			error_set("Unsupported format");
			goto err_ptr;
		}
	} else if (depth == 4) {
		if (res_size != height * (width_align / 2 + 1)) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		adv_png_unfilter_8(width_align / 2, height, *dat_ptr, width_align / 2 + 1);

		adv_png_expand_4(width_align, height, *dat_ptr);
	} else if (depth == 2) {
		if (res_size != height * (width_align / 4 + 1)) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		adv_png_unfilter_8(width_align / 4, height, *dat_ptr, width_align / 4 + 1);

		adv_png_expand_2(width_align, height, *dat_ptr);
	} else if (depth == 1) {
		if (res_size != height * (width_align / 8 + 1)) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		adv_png_unfilter_8(width_align / 8, height, *dat_ptr, width_align / 8 + 1);

		adv_png_expand_1(width_align, height, *dat_ptr);
	}

	if (adv_png_read_iend(f, ptr, ptr_size, type)!=0) {
		goto err_ptr;
	}

	free(ptr);
	return 0;

err_ptr:
	free(ptr);
err:
	free(*dat_ptr);
	free(*pal_ptr);
	free(*rns_ptr);
	return -1;
}

An issue was discovered in AdvanceCOMP through 2.1. An invalid memory address occurs in the function adv_png_unfilter_8 in lib/png.c. It can be triggered by sending a crafted file to a binary. It allows an attacker to cause a Denial of Service (Segmentation fault) or possibly have unspecified other impact when a victim opens a specially crafted file.


adv_error adv_png_read_ihdr(
	unsigned* pix_width, unsigned* pix_height, unsigned* pix_pixel,
	unsigned char** dat_ptr, unsigned* dat_size,
	unsigned char** pix_ptr, unsigned* pix_scanline,
	unsigned char** pal_ptr, unsigned* pal_size,
	unsigned char** rns_ptr, unsigned* rns_size,
	adv_fz* f, const unsigned char* data, unsigned data_size)
{
	unsigned char* ptr;
	unsigned ptr_size;
	unsigned type;
	unsigned long res_size;
	unsigned pixel;
	unsigned width;
	unsigned width_align;
	unsigned scanline;
	unsigned height;
	unsigned depth;
	int r;
	z_stream z;
	adv_bool has_palette;

	*dat_ptr = 0;
	*pix_ptr = 0;
	*pal_ptr = 0;
	*pal_size = 0;
	*rns_ptr = 0;
	*rns_size = 0;

	if (data_size != 13) {
		error_set("Invalid IHDR size %d instead of 13", data_size);
		goto err;
	}

	*pix_width = width = be_uint32_read(data + 0);
	*pix_height = height = be_uint32_read(data + 4);

	depth = data[8];
	if (data[9] == 3 && depth == 8) {
		pixel = 1;
		width_align = width;
		has_palette = 1;
	} else if (data[9] == 3 && depth == 4) {
		pixel = 1;
		width_align = (width + 1) & ~1;
		has_palette = 1;
	} else if (data[9] == 3 && depth == 2) {
		pixel = 1;
		width_align = (width + 3) & ~3;
		has_palette = 1;
	} else if (data[9] == 3 && depth == 1) {
		pixel = 1;
		width_align = (width + 7) & ~7;
		has_palette = 1;
	} else if (data[9] == 2 && depth == 8) {
		pixel = 3;
		width_align = width;
		has_palette = 0;
	} else if (data[9] == 6 && depth == 8) {
		pixel = 4;
		width_align = width;
		has_palette = 0;
	} else {
		error_unsupported_set("Unsupported bit depth/color type, %d/%d", (unsigned)data[8], (unsigned)data[9]);
		goto err;
	}
	*pix_pixel = pixel;

	if (data[10] != 0) { /* compression */
		error_unsupported_set("Unsupported compression, %d instead of 0", (unsigned)data[10]);
		goto err;
	}
	if (data[11] != 0) { /* filter */
		error_unsupported_set("Unsupported filter, %d instead of 0", (unsigned)data[11]);
		goto err;
	}
	if (data[12] != 0) { /* interlace */
		error_unsupported_set("Unsupported interlace %d", (unsigned)data[12]);
		goto err;
	}

	if (adv_png_read_chunk(f, &ptr, &ptr_size, &type) != 0)
		goto err;

	while (type != ADV_PNG_CN_IDAT) {
		if (type == ADV_PNG_CN_PLTE) {
			if (ptr_size > 256*3) {
				error_set("Invalid palette size in PLTE chunk");
				goto err_ptr;
			}

			if (*pal_ptr) {
				error_set("Double palette specification");
				goto err_ptr;
			}

			*pal_ptr = ptr;
			*pal_size = ptr_size;
		} else if (type == ADV_PNG_CN_tRNS) {
			if (*rns_ptr) {
				error_set("Double rns specification");
				goto err_ptr;
			}

			*rns_ptr = ptr;
			*rns_size = ptr_size;
		} else {
			/* ancillary bit. bit 5 of first byte. 0 (uppercase) = critical, 1 (lowercase) = ancillary. */
			if ((type & 0x20000000) == 0) {
				char buf[4];
				be_uint32_write(buf, type);
				error_unsupported_set("Unsupported critical chunk '%c%c%c%c'", buf[0], buf[1], buf[2], buf[3]);
				goto err_ptr;
			}

			free(ptr);
		}

		if (adv_png_read_chunk(f, &ptr, &ptr_size, &type) != 0)
			goto err;
	}

	if (has_palette && !*pal_ptr) {
		error_set("Missing PLTE chunk");
		goto err_ptr;
	}

	if (!has_palette && *pal_ptr) {
		error_set("Unexpected PLTE chunk");
		goto err_ptr;
	}

	/* check for overflow */
	if (pixel == 0 || width_align >= UINT_MAX / pixel) {
		error_set("Invalid image size");
		goto err_ptr;
	}

	scanline = width_align * pixel + 1;

	/* check for overflow */
	if (scanline == 0 || height >= UINT_MAX / scanline) {
		error_set("Invalid image size");
		goto err_ptr;
	}

	*dat_size = height * scanline;
	*dat_ptr = malloc(*dat_size);
	*pix_scanline = scanline;
	*pix_ptr = *dat_ptr + 1;

	z.zalloc = 0;
	z.zfree = 0;
	z.next_out = *dat_ptr;
	z.avail_out = *dat_size;
	z.next_in = 0;
	z.avail_in = 0;

	r = inflateInit(&z);

	while (r == Z_OK && type == ADV_PNG_CN_IDAT) {
		z.next_in = ptr;
		z.avail_in = ptr_size;

		r = inflate(&z, Z_NO_FLUSH);

		free(ptr);

		if (adv_png_read_chunk(f, &ptr, &ptr_size, &type) != 0) {
			inflateEnd(&z);
			goto err;
		}
	}

	res_size = z.total_out;

	inflateEnd(&z);

	if (r != Z_STREAM_END) {
		error_set("Invalid compressed data");
		goto err_ptr;
	}

	if (depth == 8) {
		if (res_size != *dat_size) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		if (pixel == 1)
			adv_png_unfilter_8(width * pixel, height, *dat_ptr, width_align * pixel + 1);
		else if (pixel == 3)
			adv_png_unfilter_24(width * pixel, height, *dat_ptr, width_align * pixel + 1);
		else if (pixel == 4)
			adv_png_unfilter_32(width * pixel, height, *dat_ptr, width_align * pixel + 1);
		else {
			error_set("Unsupported format");
			goto err_ptr;
		}
	} else if (depth == 4) {
		if (res_size != height * (width_align / 2 + 1)) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		adv_png_unfilter_8(width_align / 2, height, *dat_ptr, width_align / 2 + 1);

		adv_png_expand_4(width_align, height, *dat_ptr);
	} else if (depth == 2) {
		if (res_size != height * (width_align / 4 + 1)) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		adv_png_unfilter_8(width_align / 4, height, *dat_ptr, width_align / 4 + 1);

		adv_png_expand_2(width_align, height, *dat_ptr);
	} else if (depth == 1) {
		if (res_size != height * (width_align / 8 + 1)) {
			error_set("Invalid decompressed size");
			goto err_ptr;
		}

		adv_png_unfilter_8(width_align / 8, height, *dat_ptr, width_align / 8 + 1);

		adv_png_expand_1(width_align, height, *dat_ptr);
	}

	if (adv_png_read_iend(f, ptr, ptr_size, type)!=0) {
		goto err_ptr;
	}

	free(ptr);
	return 0;

err_ptr:
	free(ptr);
err:
	free(*dat_ptr);
	free(*pal_ptr);
	free(*rns_ptr);
	return -1;
}


Fix assertion in debug mode / heap-based buffer overflow in opj_write_bytes_LE for Cinema profiles with numresolutions = 1 (#985)

static void opj_j2k_set_cinema_parameters(opj_cparameters_t *parameters,
        opj_image_t *image, opj_event_mgr_t *p_manager)
{
    /* Configure cinema parameters */
    int i;

    /* No tiling */
    parameters->tile_size_on = OPJ_FALSE;
    parameters->cp_tdx = 1;
    parameters->cp_tdy = 1;

    /* One tile part for each component */
    parameters->tp_flag = 'C';
    parameters->tp_on = 1;

    /* Tile and Image shall be at (0,0) */
    parameters->cp_tx0 = 0;
    parameters->cp_ty0 = 0;
    parameters->image_offset_x0 = 0;
    parameters->image_offset_y0 = 0;

    /* Codeblock size= 32*32 */
    parameters->cblockw_init = 32;
    parameters->cblockh_init = 32;

    /* Codeblock style: no mode switch enabled */
    parameters->mode = 0;

    /* No ROI */
    parameters->roi_compno = -1;

    /* No subsampling */
    parameters->subsampling_dx = 1;
    parameters->subsampling_dy = 1;

    /* 9-7 transform */
    parameters->irreversible = 1;

    /* Number of layers */
    if (parameters->tcp_numlayers > 1) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "1 single quality layer"
                      "-> Number of layers forced to 1 (rather than %d)\n"
                      "-> Rate of the last layer (%3.1f) will be used",
                      parameters->tcp_numlayers,
                      parameters->tcp_rates[parameters->tcp_numlayers - 1]);
        parameters->tcp_rates[0] = parameters->tcp_rates[parameters->tcp_numlayers - 1];
        parameters->tcp_numlayers = 1;
    }

    /* Resolution levels */
    switch (parameters->rsiz) {
    case OPJ_PROFILE_CINEMA_2K:
        if (parameters->numresolution > 6) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
                          "Number of decomposition levels <= 5\n"
                          "-> Number of decomposition levels forced to 5 (rather than %d)\n",
                          parameters->numresolution + 1);
            parameters->numresolution = 6;
        }
        break;
    case OPJ_PROFILE_CINEMA_4K:
        if (parameters->numresolution < 2) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
                          "Number of decomposition levels >= 1 && <= 6\n"
                          "-> Number of decomposition levels forced to 1 (rather than %d)\n",
                          parameters->numresolution + 1);
            parameters->numresolution = 1;
        } else if (parameters->numresolution > 7) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
                          "Number of decomposition levels >= 1 && <= 6\n"
                          "-> Number of decomposition levels forced to 6 (rather than %d)\n",
                          parameters->numresolution + 1);
            parameters->numresolution = 7;
        }
        break;
    default :
        break;
    }

    /* Precincts */
    parameters->csty |= 0x01;
    parameters->res_spec = parameters->numresolution - 1;
    for (i = 0; i < parameters->res_spec; i++) {
        parameters->prcw_init[i] = 256;
        parameters->prch_init[i] = 256;
    }

    /* The progression order shall be CPRL */
    parameters->prog_order = OPJ_CPRL;

    /* Progression order changes for 4K, disallowed for 2K */
    if (parameters->rsiz == OPJ_PROFILE_CINEMA_4K) {
        parameters->numpocs = (OPJ_UINT32)opj_j2k_initialise_4K_poc(parameters->POC,
                              parameters->numresolution);
    } else {
        parameters->numpocs = 0;
    }

    /* Limited bit-rate */
    parameters->cp_disto_alloc = 1;
    if (parameters->max_cs_size <= 0) {
        /* No rate has been introduced, 24 fps is assumed */
        parameters->max_cs_size = OPJ_CINEMA_24_CS;
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1302083 compressed bytes @ 24fps\n"
                      "As no rate has been given, this limit will be used.\n");
    } else if (parameters->max_cs_size > OPJ_CINEMA_24_CS) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1302083 compressed bytes @ 24fps\n"
                      "-> Specified rate exceeds this limit. Rate will be forced to 1302083 bytes.\n");
        parameters->max_cs_size = OPJ_CINEMA_24_CS;
    }

    if (parameters->max_comp_size <= 0) {
        /* No rate has been introduced, 24 fps is assumed */
        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1041666 compressed bytes @ 24fps\n"
                      "As no rate has been given, this limit will be used.\n");
    } else if (parameters->max_comp_size > OPJ_CINEMA_24_COMP) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1041666 compressed bytes @ 24fps\n"
                      "-> Specified rate exceeds this limit. Rate will be forced to 1041666 bytes.\n");
        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
    }

    parameters->tcp_rates[0] = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
                               image->comps[0].h * image->comps[0].prec) /
                               (OPJ_FLOAT32)(((OPJ_UINT32)parameters->max_cs_size) * 8 * image->comps[0].dx *
                                       image->comps[0].dy);

}

A mishandled zero case was discovered in opj_j2k_set_cinema_parameters in lib/openjp2/j2k.c in OpenJPEG 2.2.0. The vulnerability causes an out-of-bounds write, which may lead to remote denial of service (heap-based buffer overflow affecting opj_write_bytes_LE in lib/openjp2/cio.c and opj_j2k_write_sot in lib/openjp2/j2k.c) or possibly remote code execution.


static void opj_j2k_set_cinema_parameters(opj_cparameters_t *parameters,
        opj_image_t *image, opj_event_mgr_t *p_manager)
{
    /* Configure cinema parameters */
    int i;

    /* No tiling */
    parameters->tile_size_on = OPJ_FALSE;
    parameters->cp_tdx = 1;
    parameters->cp_tdy = 1;

    /* One tile part for each component */
    parameters->tp_flag = 'C';
    parameters->tp_on = 1;

    /* Tile and Image shall be at (0,0) */
    parameters->cp_tx0 = 0;
    parameters->cp_ty0 = 0;
    parameters->image_offset_x0 = 0;
    parameters->image_offset_y0 = 0;

    /* Codeblock size= 32*32 */
    parameters->cblockw_init = 32;
    parameters->cblockh_init = 32;

    /* Codeblock style: no mode switch enabled */
    parameters->mode = 0;

    /* No ROI */
    parameters->roi_compno = -1;

    /* No subsampling */
    parameters->subsampling_dx = 1;
    parameters->subsampling_dy = 1;

    /* 9-7 transform */
    parameters->irreversible = 1;

    /* Number of layers */
    if (parameters->tcp_numlayers > 1) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "1 single quality layer"
                      "-> Number of layers forced to 1 (rather than %d)\n"
                      "-> Rate of the last layer (%3.1f) will be used",
                      parameters->tcp_numlayers,
                      parameters->tcp_rates[parameters->tcp_numlayers - 1]);
        parameters->tcp_rates[0] = parameters->tcp_rates[parameters->tcp_numlayers - 1];
        parameters->tcp_numlayers = 1;
    }

    /* Resolution levels */
    switch (parameters->rsiz) {
    case OPJ_PROFILE_CINEMA_2K:
        if (parameters->numresolution > 6) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
                          "Number of decomposition levels <= 5\n"
                          "-> Number of decomposition levels forced to 5 (rather than %d)\n",
                          parameters->numresolution + 1);
            parameters->numresolution = 6;
        }
        break;
    case OPJ_PROFILE_CINEMA_4K:
        if (parameters->numresolution < 2) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
                          "Number of decomposition levels >= 1 && <= 6\n"
                          "-> Number of decomposition levels forced to 1 (rather than %d)\n",
                          parameters->numresolution + 1);
            parameters->numresolution = 1;
        } else if (parameters->numresolution > 7) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
                          "Number of decomposition levels >= 1 && <= 6\n"
                          "-> Number of decomposition levels forced to 6 (rather than %d)\n",
                          parameters->numresolution + 1);
            parameters->numresolution = 7;
        }
        break;
    default :
        break;
    }

    /* Precincts */
    parameters->csty |= 0x01;
    if (parameters->numresolution == 1) {
        parameters->res_spec = 1;
        parameters->prcw_init[0] = 128;
        parameters->prch_init[0] = 128;
    } else {
        parameters->res_spec = parameters->numresolution - 1;
        for (i = 0; i < parameters->res_spec; i++) {
            parameters->prcw_init[i] = 256;
            parameters->prch_init[i] = 256;
        }
    }

    /* The progression order shall be CPRL */
    parameters->prog_order = OPJ_CPRL;

    /* Progression order changes for 4K, disallowed for 2K */
    if (parameters->rsiz == OPJ_PROFILE_CINEMA_4K) {
        parameters->numpocs = (OPJ_UINT32)opj_j2k_initialise_4K_poc(parameters->POC,
                              parameters->numresolution);
    } else {
        parameters->numpocs = 0;
    }

    /* Limited bit-rate */
    parameters->cp_disto_alloc = 1;
    if (parameters->max_cs_size <= 0) {
        /* No rate has been introduced, 24 fps is assumed */
        parameters->max_cs_size = OPJ_CINEMA_24_CS;
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1302083 compressed bytes @ 24fps\n"
                      "As no rate has been given, this limit will be used.\n");
    } else if (parameters->max_cs_size > OPJ_CINEMA_24_CS) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1302083 compressed bytes @ 24fps\n"
                      "-> Specified rate exceeds this limit. Rate will be forced to 1302083 bytes.\n");
        parameters->max_cs_size = OPJ_CINEMA_24_CS;
    }

    if (parameters->max_comp_size <= 0) {
        /* No rate has been introduced, 24 fps is assumed */
        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1041666 compressed bytes @ 24fps\n"
                      "As no rate has been given, this limit will be used.\n");
    } else if (parameters->max_comp_size > OPJ_CINEMA_24_COMP) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
                      "Maximum 1041666 compressed bytes @ 24fps\n"
                      "-> Specified rate exceeds this limit. Rate will be forced to 1041666 bytes.\n");
        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
    }

    parameters->tcp_rates[0] = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
                               image->comps[0].h * image->comps[0].prec) /
                               (OPJ_FLOAT32)(((OPJ_UINT32)parameters->max_cs_size) * 8 * image->comps[0].dx *
                                       image->comps[0].dy);

}


static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
  XWindows *windows,const CommandType command_type,Image **image,
  MagickStatusType *state,ExceptionInfo *exception)
{
  Image
    *nexus;

  MagickBooleanType
    proceed;

  MagickStatusType
    status;

  XTextProperty
    window_name;

  /*
    Process user command.
  */
  nexus=NewImageList();
  switch (command_type)
  {
    case OpenCommand:
    {
      char
        **filelist;

      Image
        *images,
        *next;

      ImageInfo
        *read_info;

      int
        number_files;

      register int
        i;

      static char
        filenames[MagickPathExtent] = "*";

      if (resource_info->immutable != MagickFalse)
        break;
      /*
        Request file name from user.
      */
      XFileBrowserWidget(display,windows,"Animate",filenames);
      if (*filenames == '\0')
        return((Image *) NULL);
      /*
        Expand the filenames.
      */
      filelist=(char **) AcquireMagickMemory(sizeof(char *));
      if (filelist == (char **) NULL)
        {
          ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
            filenames);
          return((Image *) NULL);
        }
      number_files=1;
      filelist[0]=filenames;
      status=ExpandFilenames(&number_files,&filelist);
      if ((status == MagickFalse) || (number_files == 0))
        {
          if (number_files == 0)
            {
              ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
             return((Image *) NULL);
            }
          ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
            filenames);
          return((Image *) NULL);
        }
      read_info=CloneImageInfo(resource_info->image_info);
      images=NewImageList();
      XSetCursorState(display,windows,MagickTrue);
      XCheckRefreshWindows(display,windows);
      for (i=0; i < number_files; i++)
      {
        (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
        filelist[i]=DestroyString(filelist[i]);
        *read_info->magick='\0';
        next=ReadImage(read_info,exception);
        CatchException(exception);
        if (next != (Image *) NULL)
          AppendImageToList(&images,next);
        if (number_files <= 5)
          continue;
        proceed=SetImageProgress(images,LoadImageTag,i,(MagickSizeType)
          number_files);
        if (proceed == MagickFalse)
          break;
      }
      filelist=(char **) RelinquishMagickMemory(filelist);
      read_info=DestroyImageInfo(read_info);
      if (images == (Image *) NULL)
        {
          XSetCursorState(display,windows,MagickFalse);
          ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
          return((Image *) NULL);
        }
      nexus=GetFirstImageInList(images);
      *state|=ExitState;
      break;
    }
    case PlayCommand:
    {
      char
        basename[MagickPathExtent];

      int
        status;

      /*
        Window name is the base of the filename.
      */
      *state|=PlayAnimationState;
      *state&=(~AutoReverseAnimationState);
      GetPathComponent((*image)->magick_filename,BasePath,basename);
      (void) FormatLocaleString(windows->image.name,MagickPathExtent,
        "%s: %s",MagickPackageName,basename);
      if (resource_info->title != (char *) NULL)
        {
          char
            *title;

          title=InterpretImageProperties(resource_info->image_info,*image,
            resource_info->title,exception);
          (void) CopyMagickString(windows->image.name,title,MagickPathExtent);
          title=DestroyString(title);
        }
      status=XStringListToTextProperty(&windows->image.name,1,&window_name);
      if (status == 0)
        break;
      XSetWMName(display,windows->image.id,&window_name);
      (void) XFree((void *) window_name.value);
      break;
    }
    case StepCommand:
    case StepBackwardCommand:
    case StepForwardCommand:
    {
      *state|=StepAnimationState;
      *state&=(~PlayAnimationState);
      if (command_type == StepBackwardCommand)
        *state&=(~ForwardAnimationState);
      if (command_type == StepForwardCommand)
        *state|=ForwardAnimationState;
      if (resource_info->title != (char *) NULL)
        break;
      break;
    }
    case RepeatCommand:
    {
      *state|=RepeatAnimationState;
      *state&=(~AutoReverseAnimationState);
      *state|=PlayAnimationState;
      break;
    }
    case AutoReverseCommand:
    {
      *state|=AutoReverseAnimationState;
      *state&=(~RepeatAnimationState);
      *state|=PlayAnimationState;
      break;
    }
    case SaveCommand:
    {
      /*
        Save image.
      */
      status=XSaveImage(display,resource_info,windows,*image,exception);
      if (status == MagickFalse)
        {
          char
            message[MagickPathExtent];

          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
            exception->reason != (char *) NULL ? exception->reason : "",
            exception->description != (char *) NULL ? exception->description :
            "");
          XNoticeWidget(display,windows,"Unable to save file:",message);
          break;
        }
      break;
    }
    case SlowerCommand:
    {
      resource_info->delay++;
      break;
    }
    case FasterCommand:
    {
      if (resource_info->delay == 0)
        break;
      resource_info->delay--;
      break;
    }
    case ForwardCommand:
    {
      *state=ForwardAnimationState;
      *state&=(~AutoReverseAnimationState);
      break;
    }
    case ReverseCommand:
    {
      *state&=(~ForwardAnimationState);
      *state&=(~AutoReverseAnimationState);
      break;
    }
    case InfoCommand:
    {
      XDisplayImageInfo(display,resource_info,windows,(Image *) NULL,*image,
        exception);
      break;
    }
    case HelpCommand:
    {
      /*
        User requested help.
      */
      XTextViewWidget(display,resource_info,windows,MagickFalse,
        "Help Viewer - Animate",AnimateHelp);
      break;
    }
    case BrowseDocumentationCommand:
    {
      Atom
        mozilla_atom;

      Window
        mozilla_window,
        root_window;

      /*
        Browse the ImageMagick documentation.
      */
      root_window=XRootWindow(display,XDefaultScreen(display));
      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
      if (mozilla_window != (Window) NULL)
        {
          char
            command[MagickPathExtent],
            *url;

          /*
            Display documentation using Netscape remote control.
          */
          url=GetMagickHomeURL();
          (void) FormatLocaleString(command,MagickPathExtent,
            "openurl(%s,new-tab)",url);
          url=DestroyString(url);
          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
          (void) XChangeProperty(display,mozilla_window,mozilla_atom,
            XA_STRING,8,PropModeReplace,(unsigned char *) command,
            (int) strlen(command));
          XSetCursorState(display,windows,MagickFalse);
          break;
        }
      XSetCursorState(display,windows,MagickTrue);
      XCheckRefreshWindows(display,windows);
      status=InvokeDelegate(resource_info->image_info,*image,"browse",
        (char *) NULL,exception);
      if (status == MagickFalse)
        XNoticeWidget(display,windows,"Unable to browse documentation",
          (char *) NULL);
      XDelay(display,1500);
      XSetCursorState(display,windows,MagickFalse);
      break;
    }
    case VersionCommand:
    {
      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
        GetMagickCopyright());
      break;
    }
    case QuitCommand:
    {
      /*
        exit program
      */
      if (resource_info->confirm_exit == MagickFalse)
        XClientMessage(display,windows->image.id,windows->im_protocols,
          windows->im_exit,CurrentTime);
      else
        {
          int
            status;

          /*
            Confirm program exit.
          */
          status=XConfirmWidget(display,windows,"Do you really want to exit",
            resource_info->client_name);
          if (status != 0)
            XClientMessage(display,windows->image.id,windows->im_protocols,
              windows->im_exit,CurrentTime);
        }
      break;
    }
    default:
      break;
  }
  return(nexus);
}

In ImageMagick 7.0.8-4, there is a memory leak in the XMagickCommand function in MagickCore/animate.c.