Commit message: tjLoadImage(): Fix FPE triggered by malformed BMP

In rdbmp.c, it is necessary to guard against 32-bit overflow/wraparound
when allocating the row buffer, because since BMP files have 32-bit
width and height fields, the value of biWidth can be up to 4294967295.
Specifically, if biWidth is 1073741824 and cinfo->input_components = 4,
then the samplesperrow argument in alloc_sarray() would wrap around to
0, and a division by zero error would occur at line 458 in jmemmgr.c.

If biWidth is set to a higher value, then samplesperrow would wrap
around to a small number, which would likely cause a buffer overflow
(this has not been tested or verified.)
CVE: CVE-2018-1152
CWE: CWE-369
Vulnerability description: libjpeg-turbo 1.5.90 is vulnerable to a denial of service vulnerability caused by a divide by zero when processing a crafted BMP image.

Project: libjpeg-turbo

GLOBAL(cjpeg_source_ptr)
jinit_read_bmp(j_compress_ptr cinfo, boolean use_inversion_array)
{
  bmp_source_ptr source;

  /* Create module interface object */
  source = (bmp_source_ptr)
    (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
                                sizeof(bmp_source_struct));
  source->cinfo = cinfo;        /* make back link for subroutines */
  /* Fill in method ptrs, except get_pixel_rows which start_input sets */
  source->pub.start_input = start_input_bmp;
  source->pub.finish_input = finish_input_bmp;

  source->use_inversion_array = use_inversion_array;

  return (cjpeg_source_ptr)source;
}

Project: libjpeg-turbo

METHODDEF(JDIMENSION)
get_24bit_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading 24-bit pixels */
{
  bmp_source_ptr source = (bmp_source_ptr)sinfo;
  JSAMPARRAY image_ptr;
  register JSAMPROW inptr, outptr;
  register JDIMENSION col;

  if (source->use_inversion_array) {
    /* Fetch next row from virtual array */
    source->source_row--;
    image_ptr = (*cinfo->mem->access_virt_sarray)
      ((j_common_ptr)cinfo, source->whole_image,
       source->source_row, (JDIMENSION)1, FALSE);
    inptr = image_ptr[0];
  } else {
    if (!ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
      ERREXIT(cinfo, JERR_INPUT_EOF);
    inptr = source->iobuffer;
  }

  /* Transfer data.  Note source values are in BGR order
   * (even though Microsoft's own documents say the opposite).
   */
  outptr = source->pub.buffer[0];
  if (cinfo->in_color_space == JCS_EXT_BGR) {
    MEMCOPY(outptr, inptr, source->row_width);
  } else if (cinfo->in_color_space == JCS_CMYK) {
    for (col = cinfo->image_width; col > 0; col--) {
      /* can omit GETJSAMPLE() safely */
      JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
      rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
      outptr += 4;
    }
  } else {
    register int rindex = rgb_red[cinfo->in_color_space];
    register int gindex = rgb_green[cinfo->in_color_space];
    register int bindex = rgb_blue[cinfo->in_color_space];
    register int aindex = alpha_index[cinfo->in_color_space];
    register int ps = rgb_pixelsize[cinfo->in_color_space];

    if (aindex >= 0) {
      for (col = cinfo->image_width; col > 0; col--) {
        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
        outptr[gindex] = *inptr++;
        outptr[rindex] = *inptr++;
        outptr[aindex] = 0xFF;
        outptr += ps;
      }
    } else {
      for (col = cinfo->image_width; col > 0; col--) {
        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
        outptr[gindex] = *inptr++;
        outptr[rindex] = *inptr++;
        outptr += ps;
      }
    }
  }

  return 1;
}

Project: libjpeg-turbo

METHODDEF(JDIMENSION)
get_32bit_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading 32-bit pixels */
{
  bmp_source_ptr source = (bmp_source_ptr)sinfo;
  JSAMPARRAY image_ptr;
  register JSAMPROW inptr, outptr;
  register JDIMENSION col;

  if (source->use_inversion_array) {
    /* Fetch next row from virtual array */
    source->source_row--;
    image_ptr = (*cinfo->mem->access_virt_sarray)
      ((j_common_ptr)cinfo, source->whole_image,
       source->source_row, (JDIMENSION)1, FALSE);
    inptr = image_ptr[0];
  } else {
    if (!ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
      ERREXIT(cinfo, JERR_INPUT_EOF);
    inptr = source->iobuffer;
  }

  /* Transfer data.  Note source values are in BGR order
   * (even though Microsoft's own documents say the opposite).
   */
  outptr = source->pub.buffer[0];
  if (cinfo->in_color_space == JCS_EXT_BGRX ||
      cinfo->in_color_space == JCS_EXT_BGRA) {
    MEMCOPY(outptr, inptr, source->row_width);
  } else if (cinfo->in_color_space == JCS_CMYK) {
    for (col = cinfo->image_width; col > 0; col--) {
      /* can omit GETJSAMPLE() safely */
      JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
      rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
      inptr++;                          /* skip the 4th byte (Alpha channel) */
      outptr += 4;
    }
  } else {
    register int rindex = rgb_red[cinfo->in_color_space];
    register int gindex = rgb_green[cinfo->in_color_space];
    register int bindex = rgb_blue[cinfo->in_color_space];
    register int aindex = alpha_index[cinfo->in_color_space];
    register int ps = rgb_pixelsize[cinfo->in_color_space];

    if (aindex >= 0) {
      for (col = cinfo->image_width; col > 0; col--) {
        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
        outptr[gindex] = *inptr++;
        outptr[rindex] = *inptr++;
        outptr[aindex] = *inptr++;
        outptr += ps;
      }
    } else {
      for (col = cinfo->image_width; col > 0; col--) {
        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
        outptr[gindex] = *inptr++;
        outptr[rindex] = *inptr++;
        inptr++;                        /* skip the 4th byte (Alpha channel) */
        outptr += ps;
      }
    }
  }

  return 1;
}

Project: libjpeg-turbo

METHODDEF(JDIMENSION)
preload_image(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
{
  bmp_source_ptr source = (bmp_source_ptr)sinfo;
  register FILE *infile = source->pub.input_file;
  register JSAMPROW out_ptr;
  JSAMPARRAY image_ptr;
  JDIMENSION row;
  cd_progress_ptr progress = (cd_progress_ptr)cinfo->progress;

  /* Read the data into a virtual array in input-file row order. */
  for (row = 0; row < cinfo->image_height; row++) {
    if (progress != NULL) {
      progress->pub.pass_counter = (long)row;
      progress->pub.pass_limit = (long)cinfo->image_height;
      (*progress->pub.progress_monitor) ((j_common_ptr)cinfo);
    }
    image_ptr = (*cinfo->mem->access_virt_sarray)
      ((j_common_ptr)cinfo, source->whole_image, row, (JDIMENSION)1, TRUE);
    out_ptr = image_ptr[0];
    if (fread(out_ptr, 1, source->row_width, infile) != source->row_width) {
      if (feof(infile))
        ERREXIT(cinfo, JERR_INPUT_EOF);
      else
        ERREXIT(cinfo, JERR_FILE_READ);
    }
  }
  if (progress != NULL)
    progress->completed_extra_passes++;

  /* Set up to read from the virtual array in top-to-bottom order */
  switch (source->bits_per_pixel) {
  case 8:
    source->pub.get_pixel_rows = get_8bit_row;
    break;
  case 24:
    source->pub.get_pixel_rows = get_24bit_row;
    break;
  case 32:
    source->pub.get_pixel_rows = get_32bit_row;
    break;
  default:
    ERREXIT(cinfo, JERR_BMP_BADDEPTH);
  }
  source->source_row = cinfo->image_height;

  /* And read the first row */
  return (*source->pub.get_pixel_rows) (cinfo, sinfo);
}

Project: libjpeg-turbo
File: rdbmp.c

finish_input_bmp(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
{
  /* no work */
}

Project: libjpeg-turbo
File: rdbmp.c

read_byte(bmp_source_ptr sinfo)
/* Read next byte from BMP file */
{
  register FILE *infile = sinfo->pub.input_file;
  register int c;

  if ((c = getc(infile)) == EOF)
    ERREXIT(sinfo->cinfo, JERR_INPUT_EOF);
  return c;
}

Project: libjpeg-turbo
File: rdbmp.c

read_colormap(bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
/* Read the colormap from a BMP file */
{
  int i, gray = 1;

  switch (mapentrysize) {
  case 3:
    /* BGR format (occurs in OS/2 files) */
    for (i = 0; i < cmaplen; i++) {
      sinfo->colormap[2][i] = (JSAMPLE)read_byte(sinfo);
      sinfo->colormap[1][i] = (JSAMPLE)read_byte(sinfo);
      sinfo->colormap[0][i] = (JSAMPLE)read_byte(sinfo);
      if (sinfo->colormap[2][i] != sinfo->colormap[1][i] ||
          sinfo->colormap[1][i] != sinfo->colormap[0][i])
        gray = 0;
    }
    break;
  case 4:
    /* BGR0 format (occurs in MS Windows files) */
    for (i = 0; i < cmaplen; i++) {
      sinfo->colormap[2][i] = (JSAMPLE)read_byte(sinfo);
      sinfo->colormap[1][i] = (JSAMPLE)read_byte(sinfo);
      sinfo->colormap[0][i] = (JSAMPLE)read_byte(sinfo);
      (void)read_byte(sinfo);
      if (sinfo->colormap[2][i] != sinfo->colormap[1][i] ||
          sinfo->colormap[1][i] != sinfo->colormap[0][i])
        gray = 0;
    }
    break;
  default:
    ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP);
    break;
  }

  if (sinfo->cinfo->in_color_space == JCS_UNKNOWN && gray)
    sinfo->cinfo->in_color_space = JCS_GRAYSCALE;

  if (sinfo->cinfo->in_color_space == JCS_GRAYSCALE && !gray)
    ERREXIT(sinfo->cinfo, JERR_BAD_IN_COLORSPACE);
}