Improve JPX fuzzer

This CL splits initialization into two parts so that the fuzzer can
check for enormous sizes in headers before calling the opj decoding
code.

Bug: chromium:903724
Change-Id: I1dc7095d65b55319cb748d8a24206d72dd66390d
Reviewed-on: https://pdfium-review.googlesource.com/c/45732
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Nicolás Peña Moreno <npm@chromium.org>
diff --git a/core/fpdfapi/render/cpdf_dibbase.cpp b/core/fpdfapi/render/cpdf_dibbase.cpp
index 5e48c68..1f1ec21 100644
--- a/core/fpdfapi/render/cpdf_dibbase.cpp
+++ b/core/fpdfapi/render/cpdf_dibbase.cpp
@@ -592,6 +592,9 @@
   if (!context->decoder())
     return nullptr;
 
+  if (!context->decoder()->StartDecode())
+    return nullptr;
+
   uint32_t width = 0;
   uint32_t height = 0;
   uint32_t components = 0;
diff --git a/core/fxcodec/codec/ccodec_jpxmodule.cpp b/core/fxcodec/codec/ccodec_jpxmodule.cpp
index aaaaf81..fb197de 100644
--- a/core/fxcodec/codec/ccodec_jpxmodule.cpp
+++ b/core/fxcodec/codec/ccodec_jpxmodule.cpp
@@ -517,7 +517,10 @@
 
   m_Image = pTempImage;
   m_Image->pdfium_use_colorspace = !!m_ColorSpace;
+  return true;
+}
 
+bool CJPX_Decoder::StartDecode() {
   if (!m_Parameters.nb_tile_to_decode) {
     if (!opj_set_decode_area(m_Codec.Get(), m_Image.Get(), m_Parameters.DA_x0,
                              m_Parameters.DA_y0, m_Parameters.DA_x1,
@@ -554,7 +557,6 @@
     m_Image->icc_profile_buf = nullptr;
     m_Image->icc_profile_len = 0;
   }
-
   return true;
 }
 
diff --git a/core/fxcodec/codec/cjpx_decoder.h b/core/fxcodec/codec/cjpx_decoder.h
index 5be4b87..940efd8 100644
--- a/core/fxcodec/codec/cjpx_decoder.h
+++ b/core/fxcodec/codec/cjpx_decoder.h
@@ -24,6 +24,7 @@
 
   bool Init(pdfium::span<const uint8_t> src_data);
   void GetInfo(uint32_t* width, uint32_t* height, uint32_t* components);
+  bool StartDecode();
   bool Decode(uint8_t* dest_buf,
               uint32_t pitch,
               const std::vector<uint8_t>& offsets);
diff --git a/testing/fuzzers/pdf_jpx_fuzzer.cc b/testing/fuzzers/pdf_jpx_fuzzer.cc
index 011b171..51601f6 100644
--- a/testing/fuzzers/pdf_jpx_fuzzer.cc
+++ b/testing/fuzzers/pdf_jpx_fuzzer.cc
@@ -16,6 +16,14 @@
 
 namespace {
 const uint32_t kMaxJPXFuzzSize = 100 * 1024 * 1024;  // 100 MB
+
+bool CheckImageSize(uint32_t width, uint32_t height, uint32_t components) {
+  static constexpr uint32_t kMemLimitBytes = 1024 * 1024 * 1024;  // 1 GB.
+  FX_SAFE_UINT32 mem = width;
+  mem *= height;
+  mem *= components;
+  return mem.IsValid() && mem.ValueOrDie() <= kMemLimitBytes;
+}
 }  // namespace
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
@@ -24,16 +32,21 @@
   if (!decoder)
     return 0;
 
+  // A call to StartDecode could be too expensive if image size is very big, so
+  // check size before calling StartDecode().
   uint32_t width;
   uint32_t height;
   uint32_t components;
   g_module.GetImageInfo(decoder.get(), &width, &height, &components);
+  if (!CheckImageSize(width, height, components))
+    return 0;
 
-  static constexpr uint32_t kMemLimit = 1024 * 1024 * 1024;  // 1 GB.
-  FX_SAFE_UINT32 mem = width;
-  mem *= height;
-  mem *= components;
-  if (!mem.IsValid() || mem.ValueOrDie() > kMemLimit)
+  if (!decoder->StartDecode())
+    return 0;
+
+  // StartDecode() could change image size, so check again.
+  g_module.GetImageInfo(decoder.get(), &width, &height, &components);
+  if (!CheckImageSize(width, height, components))
     return 0;
 
   FXDIB_Format format;