TurboJPEG: Update JPEG buf ptrs on comp/xform err
When using the in-memory destination manager, it is necessary to
explicitly call the destination manager's term_destination() method if
an error occurs. That method is called by jpeg_finish_compress() but
not by jpeg_abort_compress().
This fixes a potential double free() that could occur if tjCompress*()
or tjTransform() returned an error and the calling application tried to
clean up a JPEG buffer that was dynamically re-allocated by one of those
functions.
diff --git a/ChangeLog.md b/ChangeLog.md
index 12e730a..f47980c 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -45,6 +45,11 @@
generated when using the `tjLoadImage()` function to load a 16-bit binary PPM
file into an extended RGB image buffer.
+9. Fixed an issue whereby, if a JPEG buffer was automatically re-allocated by
+one of the TurboJPEG compression or transform functions and an error
+subsequently occurred during compression or transformation, the JPEG buffer
+pointer passed by the application was not updated when the function returned.
+
2.0.90 (2.1 beta1)
==================
diff --git a/fuzz/transform.cc b/fuzz/transform.cc
index 401018e..b8a7516 100644
--- a/fuzz/transform.cc
+++ b/fuzz/transform.cc
@@ -92,21 +92,37 @@
if (!dstBufs[2])
goto bailout;
- tjTransform(handle, data, size, NUMXFORMS, dstBufs, dstSizes, transforms,
- TJFLAG_LIMITSCANS | TJFLAG_NOREALLOC);
-
maxBufSize = tjBufSize(width, height, jpegSubsamp);
- /* Touch all of the output pixels in order to catch uninitialized reads
- when using MemorySanitizer. */
- for (t = 0; t < NUMXFORMS; t++) {
+ if (tjTransform(handle, data, size, NUMXFORMS, dstBufs, dstSizes, transforms,
+ TJFLAG_LIMITSCANS | TJFLAG_NOREALLOC) == 0) {
+ /* Touch all of the output pixels in order to catch uninitialized reads
+ when using MemorySanitizer. */
+ for (t = 0; t < NUMXFORMS; t++) {
+ int sum = 0;
+
+ for (i = 0; i < dstSizes[t]; i++)
+ sum += dstBufs[t][i];
+
+ /* Prevent the code above from being optimized out. This test should
+ never be true, but the compiler doesn't know that. */
+ if (sum > 255 * maxBufSize)
+ goto bailout;
+ }
+ }
+
+ transforms[0].options &= ~TJXOPT_COPYNONE;
+ free(dstBufs[0]);
+ dstBufs[0] = NULL;
+ dstSizes[0] = 0;
+
+ if (tjTransform(handle, data, size, 1, dstBufs, dstSizes, transforms,
+ TJFLAG_LIMITSCANS) == 0) {
int sum = 0;
- for (i = 0; i < dstSizes[t]; i++)
- sum += dstBufs[t][i];
+ for (i = 0; i < dstSizes[0]; i++)
+ sum += dstBufs[0][i];
- /* Prevent the code above from being optimized out. This test should never
- be true, but the compiler doesn't know that. */
if (sum > 255 * maxBufSize)
goto bailout;
}
diff --git a/turbojpeg.c b/turbojpeg.c
index 3500c78..793a3ee 100644
--- a/turbojpeg.c
+++ b/turbojpeg.c
@@ -720,7 +720,10 @@
jpeg_finish_compress(cinfo);
bailout:
- if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
+ if (cinfo->global_state > CSTATE_START) {
+ if (alloc) (*cinfo->dest->term_destination) (cinfo);
+ jpeg_abort_compress(cinfo);
+ }
free(row_pointer);
if (this->jerr.warning) retval = -1;
this->jerr.stopOnWarning = FALSE;
@@ -1088,7 +1091,10 @@
jpeg_finish_compress(cinfo);
bailout:
- if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
+ if (cinfo->global_state > CSTATE_START) {
+ if (alloc) (*cinfo->dest->term_destination) (cinfo);
+ jpeg_abort_compress(cinfo);
+ }
for (i = 0; i < MAX_COMPONENTS; i++) {
free(tmpbuf[i]);
free(inbuf[i]);
@@ -1886,7 +1892,7 @@
{
jpeg_transform_info *xinfo = NULL;
jvirt_barray_ptr *srccoefs, *dstcoefs;
- int retval = 0, i, jpegSubsamp, saveMarkers = 0;
+ int retval = 0, alloc = 1, i, jpegSubsamp, saveMarkers = 0;
struct my_progress_mgr progress;
GET_INSTANCE(handle);
@@ -1974,7 +1980,7 @@
srccoefs = jpeg_read_coefficients(dinfo);
for (i = 0; i < n; i++) {
- int w, h, alloc = 1;
+ int w, h;
if (!xinfo[i].crop) {
w = dinfo->image_width; h = dinfo->image_height;
@@ -2032,7 +2038,10 @@
jpeg_finish_decompress(dinfo);
bailout:
- if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
+ if (cinfo->global_state > CSTATE_START) {
+ if (alloc) (*cinfo->dest->term_destination) (cinfo);
+ jpeg_abort_compress(cinfo);
+ }
if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
free(xinfo);
if (this->jerr.warning) retval = -1;