static Image *ReadBMPImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  BMPInfo
    bmp_info;

  Image
    *image;

  IndexPacket
    index;

  MagickBooleanType
    status;

  MagickOffsetType
    offset,
    start_position;

  MemoryInfo
    *pixel_info;

  register IndexPacket
    *indexes;

  register PixelPacket
    *q;

  register ssize_t
    i,
    x;

  register unsigned char
    *p;

  size_t
    bit,
    bytes_per_line,
    length;

  ssize_t
    count,
    y;

  unsigned char
    magick[12],
    *pixels;

  unsigned int
    blue,
    green,
    offset_bits,
    red;

  /*
    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);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Determine if this a BMP file.
  */
  (void) memset(&bmp_info,0,sizeof(bmp_info));
  bmp_info.ba_offset=0;
  start_position=0;
  offset_bits=0;
  count=ReadBlob(image,2,magick);
  if (count != 2)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  do
  {
    LongPixelPacket
      shift;

    PixelPacket
      quantum_bits;

    /*
      Verify BMP identifier.
    */
    start_position=TellBlob(image)-2;
    bmp_info.ba_offset=0;
    while (LocaleNCompare((char *) magick,"BA",2) == 0)
    {
      bmp_info.file_size=ReadBlobLSBLong(image);
      bmp_info.ba_offset=ReadBlobLSBLong(image);
      bmp_info.offset_bits=ReadBlobLSBLong(image);
      count=ReadBlob(image,2,magick);
      if (count != 2)
        break;
    }
    if (image->debug != MagickFalse)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Magick: %c%c",
        magick[0],magick[1]);
    if ((count != 2) || ((LocaleNCompare((char *) magick,"BM",2) != 0) &&
        (LocaleNCompare((char *) magick,"CI",2) != 0)))
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    bmp_info.file_size=ReadBlobLSBLong(image);
    (void) ReadBlobLSBLong(image);

    if (image->debug != MagickFalse)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
         "  File_size in header:  %u bytes",bmp_info.file_size);

    bmp_info.offset_bits=ReadBlobLSBLong(image);
    bmp_info.size=ReadBlobLSBLong(image);
    if (image->debug != MagickFalse)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  BMP size: %u",
        bmp_info.size);
    if (bmp_info.size == 12)
      {
        /*
          OS/2 BMP image file.
        */
        (void) CopyMagickString(image->magick,"BMP2",MaxTextExtent);
        bmp_info.width=(ssize_t) ((short) ReadBlobLSBShort(image));
        bmp_info.height=(ssize_t) ((short) ReadBlobLSBShort(image));
        bmp_info.planes=ReadBlobLSBShort(image);
        bmp_info.bits_per_pixel=ReadBlobLSBShort(image);
        bmp_info.x_pixels=0;
        bmp_info.y_pixels=0;
        bmp_info.number_colors=0;
        bmp_info.compression=BI_RGB;
        bmp_info.image_size=0;
        bmp_info.alpha_mask=0;
        if (image->debug != MagickFalse)
          {
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
              "  Format: OS/2 Bitmap");
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
              "  Geometry: %.20gx%.20g",(double) bmp_info.width,(double)
              bmp_info.height);
          }
      }
    else
      {
        /*
          Microsoft Windows BMP image file.
        */
        if (bmp_info.size < 40)
          ThrowReaderException(CorruptImageError,"NonOS2HeaderSizeError");
        bmp_info.width=(ssize_t) ReadBlobLSBSignedLong(image);
        bmp_info.height=(ssize_t) ReadBlobLSBSignedLong(image);
        bmp_info.planes=ReadBlobLSBShort(image);
        bmp_info.bits_per_pixel=ReadBlobLSBShort(image);
        bmp_info.compression=ReadBlobLSBLong(image);
        bmp_info.image_size=ReadBlobLSBLong(image);
        bmp_info.x_pixels=ReadBlobLSBLong(image);
        bmp_info.y_pixels=ReadBlobLSBLong(image);
        bmp_info.number_colors=ReadBlobLSBLong(image);
        if ((MagickSizeType) bmp_info.number_colors > GetBlobSize(image))
          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
        bmp_info.colors_important=ReadBlobLSBLong(image);
        if (image->debug != MagickFalse)
          {
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
              "  Format: MS Windows bitmap");
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
              "  Geometry: %.20gx%.20g",(double) bmp_info.width,(double)
              bmp_info.height);
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
              "  Bits per pixel: %.20g",(double) bmp_info.bits_per_pixel);
            switch (bmp_info.compression)
            {
              case BI_RGB:
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "  Compression: BI_RGB");
                break;
              }
              case BI_RLE4:
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "  Compression: BI_RLE4");
                break;
              }
              case BI_RLE8:
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "  Compression: BI_RLE8");
                break;
              }
              case BI_BITFIELDS:
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "  Compression: BI_BITFIELDS");
                break;
              }
              case BI_PNG:
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "  Compression: BI_PNG");
                break;
              }
              case BI_JPEG:
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "  Compression: BI_JPEG");
                break;
              }
              default:
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "  Compression: UNKNOWN (%u)",bmp_info.compression);
              }
            }
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
              "  Number of colors: %u",bmp_info.number_colors);
          }
        bmp_info.red_mask=ReadBlobLSBLong(image);
        bmp_info.green_mask=ReadBlobLSBLong(image);
        bmp_info.blue_mask=ReadBlobLSBLong(image);
        if (bmp_info.size > 40)
          {
            double
              gamma;

            /*
              Read color management information.
            */
            bmp_info.alpha_mask=ReadBlobLSBLong(image);
            bmp_info.colorspace=ReadBlobLSBSignedLong(image);
            /*
              Decode 2^30 fixed point formatted CIE primaries.
            */
#           define BMP_DENOM ((double) 0x40000000)
            bmp_info.red_primary.x=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.red_primary.y=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.red_primary.z=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.green_primary.x=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.green_primary.y=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.green_primary.z=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.blue_primary.x=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.blue_primary.y=(double) ReadBlobLSBLong(image)/BMP_DENOM;
            bmp_info.blue_primary.z=(double) ReadBlobLSBLong(image)/BMP_DENOM;

            gamma=bmp_info.red_primary.x+bmp_info.red_primary.y+
              bmp_info.red_primary.z;
            gamma=PerceptibleReciprocal(gamma);
            bmp_info.red_primary.x*=gamma;
            bmp_info.red_primary.y*=gamma;
            image->chromaticity.red_primary.x=bmp_info.red_primary.x;
            image->chromaticity.red_primary.y=bmp_info.red_primary.y;

            gamma=bmp_info.green_primary.x+bmp_info.green_primary.y+
              bmp_info.green_primary.z;
            gamma=PerceptibleReciprocal(gamma);
            bmp_info.green_primary.x*=gamma;
            bmp_info.green_primary.y*=gamma;
            image->chromaticity.green_primary.x=bmp_info.green_primary.x;
            image->chromaticity.green_primary.y=bmp_info.green_primary.y;

            gamma=bmp_info.blue_primary.x+bmp_info.blue_primary.y+
              bmp_info.blue_primary.z;
            gamma=PerceptibleReciprocal(gamma);
            bmp_info.blue_primary.x*=gamma;
            bmp_info.blue_primary.y*=gamma;
            image->chromaticity.blue_primary.x=bmp_info.blue_primary.x;
            image->chromaticity.blue_primary.y=bmp_info.blue_primary.y;

            /*
              Decode 16^16 fixed point formatted gamma_scales.
            */
            bmp_info.gamma_scale.x=(double) ReadBlobLSBLong(image)/0x10000;
            bmp_info.gamma_scale.y=(double) ReadBlobLSBLong(image)/0x10000;
            bmp_info.gamma_scale.z=(double) ReadBlobLSBLong(image)/0x10000;
            /*
              Compute a single gamma from the BMP 3-channel gamma.
            */
            image->gamma=(bmp_info.gamma_scale.x+bmp_info.gamma_scale.y+
              bmp_info.gamma_scale.z)/3.0;
          }
        else
          (void) CopyMagickString(image->magick,"BMP3",MaxTextExtent);

        if (bmp_info.size > 108)
          {
            size_t
              intent;

            /*
              Read BMP Version 5 color management information.
            */
            intent=ReadBlobLSBLong(image);
            switch ((int) intent)
            {
              case LCS_GM_BUSINESS:
              {
                image->rendering_intent=SaturationIntent;
                break;
              }
              case LCS_GM_GRAPHICS:
              {
                image->rendering_intent=RelativeIntent;
                break;
              }
              case LCS_GM_IMAGES:
              {
                image->rendering_intent=PerceptualIntent;
                break;
              }
              case LCS_GM_ABS_COLORIMETRIC:
              {
                image->rendering_intent=AbsoluteIntent;
                break;
              }
            }
            (void) ReadBlobLSBLong(image);  /* Profile data */
            (void) ReadBlobLSBLong(image);  /* Profile size */
            (void) ReadBlobLSBLong(image);  /* Reserved byte */
          }
      }
    if ((MagickSizeType) bmp_info.file_size > GetBlobSize(image))
      (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
        "LengthAndFilesizeDoNotMatch","`%s'",image->filename);
    else
      if ((MagickSizeType) bmp_info.file_size < GetBlobSize(image))
        (void) ThrowMagickException(exception,GetMagickModule(),
          CorruptImageWarning,"LengthAndFilesizeDoNotMatch","`%s'",
          image->filename);
    if (bmp_info.width <= 0)
      ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
    if (bmp_info.height == 0)
      ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
    if (bmp_info.planes != 1)
      ThrowReaderException(CorruptImageError,"StaticPlanesValueNotEqualToOne");
    if ((bmp_info.bits_per_pixel != 1) && (bmp_info.bits_per_pixel != 4) &&
        (bmp_info.bits_per_pixel != 8) && (bmp_info.bits_per_pixel != 16) &&
        (bmp_info.bits_per_pixel != 24) && (bmp_info.bits_per_pixel != 32))
      ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
    if (bmp_info.bits_per_pixel < 16 &&
        bmp_info.number_colors > (1U << bmp_info.bits_per_pixel))
      ThrowReaderException(CorruptImageError,"UnrecognizedNumberOfColors");
    if ((bmp_info.compression == 1) && (bmp_info.bits_per_pixel != 8))
      ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
    if ((bmp_info.compression == 2) && (bmp_info.bits_per_pixel != 4))
      ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
    if ((bmp_info.compression == 3) && (bmp_info.bits_per_pixel < 16))
      ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
    switch (bmp_info.compression)
    {
      case BI_RGB:
        image->compression=NoCompression;
        break;
      case BI_RLE8:
      case BI_RLE4:
        image->compression=RLECompression;
        break;
      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(bmp_info.width);
    image->rows=(size_t) MagickAbsoluteValue(bmp_info.height);
    image->depth=bmp_info.bits_per_pixel <= 8 ? bmp_info.bits_per_pixel : 8;
    image->matte=((bmp_info.alpha_mask != 0) &&
      (bmp_info.compression == BI_BITFIELDS)) ? MagickTrue : MagickFalse;
    if (bmp_info.bits_per_pixel < 16)
      {
        size_t
          one;

        image->storage_class=PseudoClass;
        image->colors=bmp_info.number_colors;
        one=1;
        if (image->colors == 0)
          image->colors=one << bmp_info.bits_per_pixel;
      }
    image->x_resolution=(double) bmp_info.x_pixels/100.0;
    image->y_resolution=(double) bmp_info.y_pixels/100.0;
    image->units=PixelsPerCentimeterResolution;
    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    status=SetImageExtent(image,image->columns,image->rows);
    if (status == MagickFalse)
      {
        InheritException(exception,&image->exception);
        return(DestroyImageList(image));
      }
    if (image->storage_class == PseudoClass)
      {
        unsigned char
          *bmp_colormap;

        size_t
          packet_size;

        /*
          Read BMP raster colormap.
        */
        if (image->debug != MagickFalse)
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  Reading colormap of %.20g colors",(double) image->colors);
        if (AcquireImageColormap(image,image->colors) == MagickFalse)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        bmp_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
          image->colors,4*sizeof(*bmp_colormap));
        if (bmp_colormap == (unsigned char *) NULL)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        if ((bmp_info.size == 12) || (bmp_info.size == 64))
          packet_size=3;
        else
          packet_size=4;
        offset=SeekBlob(image,start_position+14+bmp_info.size,SEEK_SET);
        if (offset < 0)
          {
            bmp_colormap=(unsigned char *) RelinquishMagickMemory(bmp_colormap);
            ThrowReaderException(CorruptImageError,"ImproperImageHeader");
          }
        count=ReadBlob(image,packet_size*image->colors,bmp_colormap);
        if (count != (ssize_t) (packet_size*image->colors))
          {
            bmp_colormap=(unsigned char *) RelinquishMagickMemory(bmp_colormap);
            ThrowReaderException(CorruptImageError,
              "InsufficientImageDataInFile");
          }
        p=bmp_colormap;
        for (i=0; i < (ssize_t) image->colors; i++)
        {
          image->colormap[i].blue=ScaleCharToQuantum(*p++);
          image->colormap[i].green=ScaleCharToQuantum(*p++);
          image->colormap[i].red=ScaleCharToQuantum(*p++);
          if (packet_size == 4)
            p++;
        }
        bmp_colormap=(unsigned char *) RelinquishMagickMemory(bmp_colormap);
      }
    /*
      Read image data.
    */
    if (bmp_info.offset_bits == offset_bits)
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    offset_bits=bmp_info.offset_bits;
    offset=SeekBlob(image,start_position+bmp_info.offset_bits,SEEK_SET);
    if (offset < 0)
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    if (bmp_info.compression == BI_RLE4)
      bmp_info.bits_per_pixel<<=1;
    bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32);
    length=(size_t) bytes_per_line*image->rows;
    if ((bmp_info.compression == BI_RGB) ||
        (bmp_info.compression == BI_BITFIELDS))
      {
        if ((MagickSizeType) length > GetBlobSize(image))
          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
        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 (image->debug != MagickFalse)
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  Reading pixels (%.20g bytes)",(double) length);
        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.
        */
        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);
        status=DecodeImage(image,bmp_info.compression,pixels,
          image->columns*image->rows);
        if (status == MagickFalse)
          {
            pixel_info=RelinquishVirtualMemory(pixel_info);
            ThrowReaderException(CorruptImageError,
              "UnableToRunlengthDecodeImage");
          }
      }
    /*
      Convert BMP raster image to pixel packets.
    */
    if (bmp_info.compression == BI_RGB)
      {
        /*
          We should ignore the alpha value in BMP3 files but there have been
          reports about 32 bit files with alpha. We do a quick check to see if
          the alpha channel contains a value that is not zero (default value).
          If we find a non zero value we asume the program that wrote the file
          wants to use the alpha channel.
        */
        if ((image->matte == MagickFalse) && (bmp_info.size == 40) &&
            (bmp_info.bits_per_pixel == 32))
          {
            bytes_per_line=4*(image->columns);
            for (y=(ssize_t) image->rows-1; y >= 0; y--)
            {
              p=pixels+(image->rows-y-1)*bytes_per_line;
              for (x=0; x < (ssize_t) image->columns; x++)
              {
                if (*(p+3) != 0)
                  {
                    image->matte=MagickTrue;
                    y=-1;
                    break;
                  }
                p+=4;
              }
            }
          }
        bmp_info.alpha_mask=image->matte != MagickFalse ? 0xff000000U : 0U;
        bmp_info.red_mask=0x00ff0000U;
        bmp_info.green_mask=0x0000ff00U;
        bmp_info.blue_mask=0x000000ffU;
        if (bmp_info.bits_per_pixel == 16)
          {
            /*
              RGB555.
            */
            bmp_info.red_mask=0x00007c00U;
            bmp_info.green_mask=0x000003e0U;
            bmp_info.blue_mask=0x0000001fU;
          }
      }
    (void) memset(&shift,0,sizeof(shift));
    (void) memset(&quantum_bits,0,sizeof(quantum_bits));
    if ((bmp_info.bits_per_pixel == 16) || (bmp_info.bits_per_pixel == 32))
      {
        register size_t
          sample;

        /*
          Get shift and quantum bits info from bitfield masks.
        */
        if (bmp_info.red_mask != 0)
          while (((bmp_info.red_mask << shift.red) & 0x80000000UL) == 0)
          {
            shift.red++;
            if (shift.red > 32U)
              break;
          }
        if (bmp_info.green_mask != 0)
          while (((bmp_info.green_mask << shift.green) & 0x80000000UL) == 0)
          {
            shift.green++;
            if (shift.green > 32U)
              break;
          }
        if (bmp_info.blue_mask != 0)
          while (((bmp_info.blue_mask << shift.blue) & 0x80000000UL) == 0)
          {
            shift.blue++;
            if (shift.blue > 32U)
              break;
          }
        if (bmp_info.alpha_mask != 0)
          while (((bmp_info.alpha_mask << shift.opacity) & 0x80000000UL) == 0)
          {
            shift.opacity++;
            if (shift.opacity > 32U)
              break;
          }
        sample=shift.red;
        while (((bmp_info.red_mask << sample) & 0x80000000UL) != 0)
        {
          sample++;
          if (sample > 32U)
            break;
        }
        quantum_bits.red=ClampToQuantum((MagickRealType) sample-shift.red);
        sample=shift.green;
        while (((bmp_info.green_mask << sample) & 0x80000000UL) != 0)
        {
          sample++;
          if (sample > 32U)
            break;
        }
        quantum_bits.green=ClampToQuantum((MagickRealType) sample-shift.green);
        sample=shift.blue;
        while (((bmp_info.blue_mask << sample) & 0x80000000UL) != 0)
        {
          sample++;
          if (sample > 32U)
            break;
        }
        quantum_bits.blue=ClampToQuantum((MagickRealType) sample-shift.blue);
        sample=shift.opacity;
        while (((bmp_info.alpha_mask << sample) & 0x80000000UL) != 0)
        {
          sample++;
          if (sample > 32U)
            break;
        }
        quantum_bits.opacity=ClampToQuantum((MagickRealType) sample-
          shift.opacity);
      }
    switch (bmp_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 == (PixelPacket *) NULL)
            break;
          indexes=GetAuthenticIndexQueue(image);
          for (x=0; x < ((ssize_t) image->columns-7); x+=8)
          {
            for (bit=0; bit < 8; bit++)
            {
              index=(IndexPacket) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
              SetPixelIndex(indexes+x+bit,index);
              q++;
            }
            p++;
          }
          if ((image->columns % 8) != 0)
            {
              for (bit=0; bit < (image->columns % 8); bit++)
              {
                index=(IndexPacket) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
                SetPixelIndex(indexes+x+bit,index);
              }
              p++;
            }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                (image->rows-y),image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        (void) SyncImage(image);
        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 == (PixelPacket *) NULL)
            break;
          indexes=GetAuthenticIndexQueue(image);
          for (x=0; x < ((ssize_t) image->columns-1); x+=2)
          {
            (void) IsValidColormapIndex(image,(ssize_t) ((*p >> 4) & 0x0f),
              &index,exception);
            SetPixelIndex(indexes+x,index);
            (void) IsValidColormapIndex(image,(ssize_t) (*p & 0x0f),&index,
              exception);
            SetPixelIndex(indexes+x+1,index);
            p++;
          }
          if ((image->columns % 2) != 0)
            {
              (void) IsValidColormapIndex(image,(ssize_t) ((*p >> 4) & 0xf),
                &index,exception);
              SetPixelIndex(indexes+(x++),index);
              p++;
            }
          if (x < (ssize_t) image->columns)
            break;
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                (image->rows-y),image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        (void) SyncImage(image);
        break;
      }
      case 8:
      {
        /*
          Convert PseudoColor scanline.
        */
        if ((bmp_info.compression == BI_RLE8) ||
            (bmp_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 == (PixelPacket *) NULL)
            break;
          indexes=GetAuthenticIndexQueue(image);
          for (x=(ssize_t) image->columns; x != 0; --x)
          {
            (void) IsValidColormapIndex(image,(ssize_t) *p,&index,exception);
            SetPixelIndex(indexes,index);
            indexes++;
            p++;
            q++;
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          offset=(MagickOffsetType) (image->rows-y-1);
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                (image->rows-y),image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        (void) SyncImage(image);
        break;
      }
      case 16:
      {
        unsigned int
          alpha,
          pixel;

        /*
          Convert bitfield encoded 16-bit PseudoColor scanline.
        */
        if (bmp_info.compression != BI_RGB &&
            bmp_info.compression != BI_BITFIELDS)
          {
            pixel_info=RelinquishVirtualMemory(pixel_info);
            ThrowReaderException(CorruptImageError,
              "UnrecognizedImageCompression");
          }
        bytes_per_line=2*(image->columns+image->columns % 2);
        image->storage_class=DirectClass;
        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 == (PixelPacket *) NULL)
            break;
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            pixel=(unsigned int) (*p++);
            pixel|=(*p++) << 8;
            red=((pixel & bmp_info.red_mask) << shift.red) >> 16;
            if (quantum_bits.red == 5)
              red|=((red & 0xe000) >> 5);
            if (quantum_bits.red <= 8)
              red|=((red & 0xff00) >> 8);
            green=((pixel & bmp_info.green_mask) << shift.green) >> 16;
            if (quantum_bits.green == 5)
              green|=((green & 0xe000) >> 5);
            if (quantum_bits.green == 6)
              green|=((green & 0xc000) >> 6);
            if (quantum_bits.green <= 8)
              green|=((green & 0xff00) >> 8);
            blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16;
            if (quantum_bits.blue == 5)
              blue|=((blue & 0xe000) >> 5);
            if (quantum_bits.blue <= 8)
              blue|=((blue & 0xff00) >> 8);
            SetPixelRed(q,ScaleShortToQuantum((unsigned short) red));
            SetPixelGreen(q,ScaleShortToQuantum((unsigned short) green));
            SetPixelBlue(q,ScaleShortToQuantum((unsigned short) blue));
            SetPixelOpacity(q,OpaqueOpacity);
            if (image->matte != MagickFalse)
              {
                alpha=((pixel & bmp_info.alpha_mask) << shift.opacity) >> 16;
                if (quantum_bits.opacity <= 8)
                  alpha|=((alpha & 0xff00) >> 8);
                SetPixelAlpha(q,ScaleShortToQuantum((unsigned short) alpha));
              }
            q++;
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          offset=(MagickOffsetType) (image->rows-y-1);
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                (image->rows-y),image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        break;
      }
      case 24:
      {
        /*
          Convert DirectColor scanline.
        */
        bytes_per_line=4*((image->columns*24+31)/32);
        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 == (PixelPacket *) NULL)
            break;
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            SetPixelBlue(q,ScaleCharToQuantum(*p++));
            SetPixelGreen(q,ScaleCharToQuantum(*p++));
            SetPixelRed(q,ScaleCharToQuantum(*p++));
            SetPixelOpacity(q,OpaqueOpacity);
            q++;
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          offset=(MagickOffsetType) (image->rows-y-1);
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                (image->rows-y),image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        break;
      }
      case 32:
      {
        /*
          Convert bitfield encoded DirectColor scanline.
        */
        if ((bmp_info.compression != BI_RGB) &&
            (bmp_info.compression != BI_BITFIELDS))
          {
            pixel_info=RelinquishVirtualMemory(pixel_info);
            ThrowReaderException(CorruptImageError,
              "UnrecognizedImageCompression");
          }
        bytes_per_line=4*(image->columns);
        for (y=(ssize_t) image->rows-1; y >= 0; y--)
        {
          unsigned int
            alpha,
            pixel;

          p=pixels+(image->rows-y-1)*bytes_per_line;
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
          if (q == (PixelPacket *) NULL)
            break;
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            pixel=(unsigned int) (*p++);
            pixel|=((unsigned int) *p++ << 8);
            pixel|=((unsigned int) *p++ << 16);
            pixel|=((unsigned int) *p++ << 24);
            red=((pixel & bmp_info.red_mask) << shift.red) >> 16;
            if (quantum_bits.red == 8)
              red|=(red >> 8);
            green=((pixel & bmp_info.green_mask) << shift.green) >> 16;
            if (quantum_bits.green == 8)
              green|=(green >> 8);
            blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16;
            if (quantum_bits.blue == 8)
              blue|=(blue >> 8);
            SetPixelRed(q,ScaleShortToQuantum((unsigned short) red));
            SetPixelGreen(q,ScaleShortToQuantum((unsigned short) green));
            SetPixelBlue(q,ScaleShortToQuantum((unsigned short) blue));
            SetPixelAlpha(q,OpaqueOpacity);
            if (image->matte != MagickFalse)
              {
                alpha=((pixel & bmp_info.alpha_mask) << shift.opacity) >> 16;
                if (quantum_bits.opacity == 8)
                  alpha|=(alpha >> 8);
                SetPixelAlpha(q,ScaleShortToQuantum((unsigned short) alpha));
              }
            q++;
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          offset=(MagickOffsetType) (image->rows-y-1);
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                (image->rows-y),image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        break;
      }
      default:
      {
        pixel_info=RelinquishVirtualMemory(pixel_info);
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      }
    }
    pixel_info=RelinquishVirtualMemory(pixel_info);
    if (y > 0)
      break;
    if (EOFBlob(image) != MagickFalse)
      {
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
        break;
      }
    if (bmp_info.height < 0)
      {
        Image
          *flipped_image;

        /*
          Correct image orientation.
        */
        flipped_image=FlipImage(image,exception);
        if (flipped_image != (Image *) NULL)
          {
            DuplicateBlob(flipped_image,image);
            ReplaceImageInList(&image, flipped_image);
            image=flipped_image;
          }
      }
    /*
      Proceed to next image.
    */
    if (image_info->number_scenes != 0)
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    offset=(MagickOffsetType) bmp_info.ba_offset;
    if (offset != 0)
      if ((offset < TellBlob(image)) ||
          (SeekBlob(image,offset,SEEK_SET) != offset))
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    *magick='\0';
    count=ReadBlob(image,2,magick);
    if ((count == 2) && (IsBMP(magick,2) != MagickFalse))
      {
        /*
          Acquire next image structure.
        */
        AcquireNextImage(image_info,image);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            status=MagickFalse;
            break;
          }
        image=SyncNextImageInList(image);
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
          GetBlobSize(image));
        if (status == MagickFalse)
          break;
      }
  } while (IsBMP(magick,2) != MagickFalse);
  (void) CloseBlob(image);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  return(GetFirstImageInList(image));
}


Prevent global-buffer-overflow in wc_any_to_ucs()

Bug-Debian: https://github.com/tats/w3m/issues/43

wc_any_to_ucs(wc_wchar_t cc)
{
    int f;
    wc_uint16 *map = NULL;
    wc_uint32 map_size = 0x80;
    wc_map *map2;

    f = WC_CCS_INDEX(cc.ccs);
    switch (WC_CCS_TYPE(cc.ccs)) {
    case WC_CCS_A_CS94:
	if (cc.ccs == WC_CCS_US_ASCII)
	    return cc.code;
	if (f < WC_F_ISO_BASE || f > WC_F_CS94_END)
	    return WC_C_UCS4_ERROR;
	map = cs94_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_CS94W:
	if (cc.ccs == WC_CCS_GB_2312 && WcOption.use_gb12345_map) {
	    cc.ccs = WC_CCS_GB_12345;
	    return wc_any_to_ucs(cc);
	} else if (cc.ccs == WC_CCS_JIS_X_0213_1) {
	    map2 = wc_map_search((wc_uint16)(cc.code & 0x7f7f),
		jisx02131_ucs_p2_map, N_jisx02131_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	} else if (cc.ccs == WC_CCS_JIS_X_0213_2) {
	    map2 = wc_map_search((wc_uint16)(cc.code & 0x7f7f),
		jisx02132_ucs_p2_map, N_jisx02132_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	}
	if (f < WC_F_ISO_BASE || f > WC_F_CS94W_END)
	    return 0;
	map = cs94w_ucs_map[f - WC_F_ISO_BASE];
	map_size = cs94w_ucs_map_size[f - WC_F_ISO_BASE];
	cc.code = WC_CS94W_N(cc.code);
	break;
    case WC_CCS_A_CS96:
	if (f < WC_F_ISO_BASE || f > WC_F_CS96_END)
	    return WC_C_UCS4_ERROR;
	map = cs96_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_CS96W:
	if (f < WC_F_ISO_BASE || f > WC_F_CS96W_END)
	    return WC_C_UCS4_ERROR;
	map = cs96w_ucs_map[f - WC_F_ISO_BASE];
	map_size = cs96w_ucs_map_size[f - WC_F_ISO_BASE];
	cc.code = WC_CS96W_N(cc.code);
	break;
    case WC_CCS_A_CS942:
	if (f < WC_F_ISO_BASE || f > WC_F_CS942_END)
	    return WC_C_UCS4_ERROR;
	map = cs942_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_PCS:
	if (f < WC_F_PCS_BASE || f > WC_F_PCS_END)
	    return WC_C_UCS4_ERROR;
	switch (cc.ccs) {
	case WC_CCS_CP1258_2:
	    map2 = wc_map_search((wc_uint16)cc.code,
		cp12582_ucs_map, N_cp12582_ucs_map);
	    if (map2)
		return map2->code2;
	    return WC_C_UCS4_ERROR;
	case WC_CCS_TCVN_5712_3:
	    return wc_any_to_ucs(wc_tcvn57123_to_tcvn5712(cc));
	case WC_CCS_GBK_80:
	    return WC_C_UCS2_EURO;
	}
	map = pcs_ucs_map[f - WC_F_PCS_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_PCSW:
	if (f < WC_F_PCS_BASE || f > WC_F_PCSW_END)
	    return WC_C_UCS4_ERROR;
	map = pcsw_ucs_map[f - WC_F_PCS_BASE];
	map_size = pcsw_ucs_map_size[f - WC_F_PCS_BASE];
	switch (cc.ccs) {
	case WC_CCS_BIG5:
	    cc.code = WC_BIG5_N(cc.code);
	    break;
	case WC_CCS_BIG5_2:
	    cc.code = WC_CS94W_N(cc.code) + WC_C_BIG5_2_BASE;
	    break;
	case WC_CCS_HKSCS_1:
	case WC_CCS_HKSCS_2:
	    cc = wc_cs128w_to_hkscs(cc);
	case WC_CCS_HKSCS:
	    map2 = wc_map_search((wc_uint16)cc.code,
		hkscs_ucs_p2_map, N_hkscs_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	    cc.code = wc_hkscs_to_N(cc.code);
	    break;
	case WC_CCS_JOHAB:
	    return wc_any_to_ucs(wc_johab_to_cs128w(cc));
	case WC_CCS_JOHAB_1:
	    return WC_CS94x128_N(cc.code) + WC_C_UCS2_HANGUL;
	case WC_CCS_JOHAB_2:
	    cc.code = WC_CS128W_N(cc.code);
	    cc.code = WC_N_JOHAB2(cc.code);
	    map2 = wc_map_search((wc_uint16)cc.code,
		johab2_ucs_map, N_johab2_ucs_map);
	    if (map2)
		return map2->code2;
	    return WC_C_UCS4_ERROR;
	case WC_CCS_JOHAB_3:
	    if ((cc.code & 0x7f7f) < 0x2121)
		return WC_C_UCS4_ERROR;
	case WC_CCS_SJIS_EXT:
	    return wc_any_to_ucs(wc_sjis_ext_to_cs94w(cc));
	case WC_CCS_SJIS_EXT_1:
	    cc.code = wc_sjis_ext1_to_N(cc.code);
	    if (cc.code == WC_C_SJIS_ERROR)
		return WC_C_UCS4_ERROR;
	    break;
	case WC_CCS_SJIS_EXT_2:
	    cc.code = wc_sjis_ext2_to_N(cc.code);
	    if (cc.code == WC_C_SJIS_ERROR)
		return WC_C_UCS4_ERROR;
	    break;
	case WC_CCS_GBK_1:
	case WC_CCS_GBK_2:
	    cc = wc_cs128w_to_gbk(cc);
	case WC_CCS_GBK:
	    cc.code = wc_gbk_to_N(cc.code);
	    break;
	case WC_CCS_GBK_EXT:
	case WC_CCS_GBK_EXT_1:
	case WC_CCS_GBK_EXT_2:
	    return wc_gb18030_to_ucs(cc);
	case WC_CCS_UHC_1:
	case WC_CCS_UHC_2:
	    cc = wc_cs128w_to_uhc(cc);
	case WC_CCS_UHC:
	    if (cc.code > WC_C_UHC_END)
		return WC_C_UCS4_ERROR;
	    cc.code = wc_uhc_to_N(cc.code);
	    break;
	default:
	    cc.code = WC_CS94W_N(cc.code);
	    break;
	}
	break;
    case WC_CCS_A_WCS16:
	switch (WC_CCS_SET(cc.ccs)) {
	case WC_CCS_UCS2:
	    return cc.code;
	}
	return WC_C_UCS4_ERROR;
    case WC_CCS_A_WCS32:
	switch (WC_CCS_SET(cc.ccs)) {
	case WC_CCS_UCS4:
	    return cc.code;
	case WC_CCS_UCS_TAG:
	    return wc_ucs_tag_to_ucs(cc.code);
	case WC_CCS_GB18030:
	    return wc_gb18030_to_ucs(cc);
	}
	return WC_C_UCS4_ERROR;
    case WC_CCS_A_UNKNOWN:
	if (cc.ccs == WC_CCS_C1)
	    return (cc.code | 0x80);
    default:
	return WC_C_UCS4_ERROR;
    }
    if (map == NULL)
	return WC_C_UCS4_ERROR;
    if (map_size == 0 || cc.code > map_size - 1)
	return WC_C_UCS4_ERROR;
    cc.code = map[cc.code];
    return cc.code ? cc.code : WC_C_UCS4_ERROR;
}

An issue was discovered in the Tatsuya Kinoshita w3m fork before 0.5.3-33. w3m allows remote attackers to cause a denial of service (global buffer overflow and crash) via a crafted HTML page.


wc_any_to_ucs(wc_wchar_t cc)
{
    int f;
    wc_uint16 *map = NULL;
    wc_uint32 map_size = 0x80;
    wc_map *map2;

    f = WC_CCS_INDEX(cc.ccs);
    switch (WC_CCS_TYPE(cc.ccs)) {
    case WC_CCS_A_CS94:
	if (cc.ccs == WC_CCS_US_ASCII)
	    return cc.code;
	if (f < WC_F_ISO_BASE || f > WC_F_CS94_END)
	    return WC_C_UCS4_ERROR;
	map = cs94_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_CS94W:
	if (cc.ccs == WC_CCS_GB_2312 && WcOption.use_gb12345_map) {
	    cc.ccs = WC_CCS_GB_12345;
	    return wc_any_to_ucs(cc);
	} else if (cc.ccs == WC_CCS_JIS_X_0213_1) {
	    map2 = wc_map_search((wc_uint16)(cc.code & 0x7f7f),
		jisx02131_ucs_p2_map, N_jisx02131_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	} else if (cc.ccs == WC_CCS_JIS_X_0213_2) {
	    map2 = wc_map_search((wc_uint16)(cc.code & 0x7f7f),
		jisx02132_ucs_p2_map, N_jisx02132_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	}
	if (f < WC_F_ISO_BASE || f > WC_F_CS94W_END)
	    return 0;
	map = cs94w_ucs_map[f - WC_F_ISO_BASE];
	map_size = cs94w_ucs_map_size[f - WC_F_ISO_BASE];
	cc.code = WC_CS94W_N(cc.code);
	break;
    case WC_CCS_A_CS96:
	if (f < WC_F_ISO_BASE || f > WC_F_CS96_END)
	    return WC_C_UCS4_ERROR;
	map = cs96_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_CS96W:
	if (f < WC_F_ISO_BASE || f > WC_F_CS96W_END)
	    return WC_C_UCS4_ERROR;
	map = cs96w_ucs_map[f - WC_F_ISO_BASE];
	map_size = cs96w_ucs_map_size[f - WC_F_ISO_BASE];
	cc.code = WC_CS96W_N(cc.code);
	break;
    case WC_CCS_A_CS942:
	if (f < WC_F_ISO_BASE || f > WC_F_CS942_END)
	    return WC_C_UCS4_ERROR;
	map = cs942_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_PCS:
	if (f < WC_F_PCS_BASE || f > WC_F_PCS_END)
	    return WC_C_UCS4_ERROR;
	switch (cc.ccs) {
	case WC_CCS_CP1258_2:
	    map2 = wc_map_search((wc_uint16)cc.code,
		cp12582_ucs_map, N_cp12582_ucs_map);
	    if (map2)
		return map2->code2;
	    return WC_C_UCS4_ERROR;
	case WC_CCS_TCVN_5712_3:
	    return wc_any_to_ucs(wc_tcvn57123_to_tcvn5712(cc));
	case WC_CCS_GBK_80:
	    return WC_C_UCS2_EURO;
	}
	map = pcs_ucs_map[f - WC_F_PCS_BASE];
	map_size = pcs_ucs_map_size[f - WC_F_PCS_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_PCSW:
	if (f < WC_F_PCS_BASE || f > WC_F_PCSW_END)
	    return WC_C_UCS4_ERROR;
	map = pcsw_ucs_map[f - WC_F_PCS_BASE];
	map_size = pcsw_ucs_map_size[f - WC_F_PCS_BASE];
	switch (cc.ccs) {
	case WC_CCS_BIG5:
	    cc.code = WC_BIG5_N(cc.code);
	    break;
	case WC_CCS_BIG5_2:
	    cc.code = WC_CS94W_N(cc.code) + WC_C_BIG5_2_BASE;
	    break;
	case WC_CCS_HKSCS_1:
	case WC_CCS_HKSCS_2:
	    cc = wc_cs128w_to_hkscs(cc);
	case WC_CCS_HKSCS:
	    map2 = wc_map_search((wc_uint16)cc.code,
		hkscs_ucs_p2_map, N_hkscs_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	    cc.code = wc_hkscs_to_N(cc.code);
	    break;
	case WC_CCS_JOHAB:
	    return wc_any_to_ucs(wc_johab_to_cs128w(cc));
	case WC_CCS_JOHAB_1:
	    return WC_CS94x128_N(cc.code) + WC_C_UCS2_HANGUL;
	case WC_CCS_JOHAB_2:
	    cc.code = WC_CS128W_N(cc.code);
	    cc.code = WC_N_JOHAB2(cc.code);
	    map2 = wc_map_search((wc_uint16)cc.code,
		johab2_ucs_map, N_johab2_ucs_map);
	    if (map2)
		return map2->code2;
	    return WC_C_UCS4_ERROR;
	case WC_CCS_JOHAB_3:
	    if ((cc.code & 0x7f7f) < 0x2121)
		return WC_C_UCS4_ERROR;
	case WC_CCS_SJIS_EXT:
	    return wc_any_to_ucs(wc_sjis_ext_to_cs94w(cc));
	case WC_CCS_SJIS_EXT_1:
	    cc.code = wc_sjis_ext1_to_N(cc.code);
	    if (cc.code == WC_C_SJIS_ERROR)
		return WC_C_UCS4_ERROR;
	    break;
	case WC_CCS_SJIS_EXT_2:
	    cc.code = wc_sjis_ext2_to_N(cc.code);
	    if (cc.code == WC_C_SJIS_ERROR)
		return WC_C_UCS4_ERROR;
	    break;
	case WC_CCS_GBK_1:
	case WC_CCS_GBK_2:
	    cc = wc_cs128w_to_gbk(cc);
	case WC_CCS_GBK:
	    cc.code = wc_gbk_to_N(cc.code);
	    break;
	case WC_CCS_GBK_EXT:
	case WC_CCS_GBK_EXT_1:
	case WC_CCS_GBK_EXT_2:
	    return wc_gb18030_to_ucs(cc);
	case WC_CCS_UHC_1:
	case WC_CCS_UHC_2:
	    cc = wc_cs128w_to_uhc(cc);
	case WC_CCS_UHC:
	    if (cc.code > WC_C_UHC_END)
		return WC_C_UCS4_ERROR;
	    cc.code = wc_uhc_to_N(cc.code);
	    break;
	default:
	    cc.code = WC_CS94W_N(cc.code);
	    break;
	}
	break;
    case WC_CCS_A_WCS16:
	switch (WC_CCS_SET(cc.ccs)) {
	case WC_CCS_UCS2:
	    return cc.code;
	}
	return WC_C_UCS4_ERROR;
    case WC_CCS_A_WCS32:
	switch (WC_CCS_SET(cc.ccs)) {
	case WC_CCS_UCS4:
	    return cc.code;
	case WC_CCS_UCS_TAG:
	    return wc_ucs_tag_to_ucs(cc.code);
	case WC_CCS_GB18030:
	    return wc_gb18030_to_ucs(cc);
	}
	return WC_C_UCS4_ERROR;
    case WC_CCS_A_UNKNOWN:
	if (cc.ccs == WC_CCS_C1)
	    return (cc.code | 0x80);
    default:
	return WC_C_UCS4_ERROR;
    }
    if (map == NULL)
	return WC_C_UCS4_ERROR;
    if (map_size == 0 || cc.code > map_size - 1)
	return WC_C_UCS4_ERROR;
    cc.code = map[cc.code];
    return cc.code ? cc.code : WC_C_UCS4_ERROR;
}


tracing: Have error path in predicate_parse() free its allocated memory

In predicate_parse, there is an error path that is not going to
out_free instead it returns directly which leads to a memory leak.

Link: http://lkml.kernel.org/r/20190920225800.3870-1-navid.emamdoost@gmail.com

Signed-off-by: Navid Emamdoost <navid.emamdoost@gmail.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>

predicate_parse(const char *str, int nr_parens, int nr_preds,
		parse_pred_fn parse_pred, void *data,
		struct filter_parse_error *pe)
{
	struct prog_entry *prog_stack;
	struct prog_entry *prog;
	const char *ptr = str;
	char *inverts = NULL;
	int *op_stack;
	int *top;
	int invert = 0;
	int ret = -ENOMEM;
	int len;
	int N = 0;
	int i;

	nr_preds += 2; /* For TRUE and FALSE */

	op_stack = kmalloc_array(nr_parens, sizeof(*op_stack), GFP_KERNEL);
	if (!op_stack)
		return ERR_PTR(-ENOMEM);
	prog_stack = kcalloc(nr_preds, sizeof(*prog_stack), GFP_KERNEL);
	if (!prog_stack) {
		parse_error(pe, -ENOMEM, 0);
		goto out_free;
	}
	inverts = kmalloc_array(nr_preds, sizeof(*inverts), GFP_KERNEL);
	if (!inverts) {
		parse_error(pe, -ENOMEM, 0);
		goto out_free;
	}

	top = op_stack;
	prog = prog_stack;
	*top = 0;

	/* First pass */
	while (*ptr) {						/* #1 */
		const char *next = ptr++;

		if (isspace(*next))
			continue;

		switch (*next) {
		case '(':					/* #2 */
			if (top - op_stack > nr_parens)
				return ERR_PTR(-EINVAL);
			*(++top) = invert;
			continue;
		case '!':					/* #3 */
			if (!is_not(next))
				break;
			invert = !invert;
			continue;
		}

		if (N >= nr_preds) {
			parse_error(pe, FILT_ERR_TOO_MANY_PREDS, next - str);
			goto out_free;
		}

		inverts[N] = invert;				/* #4 */
		prog[N].target = N-1;

		len = parse_pred(next, data, ptr - str, pe, &prog[N].pred);
		if (len < 0) {
			ret = len;
			goto out_free;
		}
		ptr = next + len;

		N++;

		ret = -1;
		while (1) {					/* #5 */
			next = ptr++;
			if (isspace(*next))
				continue;

			switch (*next) {
			case ')':
			case '\0':
				break;
			case '&':
			case '|':
				/* accepting only "&&" or "||" */
				if (next[1] == next[0]) {
					ptr++;
					break;
				}
				/* fall through */
			default:
				parse_error(pe, FILT_ERR_TOO_MANY_PREDS,
					    next - str);
				goto out_free;
			}

			invert = *top & INVERT;

			if (*top & PROCESS_AND) {		/* #7 */
				update_preds(prog, N - 1, invert);
				*top &= ~PROCESS_AND;
			}
			if (*next == '&') {			/* #8 */
				*top |= PROCESS_AND;
				break;
			}
			if (*top & PROCESS_OR) {		/* #9 */
				update_preds(prog, N - 1, !invert);
				*top &= ~PROCESS_OR;
			}
			if (*next == '|') {			/* #10 */
				*top |= PROCESS_OR;
				break;
			}
			if (!*next)				/* #11 */
				goto out;

			if (top == op_stack) {
				ret = -1;
				/* Too few '(' */
				parse_error(pe, FILT_ERR_TOO_MANY_CLOSE, ptr - str);
				goto out_free;
			}
			top--;					/* #12 */
		}
	}
 out:
	if (top != op_stack) {
		/* Too many '(' */
		parse_error(pe, FILT_ERR_TOO_MANY_OPEN, ptr - str);
		goto out_free;
	}

	if (!N) {
		/* No program? */
		ret = -EINVAL;
		parse_error(pe, FILT_ERR_NO_FILTER, ptr - str);
		goto out_free;
	}

	prog[N].pred = NULL;					/* #13 */
	prog[N].target = 1;		/* TRUE */
	prog[N+1].pred = NULL;
	prog[N+1].target = 0;		/* FALSE */
	prog[N-1].target = N;
	prog[N-1].when_to_branch = false;

	/* Second Pass */
	for (i = N-1 ; i--; ) {
		int target = prog[i].target;
		if (prog[i].when_to_branch == prog[target].when_to_branch)
			prog[i].target = prog[target].target;
	}

	/* Third Pass */
	for (i = 0; i < N; i++) {
		invert = inverts[i] ^ prog[i].when_to_branch;
		prog[i].when_to_branch = invert;
		/* Make sure the program always moves forward */
		if (WARN_ON(prog[i].target <= i)) {
			ret = -EINVAL;
			goto out_free;
		}
	}

	kfree(op_stack);
	kfree(inverts);
	return prog;
out_free:
	kfree(op_stack);
	kfree(inverts);
	if (prog_stack) {
		for (i = 0; prog_stack[i].pred; i++)
			kfree(prog_stack[i].pred);
		kfree(prog_stack);
	}
	return ERR_PTR(ret);
}

A memory leak in the predicate_parse() function in kernel/trace/trace_events_filter.c in the Linux kernel through 5.3.11 allows attackers to cause a denial of service (memory consumption), aka CID-96c5c6e6a5b6.


predicate_parse(const char *str, int nr_parens, int nr_preds,
		parse_pred_fn parse_pred, void *data,
		struct filter_parse_error *pe)
{
	struct prog_entry *prog_stack;
	struct prog_entry *prog;
	const char *ptr = str;
	char *inverts = NULL;
	int *op_stack;
	int *top;
	int invert = 0;
	int ret = -ENOMEM;
	int len;
	int N = 0;
	int i;

	nr_preds += 2; /* For TRUE and FALSE */

	op_stack = kmalloc_array(nr_parens, sizeof(*op_stack), GFP_KERNEL);
	if (!op_stack)
		return ERR_PTR(-ENOMEM);
	prog_stack = kcalloc(nr_preds, sizeof(*prog_stack), GFP_KERNEL);
	if (!prog_stack) {
		parse_error(pe, -ENOMEM, 0);
		goto out_free;
	}
	inverts = kmalloc_array(nr_preds, sizeof(*inverts), GFP_KERNEL);
	if (!inverts) {
		parse_error(pe, -ENOMEM, 0);
		goto out_free;
	}

	top = op_stack;
	prog = prog_stack;
	*top = 0;

	/* First pass */
	while (*ptr) {						/* #1 */
		const char *next = ptr++;

		if (isspace(*next))
			continue;

		switch (*next) {
		case '(':					/* #2 */
			if (top - op_stack > nr_parens) {
				ret = -EINVAL;
				goto out_free;
			}
			*(++top) = invert;
			continue;
		case '!':					/* #3 */
			if (!is_not(next))
				break;
			invert = !invert;
			continue;
		}

		if (N >= nr_preds) {
			parse_error(pe, FILT_ERR_TOO_MANY_PREDS, next - str);
			goto out_free;
		}

		inverts[N] = invert;				/* #4 */
		prog[N].target = N-1;

		len = parse_pred(next, data, ptr - str, pe, &prog[N].pred);
		if (len < 0) {
			ret = len;
			goto out_free;
		}
		ptr = next + len;

		N++;

		ret = -1;
		while (1) {					/* #5 */
			next = ptr++;
			if (isspace(*next))
				continue;

			switch (*next) {
			case ')':
			case '\0':
				break;
			case '&':
			case '|':
				/* accepting only "&&" or "||" */
				if (next[1] == next[0]) {
					ptr++;
					break;
				}
				/* fall through */
			default:
				parse_error(pe, FILT_ERR_TOO_MANY_PREDS,
					    next - str);
				goto out_free;
			}

			invert = *top & INVERT;

			if (*top & PROCESS_AND) {		/* #7 */
				update_preds(prog, N - 1, invert);
				*top &= ~PROCESS_AND;
			}
			if (*next == '&') {			/* #8 */
				*top |= PROCESS_AND;
				break;
			}
			if (*top & PROCESS_OR) {		/* #9 */
				update_preds(prog, N - 1, !invert);
				*top &= ~PROCESS_OR;
			}
			if (*next == '|') {			/* #10 */
				*top |= PROCESS_OR;
				break;
			}
			if (!*next)				/* #11 */
				goto out;

			if (top == op_stack) {
				ret = -1;
				/* Too few '(' */
				parse_error(pe, FILT_ERR_TOO_MANY_CLOSE, ptr - str);
				goto out_free;
			}
			top--;					/* #12 */
		}
	}
 out:
	if (top != op_stack) {
		/* Too many '(' */
		parse_error(pe, FILT_ERR_TOO_MANY_OPEN, ptr - str);
		goto out_free;
	}

	if (!N) {
		/* No program? */
		ret = -EINVAL;
		parse_error(pe, FILT_ERR_NO_FILTER, ptr - str);
		goto out_free;
	}

	prog[N].pred = NULL;					/* #13 */
	prog[N].target = 1;		/* TRUE */
	prog[N+1].pred = NULL;
	prog[N+1].target = 0;		/* FALSE */
	prog[N-1].target = N;
	prog[N-1].when_to_branch = false;

	/* Second Pass */
	for (i = N-1 ; i--; ) {
		int target = prog[i].target;
		if (prog[i].when_to_branch == prog[target].when_to_branch)
			prog[i].target = prog[target].target;
	}

	/* Third Pass */
	for (i = 0; i < N; i++) {
		invert = inverts[i] ^ prog[i].when_to_branch;
		prog[i].when_to_branch = invert;
		/* Make sure the program always moves forward */
		if (WARN_ON(prog[i].target <= i)) {
			ret = -EINVAL;
			goto out_free;
		}
	}

	kfree(op_stack);
	kfree(inverts);
	return prog;
out_free:
	kfree(op_stack);
	kfree(inverts);
	if (prog_stack) {
		for (i = 0; prog_stack[i].pred; i++)
			kfree(prog_stack[i].pred);
		kfree(prog_stack);
	}
	return ERR_PTR(ret);
}


Change the data_len parameter of _XimAttributeToValue() to CARD16

It's coming from a length in the protocol (unsigned) and passed
to functions that expect unsigned int parameters (_XCopyToArg()
and memcpy()).

Signed-off-by: Matthieu Herrb <matthieu@herrb.eu>
Reviewed-by: Todd Carson <toc@daybefore.net>

_XimAttributeToValue(
    Xic			  ic,
    XIMResourceList	  res,
    CARD16		 *data,
    INT16		  data_len,
    XPointer		  value,
    BITMASK32		  mode)
{
    switch (res->resource_size) {
    case XimType_SeparatorOfNestedList:
    case XimType_NEST:
	break;

    case XimType_CARD8:
    case XimType_CARD16:
    case XimType_CARD32:
    case XimType_Window:
    case XimType_XIMHotKeyState:
	_XCopyToArg((XPointer)data, (XPointer *)&value, data_len);
	break;

    case XimType_STRING8:
	{
	    char	*str;

	    if (!(value))
		return False;

	    if (!(str = Xmalloc(data_len + 1)))
		return False;

	    (void)memcpy(str, (char *)data, data_len);
	    str[data_len] = '\0';

	    *((char **)value) = str;
	    break;
	}

    case XimType_XIMStyles:
	{
	    CARD16		 num = data[0];
	    register CARD32	*style_list = (CARD32 *)&data[2];
	    XIMStyle		*style;
	    XIMStyles		*rep;
	    register int	 i;
	    char		*p;
	    unsigned int         alloc_len;

	    if (!(value))
		return False;

	    if (num > (USHRT_MAX / sizeof(XIMStyle)))
		return False;
	    if ((sizeof(num) + (num * sizeof(XIMStyle))) > data_len)
		return False;
	    alloc_len = sizeof(XIMStyles) + sizeof(XIMStyle) * num;
	    if (alloc_len < sizeof(XIMStyles))
		return False;
	    if (!(p = Xmalloc(alloc_len)))
		return False;

	    rep   = (XIMStyles *)p;
	    style = (XIMStyle *)(p + sizeof(XIMStyles));

	    for (i = 0; i < num; i++)
		style[i] = (XIMStyle)style_list[i];

	    rep->count_styles = (unsigned short)num;
	    rep->supported_styles = style;
	    *((XIMStyles **)value) = rep;
	    break;
	}

    case XimType_XRectangle:
	{
	    XRectangle	*rep;

	    if (!(value))
		return False;

	    if (!(rep = Xmalloc(sizeof(XRectangle))))
		return False;

	    rep->x      = data[0];
	    rep->y      = data[1];
	    rep->width  = data[2];
	    rep->height = data[3];
	    *((XRectangle **)value) = rep;
	    break;
	}

    case XimType_XPoint:
	{
	    XPoint	*rep;

	    if (!(value))
		return False;

	    if (!(rep = Xmalloc(sizeof(XPoint))))
		return False;

	    rep->x = data[0];
	    rep->y = data[1];
	    *((XPoint **)value) = rep;
	    break;
	}

    case XimType_XFontSet:
	{
	    CARD16	 len = data[0];
	    char	*base_name;
	    XFontSet	 rep = (XFontSet)NULL;
	    char	**missing_list = NULL;
	    int		 missing_count;
	    char	*def_string;

	    if (!(value))
		return False;
	    if (!ic)
		return False;
	    if (len > data_len)
		return False;
	    if (!(base_name = Xmalloc(len + 1)))
		return False;

	    (void)strncpy(base_name, (char *)&data[1], (size_t)len);
	    base_name[len] = '\0';

	    if (mode & XIM_PREEDIT_ATTR) {
		if (!strcmp(base_name, ic->private.proto.preedit_font)) {
		    rep = ic->core.preedit_attr.fontset;
		} else if (!ic->private.proto.preedit_font_length) {
		    rep = XCreateFontSet(ic->core.im->core.display,
					base_name, &missing_list,
					&missing_count, &def_string);
		}
	    } else if (mode & XIM_STATUS_ATTR) {
		if (!strcmp(base_name, ic->private.proto.status_font)) {
		    rep = ic->core.status_attr.fontset;
		} else if (!ic->private.proto.status_font_length) {
		    rep = XCreateFontSet(ic->core.im->core.display,
					base_name, &missing_list,
					&missing_count, &def_string);
		}
	    }

	    Xfree(base_name);
	    Xfree(missing_list);
	    *((XFontSet *)value) = rep;
	    break;
	}

    case XimType_XIMHotKeyTriggers:
	{
	    CARD32			 num = *((CARD32 *)data);
	    register CARD32		*key_list = (CARD32 *)&data[2];
	    XIMHotKeyTrigger		*key;
	    XIMHotKeyTriggers		*rep;
	    register int		 i;
	    char			*p;
	    unsigned int		 alloc_len;

	    if (!(value))
		return False;

	    if (num > (UINT_MAX / sizeof(XIMHotKeyTrigger)))
		return False;
	    if ((sizeof(num) + (num * sizeof(XIMHotKeyTrigger))) > data_len)
		return False;
	    alloc_len = sizeof(XIMHotKeyTriggers)
		      + sizeof(XIMHotKeyTrigger) * num;
	    if (alloc_len < sizeof(XIMHotKeyTriggers))
		return False;
	    if (!(p = Xmalloc(alloc_len)))
		return False;

	    rep = (XIMHotKeyTriggers *)p;
	    key = (XIMHotKeyTrigger *)(p + sizeof(XIMHotKeyTriggers));

	    for (i = 0; i < num; i++, key_list += 3) {
		key[i].keysym        = (KeySym)key_list[0]; /* keysym */
		key[i].modifier      = (int)key_list[1];    /* modifier */
		key[i].modifier_mask = (int)key_list[2];    /* modifier_mask */
	    }

	    rep->num_hot_key = (int)num;
	    rep->key = key;
	    *((XIMHotKeyTriggers **)value) = rep;
	    break;
	}

    case XimType_XIMStringConversion:
	{
	    break;
	}

    default:
	return False;
    }
    return True;
}

An integer overflow leading to a heap-buffer overflow was found in The X Input Method (XIM) client was implemented in libX11 before version 1.6.10. As per upstream this is security relevant when setuid programs call XIM client functions while running with elevated privileges. No such programs are shipped with Red Hat Enterprise Linux.


_XimAttributeToValue(
    Xic			  ic,
    XIMResourceList	  res,
    CARD16		 *data,
    CARD16		  data_len,
    XPointer		  value,
    BITMASK32		  mode)
{
    switch (res->resource_size) {
    case XimType_SeparatorOfNestedList:
    case XimType_NEST:
	break;

    case XimType_CARD8:
    case XimType_CARD16:
    case XimType_CARD32:
    case XimType_Window:
    case XimType_XIMHotKeyState:
	_XCopyToArg((XPointer)data, (XPointer *)&value, data_len);
	break;

    case XimType_STRING8:
	{
	    char	*str;

	    if (!(value))
		return False;

	    if (!(str = Xmalloc(data_len + 1)))
		return False;

	    (void)memcpy(str, (char *)data, data_len);
	    str[data_len] = '\0';

	    *((char **)value) = str;
	    break;
	}

    case XimType_XIMStyles:
	{
	    CARD16		 num = data[0];
	    register CARD32	*style_list = (CARD32 *)&data[2];
	    XIMStyle		*style;
	    XIMStyles		*rep;
	    register int	 i;
	    char		*p;
	    unsigned int         alloc_len;

	    if (!(value))
		return False;

	    if (num > (USHRT_MAX / sizeof(XIMStyle)))
		return False;
	    if ((sizeof(num) + (num * sizeof(XIMStyle))) > data_len)
		return False;
	    alloc_len = sizeof(XIMStyles) + sizeof(XIMStyle) * num;
	    if (alloc_len < sizeof(XIMStyles))
		return False;
	    if (!(p = Xmalloc(alloc_len)))
		return False;

	    rep   = (XIMStyles *)p;
	    style = (XIMStyle *)(p + sizeof(XIMStyles));

	    for (i = 0; i < num; i++)
		style[i] = (XIMStyle)style_list[i];

	    rep->count_styles = (unsigned short)num;
	    rep->supported_styles = style;
	    *((XIMStyles **)value) = rep;
	    break;
	}

    case XimType_XRectangle:
	{
	    XRectangle	*rep;

	    if (!(value))
		return False;

	    if (!(rep = Xmalloc(sizeof(XRectangle))))
		return False;

	    rep->x      = data[0];
	    rep->y      = data[1];
	    rep->width  = data[2];
	    rep->height = data[3];
	    *((XRectangle **)value) = rep;
	    break;
	}

    case XimType_XPoint:
	{
	    XPoint	*rep;

	    if (!(value))
		return False;

	    if (!(rep = Xmalloc(sizeof(XPoint))))
		return False;

	    rep->x = data[0];
	    rep->y = data[1];
	    *((XPoint **)value) = rep;
	    break;
	}

    case XimType_XFontSet:
	{
	    CARD16	 len = data[0];
	    char	*base_name;
	    XFontSet	 rep = (XFontSet)NULL;
	    char	**missing_list = NULL;
	    int		 missing_count;
	    char	*def_string;

	    if (!(value))
		return False;
	    if (!ic)
		return False;
	    if (len > data_len)
		return False;
	    if (!(base_name = Xmalloc(len + 1)))
		return False;

	    (void)strncpy(base_name, (char *)&data[1], (size_t)len);
	    base_name[len] = '\0';

	    if (mode & XIM_PREEDIT_ATTR) {
		if (!strcmp(base_name, ic->private.proto.preedit_font)) {
		    rep = ic->core.preedit_attr.fontset;
		} else if (!ic->private.proto.preedit_font_length) {
		    rep = XCreateFontSet(ic->core.im->core.display,
					base_name, &missing_list,
					&missing_count, &def_string);
		}
	    } else if (mode & XIM_STATUS_ATTR) {
		if (!strcmp(base_name, ic->private.proto.status_font)) {
		    rep = ic->core.status_attr.fontset;
		} else if (!ic->private.proto.status_font_length) {
		    rep = XCreateFontSet(ic->core.im->core.display,
					base_name, &missing_list,
					&missing_count, &def_string);
		}
	    }

	    Xfree(base_name);
	    Xfree(missing_list);
	    *((XFontSet *)value) = rep;
	    break;
	}

    case XimType_XIMHotKeyTriggers:
	{
	    CARD32			 num = *((CARD32 *)data);
	    register CARD32		*key_list = (CARD32 *)&data[2];
	    XIMHotKeyTrigger		*key;
	    XIMHotKeyTriggers		*rep;
	    register int		 i;
	    char			*p;
	    unsigned int		 alloc_len;

	    if (!(value))
		return False;

	    if (num > (UINT_MAX / sizeof(XIMHotKeyTrigger)))
		return False;
	    if ((sizeof(num) + (num * sizeof(XIMHotKeyTrigger))) > data_len)
		return False;
	    alloc_len = sizeof(XIMHotKeyTriggers)
		      + sizeof(XIMHotKeyTrigger) * num;
	    if (alloc_len < sizeof(XIMHotKeyTriggers))
		return False;
	    if (!(p = Xmalloc(alloc_len)))
		return False;

	    rep = (XIMHotKeyTriggers *)p;
	    key = (XIMHotKeyTrigger *)(p + sizeof(XIMHotKeyTriggers));

	    for (i = 0; i < num; i++, key_list += 3) {
		key[i].keysym        = (KeySym)key_list[0]; /* keysym */
		key[i].modifier      = (int)key_list[1];    /* modifier */
		key[i].modifier_mask = (int)key_list[2];    /* modifier_mask */
	    }

	    rep->num_hot_key = (int)num;
	    rep->key = key;
	    *((XIMHotKeyTriggers **)value) = rep;
	    break;
	}

    case XimType_XIMStringConversion:
	{
	    break;
	}

    default:
	return False;
    }
    return True;
}


usb: cdc-acm: make sure a refcount is taken early enough

destroy() will decrement the refcount on the interface, so that
it needs to be taken so early that it never undercounts.

Fixes: 7fb57a019f94e ("USB: cdc-acm: Fix potential deadlock (lockdep warning)")
Cc: stable <stable@vger.kernel.org>
Reported-and-tested-by: syzbot+1b2449b7b5dc240d107a@syzkaller.appspotmail.com
Signed-off-by: Oliver Neukum <oneukum@suse.com>
Link: https://lore.kernel.org/r/20190808142119.7998-1-oneukum@suse.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

static int acm_probe(struct usb_interface *intf,
		     const struct usb_device_id *id)
{
	struct usb_cdc_union_desc *union_header = NULL;
	struct usb_cdc_call_mgmt_descriptor *cmgmd = NULL;
	unsigned char *buffer = intf->altsetting->extra;
	int buflen = intf->altsetting->extralen;
	struct usb_interface *control_interface;
	struct usb_interface *data_interface;
	struct usb_endpoint_descriptor *epctrl = NULL;
	struct usb_endpoint_descriptor *epread = NULL;
	struct usb_endpoint_descriptor *epwrite = NULL;
	struct usb_device *usb_dev = interface_to_usbdev(intf);
	struct usb_cdc_parsed_header h;
	struct acm *acm;
	int minor;
	int ctrlsize, readsize;
	u8 *buf;
	int call_intf_num = -1;
	int data_intf_num = -1;
	unsigned long quirks;
	int num_rx_buf;
	int i;
	int combined_interfaces = 0;
	struct device *tty_dev;
	int rv = -ENOMEM;
	int res;

	/* normal quirks */
	quirks = (unsigned long)id->driver_info;

	if (quirks == IGNORE_DEVICE)
		return -ENODEV;

	memset(&h, 0x00, sizeof(struct usb_cdc_parsed_header));

	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;

	/* handle quirks deadly to normal probing*/
	if (quirks == NO_UNION_NORMAL) {
		data_interface = usb_ifnum_to_if(usb_dev, 1);
		control_interface = usb_ifnum_to_if(usb_dev, 0);
		/* we would crash */
		if (!data_interface || !control_interface)
			return -ENODEV;
		goto skip_normal_probe;
	}

	/* normal probing*/
	if (!buffer) {
		dev_err(&intf->dev, "Weird descriptor references\n");
		return -EINVAL;
	}

	if (!intf->cur_altsetting)
		return -EINVAL;

	if (!buflen) {
		if (intf->cur_altsetting->endpoint &&
				intf->cur_altsetting->endpoint->extralen &&
				intf->cur_altsetting->endpoint->extra) {
			dev_dbg(&intf->dev,
				"Seeking extra descriptors on endpoint\n");
			buflen = intf->cur_altsetting->endpoint->extralen;
			buffer = intf->cur_altsetting->endpoint->extra;
		} else {
			dev_err(&intf->dev,
				"Zero length descriptor references\n");
			return -EINVAL;
		}
	}

	cdc_parse_cdc_header(&h, intf, buffer, buflen);
	union_header = h.usb_cdc_union_desc;
	cmgmd = h.usb_cdc_call_mgmt_descriptor;
	if (cmgmd)
		call_intf_num = cmgmd->bDataInterface;

	if (!union_header) {
		if (call_intf_num > 0) {
			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
			/* quirks for Droids MuIn LCD */
			if (quirks & NO_DATA_INTERFACE) {
				data_interface = usb_ifnum_to_if(usb_dev, 0);
			} else {
				data_intf_num = call_intf_num;
				data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
			}
			control_interface = intf;
		} else {
			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
				return -ENODEV;
			} else {
				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
				combined_interfaces = 1;
				control_interface = data_interface = intf;
				goto look_for_collapsed_interface;
			}
		}
	} else {
		data_intf_num = union_header->bSlaveInterface0;
		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
		data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
	}

	if (!control_interface || !data_interface) {
		dev_dbg(&intf->dev, "no interfaces\n");
		return -ENODEV;
	}
	if (!data_interface->cur_altsetting || !control_interface->cur_altsetting)
		return -ENODEV;

	if (data_intf_num != call_intf_num)
		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");

	if (control_interface == data_interface) {
		/* some broken devices designed for windows work this way */
		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
		combined_interfaces = 1;
		/* a popular other OS doesn't use it */
		quirks |= NO_CAP_LINE;
		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
			return -EINVAL;
		}
look_for_collapsed_interface:
		res = usb_find_common_endpoints(data_interface->cur_altsetting,
				&epread, &epwrite, &epctrl, NULL);
		if (res)
			return res;

		goto made_compressed_probe;
	}

skip_normal_probe:

	/*workaround for switched interfaces */
	if (data_interface->cur_altsetting->desc.bInterfaceClass
						!= CDC_DATA_INTERFACE_TYPE) {
		if (control_interface->cur_altsetting->desc.bInterfaceClass
						== CDC_DATA_INTERFACE_TYPE) {
			dev_dbg(&intf->dev,
				"Your device has switched interfaces.\n");
			swap(control_interface, data_interface);
		} else {
			return -EINVAL;
		}
	}

	/* Accept probe requests only for the control interface */
	if (!combined_interfaces && intf != control_interface)
		return -ENODEV;

	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
		/* valid in this context */
		dev_dbg(&intf->dev, "The data interface isn't available\n");
		return -EBUSY;
	}

	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
	    control_interface->cur_altsetting->desc.bNumEndpoints == 0)
		return -EINVAL;

	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
	epread = &data_interface->cur_altsetting->endpoint[0].desc;
	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;

	/* workaround for switched endpoints */
	if (!usb_endpoint_dir_in(epread)) {
		/* descriptors are swapped */
		dev_dbg(&intf->dev,
			"The data interface has switched endpoints\n");
		swap(epread, epwrite);
	}
made_compressed_probe:
	dev_dbg(&intf->dev, "interfaces are valid\n");

	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
	if (acm == NULL)
		goto alloc_fail;

	tty_port_init(&acm->port);
	acm->port.ops = &acm_port_ops;

	minor = acm_alloc_minor(acm);
	if (minor < 0)
		goto alloc_fail1;

	ctrlsize = usb_endpoint_maxp(epctrl);
	readsize = usb_endpoint_maxp(epread) *
				(quirks == SINGLE_RX_URB ? 1 : 2);
	acm->combined_interfaces = combined_interfaces;
	acm->writesize = usb_endpoint_maxp(epwrite) * 20;
	acm->control = control_interface;
	acm->data = data_interface;
	acm->minor = minor;
	acm->dev = usb_dev;
	if (h.usb_cdc_acm_descriptor)
		acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
	if (quirks & NO_CAP_LINE)
		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
	acm->ctrlsize = ctrlsize;
	acm->readsize = readsize;
	acm->rx_buflimit = num_rx_buf;
	INIT_WORK(&acm->work, acm_softint);
	init_waitqueue_head(&acm->wioctl);
	spin_lock_init(&acm->write_lock);
	spin_lock_init(&acm->read_lock);
	mutex_init(&acm->mutex);
	if (usb_endpoint_xfer_int(epread)) {
		acm->bInterval = epread->bInterval;
		acm->in = usb_rcvintpipe(usb_dev, epread->bEndpointAddress);
	} else {
		acm->in = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
	}
	if (usb_endpoint_xfer_int(epwrite))
		acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress);
	else
		acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress);
	init_usb_anchor(&acm->delayed);
	acm->quirks = quirks;

	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
	if (!buf)
		goto alloc_fail1;
	acm->ctrl_buffer = buf;

	if (acm_write_buffers_alloc(acm) < 0)
		goto alloc_fail2;

	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
	if (!acm->ctrlurb)
		goto alloc_fail3;

	for (i = 0; i < num_rx_buf; i++) {
		struct acm_rb *rb = &(acm->read_buffers[i]);
		struct urb *urb;

		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
								&rb->dma);
		if (!rb->base)
			goto alloc_fail4;
		rb->index = i;
		rb->instance = acm;

		urb = usb_alloc_urb(0, GFP_KERNEL);
		if (!urb)
			goto alloc_fail4;

		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
		urb->transfer_dma = rb->dma;
		if (usb_endpoint_xfer_int(epread))
			usb_fill_int_urb(urb, acm->dev, acm->in, rb->base,
					 acm->readsize,
					 acm_read_bulk_callback, rb,
					 acm->bInterval);
		else
			usb_fill_bulk_urb(urb, acm->dev, acm->in, rb->base,
					  acm->readsize,
					  acm_read_bulk_callback, rb);

		acm->read_urbs[i] = urb;
		__set_bit(i, &acm->read_urbs_free);
	}
	for (i = 0; i < ACM_NW; i++) {
		struct acm_wb *snd = &(acm->wb[i]);

		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
		if (snd->urb == NULL)
			goto alloc_fail5;

		if (usb_endpoint_xfer_int(epwrite))
			usb_fill_int_urb(snd->urb, usb_dev, acm->out,
				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
		else
			usb_fill_bulk_urb(snd->urb, usb_dev, acm->out,
				NULL, acm->writesize, acm_write_bulk, snd);
		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
		if (quirks & SEND_ZERO_PACKET)
			snd->urb->transfer_flags |= URB_ZERO_PACKET;
		snd->instance = acm;
	}

	usb_set_intfdata(intf, acm);

	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
	if (i < 0)
		goto alloc_fail5;

	if (h.usb_cdc_country_functional_desc) { /* export the country data */
		struct usb_cdc_country_functional_desc * cfd =
					h.usb_cdc_country_functional_desc;

		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
		if (!acm->country_codes)
			goto skip_countries;
		acm->country_code_size = cfd->bLength - 4;
		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
							cfd->bLength - 4);
		acm->country_rel_date = cfd->iCountryCodeRelDate;

		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
		if (i < 0) {
			kfree(acm->country_codes);
			acm->country_codes = NULL;
			acm->country_code_size = 0;
			goto skip_countries;
		}

		i = device_create_file(&intf->dev,
						&dev_attr_iCountryCodeRelDate);
		if (i < 0) {
			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
			kfree(acm->country_codes);
			acm->country_codes = NULL;
			acm->country_code_size = 0;
			goto skip_countries;
		}
	}

skip_countries:
	usb_fill_int_urb(acm->ctrlurb, usb_dev,
			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
			 /* works around buggy devices */
			 epctrl->bInterval ? epctrl->bInterval : 16);
	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
	acm->notification_buffer = NULL;
	acm->nb_index = 0;
	acm->nb_size = 0;

	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);

	acm->line.dwDTERate = cpu_to_le32(9600);
	acm->line.bDataBits = 8;
	acm_set_line(acm, &acm->line);

	usb_driver_claim_interface(&acm_driver, data_interface, acm);
	usb_set_intfdata(data_interface, acm);

	usb_get_intf(control_interface);
	tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor,
			&control_interface->dev);
	if (IS_ERR(tty_dev)) {
		rv = PTR_ERR(tty_dev);
		goto alloc_fail6;
	}

	if (quirks & CLEAR_HALT_CONDITIONS) {
		usb_clear_halt(usb_dev, acm->in);
		usb_clear_halt(usb_dev, acm->out);
	}

	return 0;
alloc_fail6:
	if (acm->country_codes) {
		device_remove_file(&acm->control->dev,
				&dev_attr_wCountryCodes);
		device_remove_file(&acm->control->dev,
				&dev_attr_iCountryCodeRelDate);
		kfree(acm->country_codes);
	}
	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
alloc_fail5:
	usb_set_intfdata(intf, NULL);
	for (i = 0; i < ACM_NW; i++)
		usb_free_urb(acm->wb[i].urb);
alloc_fail4:
	for (i = 0; i < num_rx_buf; i++)
		usb_free_urb(acm->read_urbs[i]);
	acm_read_buffers_free(acm);
	usb_free_urb(acm->ctrlurb);
alloc_fail3:
	acm_write_buffers_free(acm);
alloc_fail2:
	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
alloc_fail1:
	tty_port_put(&acm->port);
alloc_fail:
	return rv;
}

In the Linux kernel before 5.2.10, there is a use-after-free bug that can be caused by a malicious USB device in the drivers/usb/class/cdc-acm.c driver, aka CID-c52873e5a1ef.


static int acm_probe(struct usb_interface *intf,
		     const struct usb_device_id *id)
{
	struct usb_cdc_union_desc *union_header = NULL;
	struct usb_cdc_call_mgmt_descriptor *cmgmd = NULL;
	unsigned char *buffer = intf->altsetting->extra;
	int buflen = intf->altsetting->extralen;
	struct usb_interface *control_interface;
	struct usb_interface *data_interface;
	struct usb_endpoint_descriptor *epctrl = NULL;
	struct usb_endpoint_descriptor *epread = NULL;
	struct usb_endpoint_descriptor *epwrite = NULL;
	struct usb_device *usb_dev = interface_to_usbdev(intf);
	struct usb_cdc_parsed_header h;
	struct acm *acm;
	int minor;
	int ctrlsize, readsize;
	u8 *buf;
	int call_intf_num = -1;
	int data_intf_num = -1;
	unsigned long quirks;
	int num_rx_buf;
	int i;
	int combined_interfaces = 0;
	struct device *tty_dev;
	int rv = -ENOMEM;
	int res;

	/* normal quirks */
	quirks = (unsigned long)id->driver_info;

	if (quirks == IGNORE_DEVICE)
		return -ENODEV;

	memset(&h, 0x00, sizeof(struct usb_cdc_parsed_header));

	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;

	/* handle quirks deadly to normal probing*/
	if (quirks == NO_UNION_NORMAL) {
		data_interface = usb_ifnum_to_if(usb_dev, 1);
		control_interface = usb_ifnum_to_if(usb_dev, 0);
		/* we would crash */
		if (!data_interface || !control_interface)
			return -ENODEV;
		goto skip_normal_probe;
	}

	/* normal probing*/
	if (!buffer) {
		dev_err(&intf->dev, "Weird descriptor references\n");
		return -EINVAL;
	}

	if (!intf->cur_altsetting)
		return -EINVAL;

	if (!buflen) {
		if (intf->cur_altsetting->endpoint &&
				intf->cur_altsetting->endpoint->extralen &&
				intf->cur_altsetting->endpoint->extra) {
			dev_dbg(&intf->dev,
				"Seeking extra descriptors on endpoint\n");
			buflen = intf->cur_altsetting->endpoint->extralen;
			buffer = intf->cur_altsetting->endpoint->extra;
		} else {
			dev_err(&intf->dev,
				"Zero length descriptor references\n");
			return -EINVAL;
		}
	}

	cdc_parse_cdc_header(&h, intf, buffer, buflen);
	union_header = h.usb_cdc_union_desc;
	cmgmd = h.usb_cdc_call_mgmt_descriptor;
	if (cmgmd)
		call_intf_num = cmgmd->bDataInterface;

	if (!union_header) {
		if (call_intf_num > 0) {
			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
			/* quirks for Droids MuIn LCD */
			if (quirks & NO_DATA_INTERFACE) {
				data_interface = usb_ifnum_to_if(usb_dev, 0);
			} else {
				data_intf_num = call_intf_num;
				data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
			}
			control_interface = intf;
		} else {
			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
				return -ENODEV;
			} else {
				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
				combined_interfaces = 1;
				control_interface = data_interface = intf;
				goto look_for_collapsed_interface;
			}
		}
	} else {
		data_intf_num = union_header->bSlaveInterface0;
		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
		data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
	}

	if (!control_interface || !data_interface) {
		dev_dbg(&intf->dev, "no interfaces\n");
		return -ENODEV;
	}
	if (!data_interface->cur_altsetting || !control_interface->cur_altsetting)
		return -ENODEV;

	if (data_intf_num != call_intf_num)
		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");

	if (control_interface == data_interface) {
		/* some broken devices designed for windows work this way */
		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
		combined_interfaces = 1;
		/* a popular other OS doesn't use it */
		quirks |= NO_CAP_LINE;
		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
			return -EINVAL;
		}
look_for_collapsed_interface:
		res = usb_find_common_endpoints(data_interface->cur_altsetting,
				&epread, &epwrite, &epctrl, NULL);
		if (res)
			return res;

		goto made_compressed_probe;
	}

skip_normal_probe:

	/*workaround for switched interfaces */
	if (data_interface->cur_altsetting->desc.bInterfaceClass
						!= CDC_DATA_INTERFACE_TYPE) {
		if (control_interface->cur_altsetting->desc.bInterfaceClass
						== CDC_DATA_INTERFACE_TYPE) {
			dev_dbg(&intf->dev,
				"Your device has switched interfaces.\n");
			swap(control_interface, data_interface);
		} else {
			return -EINVAL;
		}
	}

	/* Accept probe requests only for the control interface */
	if (!combined_interfaces && intf != control_interface)
		return -ENODEV;

	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
		/* valid in this context */
		dev_dbg(&intf->dev, "The data interface isn't available\n");
		return -EBUSY;
	}

	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
	    control_interface->cur_altsetting->desc.bNumEndpoints == 0)
		return -EINVAL;

	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
	epread = &data_interface->cur_altsetting->endpoint[0].desc;
	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;

	/* workaround for switched endpoints */
	if (!usb_endpoint_dir_in(epread)) {
		/* descriptors are swapped */
		dev_dbg(&intf->dev,
			"The data interface has switched endpoints\n");
		swap(epread, epwrite);
	}
made_compressed_probe:
	dev_dbg(&intf->dev, "interfaces are valid\n");

	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
	if (acm == NULL)
		goto alloc_fail;

	tty_port_init(&acm->port);
	acm->port.ops = &acm_port_ops;

	ctrlsize = usb_endpoint_maxp(epctrl);
	readsize = usb_endpoint_maxp(epread) *
				(quirks == SINGLE_RX_URB ? 1 : 2);
	acm->combined_interfaces = combined_interfaces;
	acm->writesize = usb_endpoint_maxp(epwrite) * 20;
	acm->control = control_interface;
	acm->data = data_interface;

	usb_get_intf(acm->control); /* undone in destruct() */

	minor = acm_alloc_minor(acm);
	if (minor < 0)
		goto alloc_fail1;

	acm->minor = minor;
	acm->dev = usb_dev;
	if (h.usb_cdc_acm_descriptor)
		acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
	if (quirks & NO_CAP_LINE)
		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
	acm->ctrlsize = ctrlsize;
	acm->readsize = readsize;
	acm->rx_buflimit = num_rx_buf;
	INIT_WORK(&acm->work, acm_softint);
	init_waitqueue_head(&acm->wioctl);
	spin_lock_init(&acm->write_lock);
	spin_lock_init(&acm->read_lock);
	mutex_init(&acm->mutex);
	if (usb_endpoint_xfer_int(epread)) {
		acm->bInterval = epread->bInterval;
		acm->in = usb_rcvintpipe(usb_dev, epread->bEndpointAddress);
	} else {
		acm->in = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
	}
	if (usb_endpoint_xfer_int(epwrite))
		acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress);
	else
		acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress);
	init_usb_anchor(&acm->delayed);
	acm->quirks = quirks;

	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
	if (!buf)
		goto alloc_fail1;
	acm->ctrl_buffer = buf;

	if (acm_write_buffers_alloc(acm) < 0)
		goto alloc_fail2;

	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
	if (!acm->ctrlurb)
		goto alloc_fail3;

	for (i = 0; i < num_rx_buf; i++) {
		struct acm_rb *rb = &(acm->read_buffers[i]);
		struct urb *urb;

		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
								&rb->dma);
		if (!rb->base)
			goto alloc_fail4;
		rb->index = i;
		rb->instance = acm;

		urb = usb_alloc_urb(0, GFP_KERNEL);
		if (!urb)
			goto alloc_fail4;

		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
		urb->transfer_dma = rb->dma;
		if (usb_endpoint_xfer_int(epread))
			usb_fill_int_urb(urb, acm->dev, acm->in, rb->base,
					 acm->readsize,
					 acm_read_bulk_callback, rb,
					 acm->bInterval);
		else
			usb_fill_bulk_urb(urb, acm->dev, acm->in, rb->base,
					  acm->readsize,
					  acm_read_bulk_callback, rb);

		acm->read_urbs[i] = urb;
		__set_bit(i, &acm->read_urbs_free);
	}
	for (i = 0; i < ACM_NW; i++) {
		struct acm_wb *snd = &(acm->wb[i]);

		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
		if (snd->urb == NULL)
			goto alloc_fail5;

		if (usb_endpoint_xfer_int(epwrite))
			usb_fill_int_urb(snd->urb, usb_dev, acm->out,
				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
		else
			usb_fill_bulk_urb(snd->urb, usb_dev, acm->out,
				NULL, acm->writesize, acm_write_bulk, snd);
		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
		if (quirks & SEND_ZERO_PACKET)
			snd->urb->transfer_flags |= URB_ZERO_PACKET;
		snd->instance = acm;
	}

	usb_set_intfdata(intf, acm);

	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
	if (i < 0)
		goto alloc_fail5;

	if (h.usb_cdc_country_functional_desc) { /* export the country data */
		struct usb_cdc_country_functional_desc * cfd =
					h.usb_cdc_country_functional_desc;

		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
		if (!acm->country_codes)
			goto skip_countries;
		acm->country_code_size = cfd->bLength - 4;
		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
							cfd->bLength - 4);
		acm->country_rel_date = cfd->iCountryCodeRelDate;

		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
		if (i < 0) {
			kfree(acm->country_codes);
			acm->country_codes = NULL;
			acm->country_code_size = 0;
			goto skip_countries;
		}

		i = device_create_file(&intf->dev,
						&dev_attr_iCountryCodeRelDate);
		if (i < 0) {
			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
			kfree(acm->country_codes);
			acm->country_codes = NULL;
			acm->country_code_size = 0;
			goto skip_countries;
		}
	}

skip_countries:
	usb_fill_int_urb(acm->ctrlurb, usb_dev,
			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
			 /* works around buggy devices */
			 epctrl->bInterval ? epctrl->bInterval : 16);
	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
	acm->notification_buffer = NULL;
	acm->nb_index = 0;
	acm->nb_size = 0;

	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);

	acm->line.dwDTERate = cpu_to_le32(9600);
	acm->line.bDataBits = 8;
	acm_set_line(acm, &acm->line);

	usb_driver_claim_interface(&acm_driver, data_interface, acm);
	usb_set_intfdata(data_interface, acm);

	tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor,
			&control_interface->dev);
	if (IS_ERR(tty_dev)) {
		rv = PTR_ERR(tty_dev);
		goto alloc_fail6;
	}

	if (quirks & CLEAR_HALT_CONDITIONS) {
		usb_clear_halt(usb_dev, acm->in);
		usb_clear_halt(usb_dev, acm->out);
	}

	return 0;
alloc_fail6:
	if (acm->country_codes) {
		device_remove_file(&acm->control->dev,
				&dev_attr_wCountryCodes);
		device_remove_file(&acm->control->dev,
				&dev_attr_iCountryCodeRelDate);
		kfree(acm->country_codes);
	}
	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
alloc_fail5:
	usb_set_intfdata(intf, NULL);
	for (i = 0; i < ACM_NW; i++)
		usb_free_urb(acm->wb[i].urb);
alloc_fail4:
	for (i = 0; i < num_rx_buf; i++)
		usb_free_urb(acm->read_urbs[i]);
	acm_read_buffers_free(acm);
	usb_free_urb(acm->ctrlurb);
alloc_fail3:
	acm_write_buffers_free(acm);
alloc_fail2:
	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
alloc_fail1:
	tty_port_put(&acm->port);
alloc_fail:
	return rv;
}


Fix string format vulnerability

If the message in g_log_structured itself
contained format sequences like %d or %n they
were applied again, leading to leaked stack contents
and possibly memory corruption. It can be triggered
e.g. by a volume label containing format sequences.

Print the message argument itself into a "%s" string
to avoid intepreting format sequences.

https://github.com/storaged-project/udisks/issues/578

udisks_log (UDisksLogLevel     level,
            const gchar       *function,
            const gchar       *location,
            const gchar       *format,
            ...)
{
  va_list var_args;
  gchar *message;

  va_start (var_args, format);
  message = g_strdup_vprintf (format, var_args);
  va_end (var_args);

#if GLIB_CHECK_VERSION(2, 50, 0)
  g_log_structured ("udisks", (GLogLevelFlags) level,
                    "MESSAGE", message, "THREAD_ID", "%d", (gint) syscall (SYS_gettid),
                    "CODE_FUNC", function, "CODE_FILE", location);
#else
  g_log ("udisks", level, "[%d]: %s [%s, %s()]", (gint) syscall (SYS_gettid), message, location, function);
#endif

  g_free (message);
}

UDisks 2.8.0 has a format string vulnerability in udisks_log in udiskslogging.c, allowing attackers to obtain sensitive information (stack contents), cause a denial of service (memory corruption), or possibly have unspecified other impact via a malformed filesystem label, as demonstrated by %d or %n substrings.


udisks_log (UDisksLogLevel     level,
            const gchar       *function,
            const gchar       *location,
            const gchar       *format,
            ...)
{
  va_list var_args;
  gchar *message;

  va_start (var_args, format);
  message = g_strdup_vprintf (format, var_args);
  va_end (var_args);

#if GLIB_CHECK_VERSION(2, 50, 0)
  g_log_structured ("udisks", (GLogLevelFlags) level,
                    "MESSAGE", "%s", message, "THREAD_ID", "%d", (gint) syscall (SYS_gettid),
                    "CODE_FUNC", function, "CODE_FILE", location);
#else
  g_log ("udisks", level, "[%d]: %s [%s, %s()]", (gint) syscall (SYS_gettid), message, location, function);
#endif

  g_free (message);
}


Bug 701788: include space for string terminator in call to malloc in cif_print_page().

Fixes ./sanbin/gs -sOutputFile=tmp -sDEVICE=cif ../bug-701788.pdf

cif_print_page(gx_device_printer *pdev, gp_file *prn_stream)
{	int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
        int lnum;
        byte *in = (byte *)gs_malloc(pdev->memory, line_size, 1, "cif_print_page(in)");
        char *s;
        int scanline, scanbyte;
        int length, start; /* length is the number of successive 1 bits, */
                           /* start is the set of 1 bit start position */

        if (in == 0)
                return_error(gs_error_VMerror);

        if ((s = strchr(pdev->fname, '.')) == NULL)
                length = strlen(pdev->fname) + 1;
        else
                length = s - pdev->fname;
        s = (char *)gs_malloc(pdev->memory, length, sizeof(char), "cif_print_page(s)");

        strncpy(s, pdev->fname, length);
        *(s + length) = '\0';
        gp_fprintf(prn_stream, "DS1 25 1;\n9 %s;\nLCP;\n", s);
        gs_free(pdev->memory, s, length, 1, "cif_print_page(s)");

   for (lnum = 0; lnum < pdev->height; lnum++) {
      gdev_prn_copy_scan_lines(pdev, lnum, in, line_size);
      length = 0;
      for (scanline = 0; scanline < line_size; scanline++)
#ifdef TILE			/* original, simple, inefficient algorithm */
         for (scanbyte = 0; scanbyte < 8; scanbyte++)
            if (((in[scanline] >> scanbyte) & 1) != 0)
               gp_fprintf(prn_stream, "B4 4 %d %d;\n",
                  (scanline * 8 + (7 - scanbyte)) * 4,
                  (pdev->height - lnum) * 4);
#else				/* better algorithm */
         for (scanbyte = 7; scanbyte >= 0; scanbyte--)
            /* cheap linear reduction of rectangles in lines */
            if (((in[scanline] >> scanbyte) & 1) != 0) {
               if (length == 0)
                  start = (scanline * 8 + (7 - scanbyte));
               length++;
            } else {
               if (length != 0)
                  gp_fprintf(prn_stream, "B%d 4 %d %d;\n", length * 4,
                           start * 4 + length * 2,
                           (pdev->height - lnum) * 4);
               length = 0;
            }
#endif
   }
        gp_fprintf(prn_stream, "DF;\nC1;\nE\n");
        gs_free(pdev->memory, in, line_size, 1, "cif_print_page(in)");
        return 0;
}

A buffer overflow vulnerability in cif_print_page() in devices/gdevcif.c of Artifex Software GhostScript v9.50 allows a remote attacker to cause a denial of service via a crafted PDF file. This is fixed in v9.51.


cif_print_page(gx_device_printer *pdev, gp_file *prn_stream)
{	int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
        int lnum;
        byte *in = (byte *)gs_malloc(pdev->memory, line_size, 1, "cif_print_page(in)");
        char *s;
        int scanline, scanbyte;
        int length, start; /* length is the number of successive 1 bits, */
                           /* start is the set of 1 bit start position */

        if (in == 0)
                return_error(gs_error_VMerror);

        if ((s = strchr(pdev->fname, '.')) == NULL)
                length = strlen(pdev->fname) + 1;
        else
                length = s - pdev->fname;
        s = (char *)gs_malloc(pdev->memory, length+1, sizeof(char), "cif_print_page(s)");

        strncpy(s, pdev->fname, length);
        *(s + length) = '\0';
        gp_fprintf(prn_stream, "DS1 25 1;\n9 %s;\nLCP;\n", s);
        gs_free(pdev->memory, s, length+1, 1, "cif_print_page(s)");

   for (lnum = 0; lnum < pdev->height; lnum++) {
      gdev_prn_copy_scan_lines(pdev, lnum, in, line_size);
      length = 0;
      for (scanline = 0; scanline < line_size; scanline++)
#ifdef TILE			/* original, simple, inefficient algorithm */
         for (scanbyte = 0; scanbyte < 8; scanbyte++)
            if (((in[scanline] >> scanbyte) & 1) != 0)
               gp_fprintf(prn_stream, "B4 4 %d %d;\n",
                  (scanline * 8 + (7 - scanbyte)) * 4,
                  (pdev->height - lnum) * 4);
#else				/* better algorithm */
         for (scanbyte = 7; scanbyte >= 0; scanbyte--)
            /* cheap linear reduction of rectangles in lines */
            if (((in[scanline] >> scanbyte) & 1) != 0) {
               if (length == 0)
                  start = (scanline * 8 + (7 - scanbyte));
               length++;
            } else {
               if (length != 0)
                  gp_fprintf(prn_stream, "B%d 4 %d %d;\n", length * 4,
                           start * 4 + length * 2,
                           (pdev->height - lnum) * 4);
               length = 0;
            }
#endif
   }
        gp_fprintf(prn_stream, "DF;\nC1;\nE\n");
        gs_free(pdev->memory, in, line_size, 1, "cif_print_page(in)");
        return 0;
}


Fix crash in bouncedcc module.

It happens when DCC RESUME is received.
Affected ZNC versions: 0.200, 0.202.

Thanks to howeyc for reporting this and providing the patch.

	virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) {
		if (sMessage.Equals("DCC ", false, 4) && m_pUser->IsUserAttached()) {
			// DCC CHAT chat 2453612361 44592
			CString sType = sMessage.Token(1);
			CString sFile = sMessage.Token(2);
			unsigned long uLongIP = sMessage.Token(3).ToULong();
			unsigned short uPort = sMessage.Token(4).ToUShort();
			unsigned long uFileSize = sMessage.Token(5).ToULong();

			if (sType.Equals("CHAT")) {
				CNick FromNick(Nick.GetNickMask());
				unsigned short uBNCPort = CDCCBounce::DCCRequest(FromNick.GetNick(), uLongIP, uPort, "", true, this, CUtils::GetIP(uLongIP));
				if (uBNCPort) {
					CString sIP = GetLocalDCCIP();
					m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC CHAT chat " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + "\001");
				}
			} else if (sType.Equals("SEND")) {
				// DCC SEND readme.txt 403120438 5550 1104
				unsigned short uBNCPort = CDCCBounce::DCCRequest(Nick.GetNick(), uLongIP, uPort, sFile, false, this, CUtils::GetIP(uLongIP));
				if (uBNCPort) {
					CString sIP = GetLocalDCCIP();
					m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC SEND " + sFile + " " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + " " + CString(uFileSize) + "\001");
				}
			} else if (sType.Equals("RESUME")) {
				// Need to lookup the connection by port, filter the port, and forward to the user
				unsigned short uResumePort = sMessage.Token(3).ToUShort();

				set<CSocket*>::const_iterator it;
				for (it = BeginSockets(); it != EndSockets(); ++it) {
					CDCCBounce* pSock = (CDCCBounce*) *it;

					if (pSock->GetLocalPort() == uResumePort) {
						m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pClient->GetNick() + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetUserPort()) + " " + sMessage.Token(4) + "\001");
					}
				}
			} else if (sType.Equals("ACCEPT")) {
				// Need to lookup the connection by port, filter the port, and forward to the user
				set<CSocket*>::const_iterator it;
				for (it = BeginSockets(); it != EndSockets(); ++it) {
					CDCCBounce* pSock = (CDCCBounce*) *it;

					if (pSock->GetUserPort() == sMessage.Token(3).ToUShort()) {
						m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pClient->GetNick() + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetLocalPort()) + " " + sMessage.Token(4) + "\001");
					}
				}
			}

			return HALTCORE;
		}

		return CONTINUE;
	}

The CBounceDCCMod::OnPrivCTCP function in bouncedcc.cpp in the bouncedcc module in ZNC 0.200 and 0.202 allows remote attackers to cause a denial of service (crash) via a crafted DCC RESUME request.


	virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) {
		if (sMessage.Equals("DCC ", false, 4) && m_pUser->IsUserAttached()) {
			// DCC CHAT chat 2453612361 44592
			CString sType = sMessage.Token(1);
			CString sFile = sMessage.Token(2);
			unsigned long uLongIP = sMessage.Token(3).ToULong();
			unsigned short uPort = sMessage.Token(4).ToUShort();
			unsigned long uFileSize = sMessage.Token(5).ToULong();

			if (sType.Equals("CHAT")) {
				CNick FromNick(Nick.GetNickMask());
				unsigned short uBNCPort = CDCCBounce::DCCRequest(FromNick.GetNick(), uLongIP, uPort, "", true, this, CUtils::GetIP(uLongIP));
				if (uBNCPort) {
					CString sIP = GetLocalDCCIP();
					m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC CHAT chat " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + "\001");
				}
			} else if (sType.Equals("SEND")) {
				// DCC SEND readme.txt 403120438 5550 1104
				unsigned short uBNCPort = CDCCBounce::DCCRequest(Nick.GetNick(), uLongIP, uPort, sFile, false, this, CUtils::GetIP(uLongIP));
				if (uBNCPort) {
					CString sIP = GetLocalDCCIP();
					m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC SEND " + sFile + " " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + " " + CString(uFileSize) + "\001");
				}
			} else if (sType.Equals("RESUME")) {
				// Need to lookup the connection by port, filter the port, and forward to the user
				unsigned short uResumePort = sMessage.Token(3).ToUShort();

				set<CSocket*>::const_iterator it;
				for (it = BeginSockets(); it != EndSockets(); ++it) {
					CDCCBounce* pSock = (CDCCBounce*) *it;

					if (pSock->GetLocalPort() == uResumePort) {
						m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetUserPort()) + " " + sMessage.Token(4) + "\001");
					}
				}
			} else if (sType.Equals("ACCEPT")) {
				// Need to lookup the connection by port, filter the port, and forward to the user
				set<CSocket*>::const_iterator it;
				for (it = BeginSockets(); it != EndSockets(); ++it) {
					CDCCBounce* pSock = (CDCCBounce*) *it;

					if (pSock->GetUserPort() == sMessage.Token(3).ToUShort()) {
						m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetLocalPort()) + " " + sMessage.Token(4) + "\001");
					}
				}
			}

			return HALTCORE;
		}

		return CONTINUE;
	}