mirror of
https://github.com/libjpeg-turbo/libjpeg-turbo.git
synced 2026-01-12 00:04:17 +08:00
TurboJPEG: Properly handle gigapixel images
Prevent several integer overflow issues and subsequent segfaults that occurred when attempting to compress or decompress gigapixel images with the TurboJPEG API: - Modify tjBufSize(), tjBufSizeYUV2(), and tjPlaneSizeYUV() to avoid integer overflow when computing the return values and to return an error if such an overflow is unavoidable. - Modify tjunittest to validate the above. - Modify tjCompress2(), tjEncodeYUVPlanes(), tjDecompress2(), and tjDecodeYUVPlanes() to avoid integer overflow when computing the row pointers in the 64-bit TurboJPEG C API. - Modify TJBench (both C and Java versions) to avoid overflowing the size argument to malloc()/new and to fail gracefully if such an overflow is unavoidable. In general, this allows gigapixel images to be accommodated by the 64-bit TurboJPEG C API when using automatic JPEG buffer (re)allocation. Such images cannot currently be accommodated without automatic JPEG buffer (re)allocation, due to the fact that tjAlloc() accepts a 32-bit integer argument (oops.) Such images cannot be accommodated in the TurboJPEG Java API due to the fact that Java always uses a signed 32-bit integer as an array index. Fixes #361
This commit is contained in:
@@ -21,6 +21,10 @@ result will be similar regardless of whether a 4:2:2 JPEG image is rotated or
|
||||
transposed prior to decompression (in the frequency domain) or after
|
||||
decompression (in the spatial domain.)
|
||||
|
||||
4. Fixed an integer overflow and subsequent segfault that occurred when
|
||||
attempting to compress or decompress images with more than 1 billion pixels
|
||||
using the TurboJPEG API.
|
||||
|
||||
|
||||
2.0.2
|
||||
=====
|
||||
|
||||
@@ -121,6 +121,8 @@ final class TJBench {
|
||||
int rindex = TJ.getRedOffset(pixelFormat);
|
||||
int gindex = TJ.getGreenOffset(pixelFormat);
|
||||
int bindex = TJ.getBlueOffset(pixelFormat);
|
||||
if ((long)w[0] * (long)h[0] * (long)ps > (long)Integer.MAX_VALUE)
|
||||
throw new Exception("Image is too large");
|
||||
byte[] dstBuf = new byte[w[0] * h[0] * ps];
|
||||
int pixels = w[0] * h[0], dstPtr = 0, rgbPtr = 0;
|
||||
|
||||
@@ -175,8 +177,11 @@ final class TJBench {
|
||||
|
||||
tjd = new TJDecompressor();
|
||||
|
||||
if (dstBuf == null)
|
||||
if (dstBuf == null) {
|
||||
if ((long)pitch * (long)scaledh > (long)Integer.MAX_VALUE)
|
||||
throw new Exception("Image is too large");
|
||||
dstBuf = new byte[pitch * scaledh];
|
||||
}
|
||||
|
||||
/* Set the destination buffer to gray so we know whether the decompressor
|
||||
attempted to write to it */
|
||||
@@ -331,6 +336,8 @@ final class TJBench {
|
||||
String pfStr = PIXFORMATSTR[pf];
|
||||
YUVImage yuvImage = null;
|
||||
|
||||
if ((long)pitch * (long)h > (long)Integer.MAX_VALUE)
|
||||
throw new Exception("Image is too large");
|
||||
tmpBuf = new byte[pitch * h];
|
||||
|
||||
if (quiet == 0)
|
||||
@@ -491,6 +498,8 @@ final class TJBench {
|
||||
int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp;
|
||||
|
||||
FileInputStream fis = new FileInputStream(fileName);
|
||||
if (fis.getChannel().size() > (long)Integer.MAX_VALUE)
|
||||
throw new Exception("Image is too large");
|
||||
int srcSize = (int)fis.getChannel().size();
|
||||
srcBuf = new byte[srcSize];
|
||||
fis.read(srcBuf, 0, srcSize);
|
||||
|
||||
43
tjbench.c
43
tjbench.c
@@ -32,6 +32,7 @@
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <cdjpeg.h>
|
||||
#include "./tjutil.h"
|
||||
#include "./turbojpeg.h"
|
||||
@@ -161,7 +162,10 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf,
|
||||
THROW_TJ("executing tjInitDecompress()");
|
||||
|
||||
if (dstBuf == NULL) {
|
||||
if ((dstBuf = (unsigned char *)malloc(pitch * scaledh)) == NULL)
|
||||
if ((unsigned long long)pitch * (unsigned long long)scaledh >
|
||||
(unsigned long long)((size_t)-1))
|
||||
THROW("allocating destination buffer", "Image is too large");
|
||||
if ((dstBuf = (unsigned char *)malloc((size_t)pitch * scaledh)) == NULL)
|
||||
THROW_UNIX("allocating destination buffer");
|
||||
dstBufAlloc = 1;
|
||||
}
|
||||
@@ -172,8 +176,10 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf,
|
||||
if (doYUV) {
|
||||
int width = doTile ? tilew : scaledw;
|
||||
int height = doTile ? tileh : scaledh;
|
||||
int yuvSize = tjBufSizeYUV2(width, yuvPad, height, subsamp);
|
||||
unsigned long yuvSize = tjBufSizeYUV2(width, yuvPad, height, subsamp);
|
||||
|
||||
if (yuvSize == (unsigned long)-1)
|
||||
THROW_TJ("allocating YUV buffer");
|
||||
if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
|
||||
THROW_UNIX("allocating YUV buffer");
|
||||
memset(yuvBuf, 127, yuvSize);
|
||||
@@ -267,13 +273,13 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf,
|
||||
if (srcBuf && sf.num == 1 && sf.denom == 1) {
|
||||
if (!quiet) printf("Compression error written to %s.\n", tempStr);
|
||||
if (subsamp == TJ_GRAYSCALE) {
|
||||
int index, index2;
|
||||
unsigned long index, index2;
|
||||
|
||||
for (row = 0, index = 0; row < h; row++, index += pitch) {
|
||||
for (col = 0, index2 = index; col < w; col++, index2 += ps) {
|
||||
int rindex = index2 + tjRedOffset[pf];
|
||||
int gindex = index2 + tjGreenOffset[pf];
|
||||
int bindex = index2 + tjBlueOffset[pf];
|
||||
unsigned long rindex = index2 + tjRedOffset[pf];
|
||||
unsigned long gindex = index2 + tjGreenOffset[pf];
|
||||
unsigned long bindex = index2 + tjBlueOffset[pf];
|
||||
int y = (int)((double)srcBuf[rindex] * 0.299 +
|
||||
(double)srcBuf[gindex] * 0.587 +
|
||||
(double)srcBuf[bindex] * 0.114 + 0.5);
|
||||
@@ -314,13 +320,16 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp,
|
||||
*srcPtr2;
|
||||
double start, elapsed, elapsedEncode;
|
||||
int totalJpegSize = 0, row, col, i, tilew = w, tileh = h, retval = 0;
|
||||
int iter, yuvSize = 0;
|
||||
unsigned long *jpegSize = NULL;
|
||||
int iter;
|
||||
unsigned long *jpegSize = NULL, yuvSize = 0;
|
||||
int ps = tjPixelSize[pf];
|
||||
int ntilesw = 1, ntilesh = 1, pitch = w * ps;
|
||||
const char *pfStr = pixFormatStr[pf];
|
||||
|
||||
if ((tmpBuf = (unsigned char *)malloc(pitch * h)) == NULL)
|
||||
if ((unsigned long long)pitch * (unsigned long long)h >
|
||||
(unsigned long long)((size_t)-1))
|
||||
THROW("allocating temporary image buffer", "Image is too large");
|
||||
if ((tmpBuf = (unsigned char *)malloc((size_t)pitch * h)) == NULL)
|
||||
THROW_UNIX("allocating temporary image buffer");
|
||||
|
||||
if (!quiet)
|
||||
@@ -346,6 +355,8 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp,
|
||||
|
||||
if ((flags & TJFLAG_NOREALLOC) != 0)
|
||||
for (i = 0; i < ntilesw * ntilesh; i++) {
|
||||
if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX)
|
||||
THROW("getting buffer size", "Image is too large");
|
||||
if ((jpegBuf[i] = (unsigned char *)
|
||||
tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL)
|
||||
THROW_UNIX("allocating JPEG tiles");
|
||||
@@ -363,6 +374,8 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp,
|
||||
|
||||
if (doYUV) {
|
||||
yuvSize = tjBufSizeYUV2(tilew, yuvPad, tileh, subsamp);
|
||||
if (yuvSize == (unsigned long)-1)
|
||||
THROW_TJ("allocating YUV buffer");
|
||||
if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
|
||||
THROW_UNIX("allocating YUV buffer");
|
||||
memset(yuvBuf, 127, yuvSize);
|
||||
@@ -437,7 +450,7 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp,
|
||||
if (doYUV) {
|
||||
printf("Encode YUV --> Frame rate: %f fps\n",
|
||||
(double)iter / elapsedEncode);
|
||||
printf(" Output image size: %d bytes\n", yuvSize);
|
||||
printf(" Output image size: %lu bytes\n", yuvSize);
|
||||
printf(" Compression ratio: %f:1\n",
|
||||
(double)(w * h * ps) / (double)yuvSize);
|
||||
printf(" Throughput: %f Megapixels/sec\n",
|
||||
@@ -578,8 +591,11 @@ static int decompTest(char *fileName)
|
||||
THROW_UNIX("allocating JPEG size array");
|
||||
memset(jpegSize, 0, sizeof(unsigned long) * ntilesw * ntilesh);
|
||||
|
||||
if ((flags & TJFLAG_NOREALLOC) != 0 || !doTile)
|
||||
if ((flags & TJFLAG_NOREALLOC) != 0 &&
|
||||
(doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter))
|
||||
for (i = 0; i < ntilesw * ntilesh; i++) {
|
||||
if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX)
|
||||
THROW("getting buffer size", "Image is too large");
|
||||
if ((jpegBuf[i] = (unsigned char *)
|
||||
tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL)
|
||||
THROW_UNIX("allocating JPEG tiles");
|
||||
@@ -685,7 +701,7 @@ static int decompTest(char *fileName)
|
||||
}
|
||||
} else {
|
||||
if (quiet == 1) printf("N/A N/A ");
|
||||
tjFree(jpegBuf[0]);
|
||||
if(jpegBuf[0]) tjFree(jpegBuf[0]);
|
||||
jpegBuf[0] = NULL;
|
||||
decompsrc = 1;
|
||||
}
|
||||
@@ -700,7 +716,8 @@ static int decompTest(char *fileName)
|
||||
} else if (quiet == 1) printf("N/A\n");
|
||||
|
||||
for (i = 0; i < ntilesw * ntilesh; i++) {
|
||||
tjFree(jpegBuf[i]); jpegBuf[i] = NULL;
|
||||
if(jpegBuf[i]) tjFree(jpegBuf[i]);
|
||||
jpegBuf[i] = NULL;
|
||||
}
|
||||
free(jpegBuf); jpegBuf = NULL;
|
||||
if (jpegSize) { free(jpegSize); jpegSize = NULL; }
|
||||
|
||||
37
tjunittest.c
37
tjunittest.c
@@ -554,6 +554,42 @@ bailout:
|
||||
}
|
||||
|
||||
|
||||
#if SIZEOF_SIZE_T == 8
|
||||
#define CHECKSIZE(function) { \
|
||||
if ((unsigned long long)size < (unsigned long long)0xFFFFFFFF) \
|
||||
THROW(#function " overflow"); \
|
||||
}
|
||||
#else
|
||||
#define CHECKSIZE(function) { \
|
||||
if (size != (unsigned long)(-1) || \
|
||||
!strcmp(tjGetErrorStr2(NULL), "No error")) \
|
||||
THROW(#function " overflow"); \
|
||||
}
|
||||
#endif
|
||||
|
||||
static void overflowTest(void)
|
||||
{
|
||||
/* Ensure that the various buffer size functions don't overflow */
|
||||
unsigned long size;
|
||||
|
||||
size = tjBufSize(26755, 26755, TJSAMP_444);
|
||||
CHECKSIZE(tjBufSize());
|
||||
size = TJBUFSIZE(26755, 26755);
|
||||
CHECKSIZE(TJBUFSIZE());
|
||||
size = tjBufSizeYUV2(37838, 1, 37838, TJSAMP_444);
|
||||
CHECKSIZE(tjBufSizeYUV2());
|
||||
size = TJBUFSIZEYUV(37838, 37838, TJSAMP_444);
|
||||
CHECKSIZE(TJBUFSIZEYUV());
|
||||
size = tjBufSizeYUV(37838, 37838, TJSAMP_444);
|
||||
CHECKSIZE(tjBufSizeYUV());
|
||||
size = tjPlaneSizeYUV(0, 65536, 0, 65536, TJSAMP_444);
|
||||
CHECKSIZE(tjPlaneSizeYUV());
|
||||
|
||||
bailout:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void bufSizeTest(void)
|
||||
{
|
||||
int w, h, i, subsamp;
|
||||
@@ -865,6 +901,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
if (alloc) printf("Testing automatic buffer allocation\n");
|
||||
if (doYUV) num4bf = 4;
|
||||
overflowTest();
|
||||
doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test");
|
||||
doTest(39, 41, _4byteFormats, num4bf, TJSAMP_444, "test");
|
||||
doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test");
|
||||
|
||||
49
turbojpeg.c
49
turbojpeg.c
@@ -491,7 +491,7 @@ DLLEXPORT tjhandle tjInitCompress(void)
|
||||
|
||||
DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
|
||||
{
|
||||
unsigned long retval = 0;
|
||||
unsigned long long retval = 0;
|
||||
int mcuw, mcuh, chromasf;
|
||||
|
||||
if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
|
||||
@@ -503,15 +503,17 @@ DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
|
||||
mcuw = tjMCUWidth[jpegSubsamp];
|
||||
mcuh = tjMCUHeight[jpegSubsamp];
|
||||
chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
|
||||
retval = PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
|
||||
retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
|
||||
if (retval > (unsigned long long)((unsigned long)-1))
|
||||
THROWG("tjBufSize(): Image is too large");
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
return (unsigned long)retval;
|
||||
}
|
||||
|
||||
DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
|
||||
{
|
||||
unsigned long retval = 0;
|
||||
unsigned long long retval = 0;
|
||||
|
||||
if (width < 1 || height < 1)
|
||||
THROWG("TJBUFSIZE(): Invalid argument");
|
||||
@@ -519,17 +521,20 @@ DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
|
||||
/* This allows for rare corner cases in which a JPEG image can actually be
|
||||
larger than the uncompressed input (we wouldn't mention it if it hadn't
|
||||
happened before.) */
|
||||
retval = PAD(width, 16) * PAD(height, 16) * 6 + 2048;
|
||||
retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
|
||||
if (retval > (unsigned long long)((unsigned long)-1))
|
||||
THROWG("TJBUFSIZE(): Image is too large");
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
return (unsigned long)retval;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
|
||||
int subsamp)
|
||||
{
|
||||
int retval = 0, nc, i;
|
||||
unsigned long long retval = 0;
|
||||
int nc, i;
|
||||
|
||||
if (subsamp < 0 || subsamp >= NUMSUBOPT)
|
||||
THROWG("tjBufSizeYUV2(): Invalid argument");
|
||||
@@ -541,11 +546,13 @@ DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
|
||||
int ph = tjPlaneHeight(i, height, subsamp);
|
||||
|
||||
if (pw < 0 || ph < 0) return -1;
|
||||
else retval += stride * ph;
|
||||
else retval += (unsigned long long)stride * ph;
|
||||
}
|
||||
if (retval > (unsigned long long)((unsigned long)-1))
|
||||
THROWG("tjBufSizeYUV2(): Image is too large");
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
return (unsigned long)retval;
|
||||
}
|
||||
|
||||
DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
|
||||
@@ -604,7 +611,7 @@ bailout:
|
||||
DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
|
||||
int height, int subsamp)
|
||||
{
|
||||
unsigned long retval = 0;
|
||||
unsigned long long retval = 0;
|
||||
int pw, ph;
|
||||
|
||||
if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
|
||||
@@ -617,10 +624,12 @@ DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
|
||||
if (stride == 0) stride = pw;
|
||||
else stride = abs(stride);
|
||||
|
||||
retval = stride * (ph - 1) + pw;
|
||||
retval = (unsigned long long)stride * (ph - 1) + pw;
|
||||
if (retval > (unsigned long long)((unsigned long)-1))
|
||||
THROWG("tjPlaneSizeYUV(): Image is too large");
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
return (unsigned long)retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -672,9 +681,9 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
|
||||
jpeg_start_compress(cinfo, TRUE);
|
||||
for (i = 0; i < height; i++) {
|
||||
if (flags & TJFLAG_BOTTOMUP)
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
|
||||
else
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
|
||||
}
|
||||
while (cinfo->next_scanline < cinfo->image_height)
|
||||
jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
|
||||
@@ -783,9 +792,9 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
|
||||
THROW("tjEncodeYUVPlanes(): Memory allocation failure");
|
||||
for (i = 0; i < height; i++) {
|
||||
if (flags & TJFLAG_BOTTOMUP)
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
|
||||
else
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
|
||||
row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
|
||||
}
|
||||
if (height < ph0)
|
||||
for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
|
||||
@@ -1293,9 +1302,9 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
|
||||
}
|
||||
for (i = 0; i < (int)dinfo->output_height; i++) {
|
||||
if (flags & TJFLAG_BOTTOMUP)
|
||||
row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * pitch];
|
||||
row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
|
||||
else
|
||||
row_pointer[i] = &dstBuf[i * pitch];
|
||||
row_pointer[i] = &dstBuf[i * (size_t)pitch];
|
||||
}
|
||||
while (dinfo->output_scanline < dinfo->output_height)
|
||||
jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
|
||||
@@ -1450,9 +1459,9 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
|
||||
THROW("tjDecodeYUVPlanes(): Memory allocation failure");
|
||||
for (i = 0; i < height; i++) {
|
||||
if (flags & TJFLAG_BOTTOMUP)
|
||||
row_pointer[i] = &dstBuf[(height - i - 1) * pitch];
|
||||
row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
|
||||
else
|
||||
row_pointer[i] = &dstBuf[i * pitch];
|
||||
row_pointer[i] = &dstBuf[i * (size_t)pitch];
|
||||
}
|
||||
if (height < ph0)
|
||||
for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
|
||||
|
||||
Reference in New Issue
Block a user