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
=====
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;
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 */
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)
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);
#include <ctype.h>
#include <math.h>
#include <errno.h>
+#include <limits.h>
#include <cdjpeg.h>
#include "./tjutil.h"
#include "./turbojpeg.h"
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;
}
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);
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);
*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)
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");
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);
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",
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");
}
} else {
if (quiet == 1) printf("N/A N/A ");
- tjFree(jpegBuf[0]);
+ if(jpegBuf[0]) tjFree(jpegBuf[0]);
jpegBuf[0] = NULL;
decompsrc = 1;
}
} 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; }
}
+#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;
}
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");
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)
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");
/* 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");
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)
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)
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;
}
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],
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];
}
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],
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];