Commit message: JPEG2000Stream: fail gracefully if not all components have the same WxH

I think this is just a mistake, or at least the only file we have with
this scenario is a fuzzed one
CVE: CVE-2019-12293
CWE: CWE-125
Vulnerability description: In Poppler through 0.76.1, there is a heap-based buffer over-read in JPXStream::init in JPEG2000Stream.cc via data with inconsistent heights or widths.

Project: poppler
File: JPEG2000Stream.cc

void JPXStreamPrivate::init2(OPJ_CODEC_FORMAT format, unsigned char *buf, int length, bool indexed)
{
  JPXData jpxData;

  jpxData.data = buf;
  jpxData.pos = 0;
  jpxData.size = length;

  opj_stream_t *stream;

  stream = opj_stream_default_create(OPJ_TRUE);

#if OPENJPEG_VERSION >= OPENJPEG_VERSION_ENCODE(2, 1, 0)
  opj_stream_set_user_data (stream, &jpxData, nullptr);
#else
  opj_stream_set_user_data (stream, &jpxData);
#endif

  opj_stream_set_read_function(stream, jpxRead_callback);
  opj_stream_set_skip_function(stream, jpxSkip_callback);
  opj_stream_set_seek_function(stream, jpxSeek_callback);
  /* Set the length to avoid an assert */
  opj_stream_set_user_data_length(stream, length);

  opj_codec_t *decoder;

  /* Use default decompression parameters */
  opj_dparameters_t parameters;
  opj_set_default_decoder_parameters(&parameters);
  if (indexed)
    parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;

  /* Get the decoder handle of the format */
  decoder = opj_create_decompress(format);
  if (decoder == nullptr) {
    error(errSyntaxWarning, -1, "Unable to create decoder");
    goto error;
  }

  /* Catch events using our callbacks */
  opj_set_warning_handler(decoder, libopenjpeg_warning_callback, nullptr);
  opj_set_error_handler(decoder, libopenjpeg_error_callback, nullptr);

  /* Setup the decoder decoding parameters */
  if (!opj_setup_decoder(decoder, &parameters)) {
    error(errSyntaxWarning, -1, "Unable to set decoder parameters");
    goto error;
  }

  /* Decode the stream and fill the image structure */
  image = nullptr;
  if (!opj_read_header(stream, decoder, &image)) {
    error(errSyntaxWarning, -1, "Unable to read header");
    goto error;
  }

  /* Optional if you want decode the entire image */
  if (!opj_set_decode_area(decoder, image, parameters.DA_x0,
                           parameters.DA_y0, parameters.DA_x1, parameters.DA_y1)){
    error(errSyntaxWarning, -1, "X2");
    goto error;
  }

  /* Get the decoded image */
  if (!(opj_decode(decoder, stream, image) && opj_end_decompress(decoder, stream))) {
    error(errSyntaxWarning, -1, "Unable to decode image");
    goto error;
  }

  opj_destroy_codec(decoder);
  opj_stream_destroy(stream);

  if (image != nullptr)
    return;

error:
  opj_stream_destroy(stream);
  opj_destroy_codec(decoder);
  if (format == OPJ_CODEC_JP2) {
    error(errSyntaxWarning, -1, "Did no succeed opening JPX Stream as JP2, trying as J2K.");
    init2(OPJ_CODEC_J2K, buf, length, indexed);
  } else if (format == OPJ_CODEC_J2K) {
    error(errSyntaxWarning, -1, "Did no succeed opening JPX Stream as J2K, trying as JPT.");
    init2(OPJ_CODEC_JPT, buf, length, indexed);
  } else {
    error(errSyntaxError, -1, "Did no succeed opening JPX Stream.");
  }
}

Project: poppler
File: JPEG2000Stream.cc

static inline unsigned char adjustComp(int r, int adjust, int depth, int sgndcorr, bool indexed) {
  if (!indexed) {
    r += sgndcorr;
    if (adjust) {
      r = (r >> adjust)+((r >> (adjust-1))%2);
    } else if (depth < 8) {
      r = r << (8 - depth);
    }
  }
  if (unlikely(r > 255))
    r = 255;
  return r;  
}

Project: poppler
File: JPEG2000Stream.cc

static OPJ_SIZE_T jpxRead_callback(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data)
{
  JPXData *jpxData = (JPXData *)p_user_data;
  int len;

  len = jpxData->size - jpxData->pos;
  if (len < 0)
    len = 0;
  if (len == 0)
    return (OPJ_SIZE_T)-1;  /* End of file! */
  if ((OPJ_SIZE_T)len > p_nb_bytes)
    len = p_nb_bytes;
  memcpy(p_buffer, jpxData->data + jpxData->pos, len);
  jpxData->pos += len;
  return len;
}

Project: poppler
File: JPEG2000Stream.cc

void JPXStream::reset() {
  priv->counter = 0;
  priv->ccounter = 0;
}

Project: poppler
File: JPEG2000Stream.cc

int JPXStream::getChars(int nChars, unsigned char *buffer) {
  if (unlikely(priv->inited == false)) { init(); }

  for (int i = 0; i < nChars; ++i) {
    const int c = doGetChar(priv);
    if (likely(c != EOF)) buffer[i] = c;
    else return i;
  }
  return nChars;
}

Project: poppler
File: JPEG2000Stream.cc

static inline int doGetChar(JPXStreamPrivate* priv) {
  const int result = doLookChar(priv);
  if (++priv->ccounter == priv->ncomps) {
    priv->ccounter = 0;
    ++priv->counter;
  }
  return result;
}

Project: poppler
File: JPEG2000Stream.cc

GooString *JPXStream::getPSFilter(int psLevel, const char *indent) {
  return nullptr;
}

Project: poppler
File: JPEG2000Stream.cc

bool JPXStream::isBinary(bool last) {
  return str->isBinary(true);
}

Project: poppler
File: JPEG2000Stream.cc

static OPJ_OFF_T jpxSkip_callback(OPJ_OFF_T skip, void * p_user_data)
{
  JPXData *jpxData = (JPXData *)p_user_data;

  jpxData->pos += (skip > jpxData->size - jpxData->pos) ? jpxData->size - jpxData->pos : skip;
  /* Always return input value to avoid "Problem with skipping JPEG2000 box, stream error" */
  return skip;
}

Project: poppler
File: JPEG2000Stream.cc

static void libopenjpeg_error_callback(const char *msg, void * /*client_data*/) {
  error(errSyntaxError, -1, "{0:s}", msg);
}

Project: poppler
File: JPEG2000Stream.cc

void JPXStream::close() {
  if (priv->image != nullptr) {
    opj_image_destroy(priv->image);
    priv->image = nullptr;
    priv->npixels = 0;
  }
}

Project: poppler
File: JPEG2000Stream.cc

int JPXStream::getChar() {
  if (unlikely(priv->inited == false)) { init(); }

  return doGetChar(priv);
}

Project: poppler
File: JPEG2000Stream.cc

static inline int doLookChar(JPXStreamPrivate* priv) {
  if (unlikely(priv->counter >= priv->npixels))
    return EOF;

  return ((unsigned char *)priv->image->comps[priv->ccounter].data)[priv->counter];
}

Project: poppler
File: JPEG2000Stream.cc

Goffset JPXStream::getPos() {
  return priv->counter * priv->ncomps + priv->ccounter;
}

Project: poppler
File: JPEG2000Stream.cc

static void libopenjpeg_warning_callback(const char *msg, void * /*client_data*/) {
  error(errSyntaxWarning, -1, "{0:s}", msg);
}

Project: poppler
File: JPEG2000Stream.cc

void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) {
  if (unlikely(priv->inited == false)) { init(); }

  *bitsPerComponent = 8;
  int numComps = (priv->image) ? priv->image->numcomps : 1;
  if (priv->image) {
    if (priv->image->color_space == OPJ_CLRSPC_SRGB && numComps == 4) { numComps = 3; }
    else if (priv->image->color_space == OPJ_CLRSPC_SYCC && numComps == 4) { numComps = 3; }
    else if (numComps == 2) { numComps = 1; }
    else if (numComps > 4) { numComps = 4; }
  }
  if (numComps == 3)
    *csMode = streamCSDeviceRGB;
  else if (numComps == 4)
    *csMode = streamCSDeviceCMYK;
  else
    *csMode = streamCSDeviceGray;
}

Project: poppler
File: JPEG2000Stream.cc

int JPXStream::lookChar() {
  if (unlikely(priv->inited == false)) { init(); }

  return doLookChar(priv);
}

Project: poppler
File: JPEG2000Stream.cc

static OPJ_BOOL jpxSeek_callback(OPJ_OFF_T seek_pos, void * p_user_data)
{
  JPXData *jpxData = (JPXData *)p_user_data;

  if (seek_pos > jpxData->size)
    return OPJ_FALSE;
  jpxData->pos = seek_pos;
  return OPJ_TRUE;
}

Project: poppler
File: JPEG2000Stream.cc

JPXStream::JPXStream(Stream *strA) : FilterStream(strA) {
  priv = new JPXStreamPrivate;
  priv->inited = false;
  priv->image = nullptr;
  priv->npixels = 0;
  priv->ncomps = 0;
}

Project: poppler
File: JPEG2000Stream.cc

JPXStream::~JPXStream() {
  delete str;
  close();
  delete priv;
}