Fix leak inside sycc420_to_rgb().

In the process, introduce ScopedOpjImageData to help manage the memory
allocated by opj_image_data_alloc().

BUG=chromium:934638

Change-Id: I796fd9ecc504b7f8d88d3fe40129cb2eabc5adfc
Reviewed-on: https://pdfium-review.googlesource.com/c/51070
Reviewed-by: Nicolás Peña Moreno <npm@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcodec/codec/ccodec_jpxmodule.cpp b/core/fxcodec/codec/ccodec_jpxmodule.cpp
index fbfc077..0e82b7f 100644
--- a/core/fxcodec/codec/ccodec_jpxmodule.cpp
+++ b/core/fxcodec/codec/ccodec_jpxmodule.cpp
@@ -15,6 +15,7 @@
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcodec/codec/cjpx_decoder.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/optional.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
@@ -25,6 +26,19 @@
 
 namespace {
 
+// Used with std::unique_ptr to call opj_image_data_free on raw memory.
+struct OpjImageDataDeleter {
+  inline void operator()(void* ptr) const { opj_image_data_free(ptr); }
+};
+
+using ScopedOpjImageData = std::unique_ptr<int, OpjImageDataDeleter>;
+
+struct OpjImageRgbData {
+  ScopedOpjImageData r;
+  ScopedOpjImageData g;
+  ScopedOpjImageData b;
+};
+
 void fx_ignore_callback(const char* msg, void* client_data) {}
 
 opj_stream_t* fx_opj_stream_create_memory_stream(DecodeData* data) {
@@ -44,28 +58,21 @@
   return stream;
 }
 
-bool alloc_rgb(int** out_r, int** out_g, int** out_b, size_t size) {
-  int* r = static_cast<int*>(opj_image_data_alloc(size));
-  if (!r)
-    return false;
+Optional<OpjImageRgbData> alloc_rgb(size_t size) {
+  OpjImageRgbData data;
+  data.r.reset(static_cast<int*>(opj_image_data_alloc(size)));
+  if (!data.r)
+    return {};
 
-  int* g = static_cast<int*>(opj_image_data_alloc(size));
-  if (!g) {
-    opj_image_data_free(r);
-    return false;
-  }
+  data.g.reset(static_cast<int*>(opj_image_data_alloc(size)));
+  if (!data.g)
+    return {};
 
-  int* b = static_cast<int*>(opj_image_data_alloc(size));
-  if (!b) {
-    opj_image_data_free(r);
-    opj_image_data_free(g);
-    return false;
-  }
+  data.b.reset(static_cast<int*>(opj_image_data_alloc(size)));
+  if (!data.b)
+    return {};
 
-  *out_r = r;
-  *out_g = g;
-  *out_b = b;
-  return true;
+  return data;
 }
 
 void sycc_to_rgb(int offset,
@@ -106,25 +113,23 @@
   if (!y || !cb || !cr)
     return;
 
-  int* r;
-  int* g;
-  int* b;
-  if (!alloc_rgb(&r, &g, &b, max_size.ValueOrDie()))
+  Optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
+  if (!data.has_value())
     return;
 
-  int* d0 = r;
-  int* d1 = g;
-  int* d2 = b;
+  int* r = data.value().r.get();
+  int* g = data.value().g.get();
+  int* b = data.value().b.get();
   max_size /= sizeof(int);
-  for (size_t i = 0; i < max_size.ValueOrDie(); ++i) {
+  for (size_t i = 0; i < max_size.ValueOrDie(); ++i)
     sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
-  }
+
   opj_image_data_free(img->comps[0].data);
   opj_image_data_free(img->comps[1].data);
   opj_image_data_free(img->comps[2].data);
-  img->comps[0].data = d0;
-  img->comps[1].data = d1;
-  img->comps[2].data = d2;
+  img->comps[0].data = data.value().r.release();
+  img->comps[1].data = data.value().g.release();
+  img->comps[2].data = data.value().b.release();
 }
 
 bool sycc420_422_size_is_valid(opj_image_t* img) {
@@ -168,15 +173,13 @@
   if (!y || !cb || !cr)
     return;
 
-  int* r;
-  int* g;
-  int* b;
-  if (!alloc_rgb(&r, &g, &b, max_size.ValueOrDie()))
+  Optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
+  if (!data.has_value())
     return;
 
-  int* d0 = r;
-  int* d1 = g;
-  int* d2 = b;
+  int* r = data.value().r.get();
+  int* g = data.value().g.get();
+  int* b = data.value().b.get();
   for (uint32_t i = 0; i < maxh; ++i) {
     OPJ_UINT32 j;
     for (j = 0; j < (maxw & ~static_cast<OPJ_UINT32>(1)); j += 2) {
@@ -187,12 +190,13 @@
       sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
     }
   }
+
   opj_image_data_free(img->comps[0].data);
   opj_image_data_free(img->comps[1].data);
   opj_image_data_free(img->comps[2].data);
-  img->comps[0].data = d0;
-  img->comps[1].data = d1;
-  img->comps[2].data = d2;
+  img->comps[0].data = data.value().r.release();
+  img->comps[1].data = data.value().g.release();
+  img->comps[2].data = data.value().b.release();
   img->comps[1].w = maxw;
   img->comps[1].h = maxh;
   img->comps[2].w = maxw;
@@ -336,27 +340,25 @@
   OPJ_UINT32 crw = img->comps[2].w;
   bool extw = sycc420_must_extend_cbcr(yw, cbw);
   bool exth = sycc420_must_extend_cbcr(yh, cbh);
-  FX_SAFE_UINT32 safeSize = yw;
-  safeSize *= yh;
-  safeSize *= sizeof(int);
-  if (!safeSize.IsValid())
+  FX_SAFE_UINT32 safe_size = yw;
+  safe_size *= yh;
+  safe_size *= sizeof(int);
+  if (!safe_size.IsValid())
     return;
 
-  int* r;
-  int* g;
-  int* b;
-  if (!alloc_rgb(&r, &g, &b, safeSize.ValueOrDie()))
-    return;
-
-  int* d0 = r;
-  int* d1 = g;
-  int* d2 = b;
   const int* y = img->comps[0].data;
   const int* cb = img->comps[1].data;
   const int* cr = img->comps[2].data;
   if (!y || !cb || !cr)
     return;
 
+  Optional<OpjImageRgbData> data = alloc_rgb(safe_size.ValueOrDie());
+  if (!data.has_value())
+    return;
+
+  int* r = data.value().r.get();
+  int* g = data.value().g.get();
+  int* b = data.value().b.get();
   const int* ny = nullptr;
   int* nr = nullptr;
   int* ng = nullptr;
@@ -446,9 +448,9 @@
   opj_image_data_free(img->comps[0].data);
   opj_image_data_free(img->comps[1].data);
   opj_image_data_free(img->comps[2].data);
-  img->comps[0].data = d0;
-  img->comps[1].data = d1;
-  img->comps[2].data = d2;
+  img->comps[0].data = data.value().r.release();
+  img->comps[1].data = data.value().g.release();
+  img->comps[2].data = data.value().b.release();
   img->comps[1].w = yw;
   img->comps[1].h = yh;
   img->comps[2].w = yw;