This is a similar issue to the one fixed by
e500e4c154. On ILP32 systems, loading a
16-bit-per-component PNG with cinfo->data_precision==16 would cause the
intermediate rescale array calculations to overflow a signed long.
Otherwise the TurboJPEG 3.2 API would allow callers compiled against
TurboJPEG 3.1 and prior to set TJPARAM_SUBSAMP to a value that, from the
caller's point of view, is undefined. tj3DecompressHeader() would also
potentially set TJPARAM_SUBSAMP to a value that would overflow the
caller's static tjMCUWidth[] and tjMCUHeight[] arrays.
The trick here is to redefine tj3Init() in the TurboJPEG header as a
macro that wraps a new undocumented function, tj3InitVersion(), and
passes it an API version constant (TURBOJPEG_VERSION_NUMBER = 3002000)
that is also defined in the header. (This is a similar trick to the one
that the libjpeg v6a+ API uses for jpeg_create_*compress().) The old
tj3Init() function is still exposed in the library, and it now calls
tj3InitVersion(..., 3001000) internally. Based on the API version, the
API then sets and enforces an internal limit on the number of
chrominance subsampling options (TJ_NUMSAMP if the caller was compiled
against TurboJPEG 3.2+ or 7 otherwise.)
Thus, if an application is compiled against TurboJPEG 3.1-, it will
still call the old tj3Init() function, which sets the API version to
3.1 and doesn't expose TJSAMP_410 or TJSAMP_24. If an application is
compiled against TurboJPEG 3.2+, then it will call the new
tj3InitVersion() function via the tj3Init() macro, which sets the API
version to 3.2 and exposes TJSAMP_410 and TJSAMP_24.
Ramifications:
- Applications compiled against TurboJPEG 3.2+ will not be able to use
the TurboJPEG 3.1- API library at run time, because the
tj3InitVersion() function will not be available.
- Applications compiled against TurboJPEG 3.1- will work the same
regardless of whether they use the TurboJPEG 3.1- or 3.2+ API library
at run time.
- Applications that weren't specifically developed for TurboJPEG 3.2+
will not know about TJSAMP_410 or TJSAMP_24. Thus, when decompressing
or transforming without transposition, such applications will gain the
ability to obtain the iMCU width/height of a 4:1:0 or 2:4 JPEG image,
which is useful when adjusting the cropping region. However, they
will not gain any other abilities.
The PNG spec says "truecolor."
Also use "uncompressed RGB" rather than "uncompressed true color" when
referring to TARGA images, to conform to that spec.
54bd58cc30 (PNG image I/O) exposed an
issue whereby, if an application calls jpeg_read_header() and
jpeg_abort_decompress() (as tj3DecompressHeader() does), then the
total_start field will be 0 in jpeg_abort(). That meant that the
total_elapsed field computed in jpeg_abort() was near 0. This commit
modifies jpeg_abort() so that it avoids incrementing total_elapsed
unless total_start is non-zero, thus ensuring that total_elapsed
reflects the time between
jinit_c_master_control()/jinit_master_decompress() and jpeg_abort().
If a malformed lossless JPEG image has close to, but not more than, 100
scans, then all iterations of the decompress fuzzers will run. This
never takes more than 5 seconds on my machine, but apparently OSS-Fuzz
sometimes uses slower hardware (particularly for the ASan/i386 tests)
that sometimes exceeds the 25-second timeout when fuzz testing such a
malformed image. As with the pixel limit, the goal of the scan limit is
to maximize the test throughput in order to maximize coverage. 100
scans was an arbitrary choice, and reducing it to 50 does not affect
default coverage (and may actually improve asymptotic coverage.)
fc324109d5 checked for unsigned int
overflow, but jsize is always a signed int, even when using a 64-bit
JVM. (This reflects the fact that array sizes in Java are always signed
ints.) Thus, array sizes should be checked against INT_MAX, not
UINT_MAX.
In almost all cases, this is a belt-and-suspenders measure. With the
exception of TJCompressor.loadSourceImage(), the array size checks in
question are checking a Java array, which can never have more than
INT_MAX elements. Thus, even if the previous unsigned int overflow
checks failed to catch signed int overflow, the error would have been
caught in a different place. With TJCompressor.loadSourceImage(), it
was possible to load a packed-pixel image large enough that arraySize
overflowed a signed int and caused the NewByteArray() or NewShortArray()
JNI methods to crash the JVM. However, this would have required a
multi-gigabyte packed-pixel image.
This commit also improves the array size checks in
TJCompressor.loadSourceImage() and TJDecompressor.saveImage() so that
they take into account row alignment/pitch.
Fixes#877
- This eliminates the need for any backward compatibility hacks in the
build system.
- CMake 3.4- is no longer supported by CMake 4+.
- The only reason why we continued to support CMake 2.8.12 was that it
was pre-installed on RHEL/CentOS 7, which was used by our official
Docker image. That was already a weak argument, since CMake 3.x
binaries worked on systems with glibc 2.12 and later (RHEL 6+ and
equivalents or Ubuntu 12.04+.)
Our official Docker image now uses Rocky Linux 8 in order to support
cross-compilation of the Arm Neon SIMD extensions with GCC 12, which
was necessary in order to eliminate the GNU assembler (GAS) fallback
code.
- CMake 3.15+ is pre-installed on RHEL 8+ and equivalents and Ubuntu
20.04+, and on other platforms, it is no more difficult to install
than CMake 2.8.12. (In fact, newer CMake versions are easier to
install on Mac systems. CMake 3.18- did not provide Apple Silicon
binaries, and CMake 3.15- was deployed as a pkg installer rather than
a standalone app bundle.)
... when using ERREXITS(). This warning was innocuous, because
ERREXITS() immediately NULL-terminates (cinfo)->err->msg_parm.s[] after
copying to it with strncpy(). ERREXITS() was unused until PNG image I/O
support was added, so the warning didn't occur with prior versions of
libjpeg-turbo.
In the decompressor, this does nothing except fix a -Wswitch-default
warning with Clang, because cinfo.Ss (the predictor selection value) is
already range-checked in start_pass_lossless().
In the compressor, this fixes an oversight whereby a calling application
could manually set cinfo.Ss to an invalid value after calling
jpeg_enable_lossless() and before calling jpeg_start_compress(), thus
causing an incorrect (all white) lossless JPEG image to be silently
generated.
It is our convention to use the generic CMake project name rather than
hard-coding "libjpeg-turbo" in error messages and option descriptions,
to facilitate downstream integration.
This is mostly a code cleanliness thing. It doesn't fix any known
issues. (Hypothetically, a caller might call tj3GetErrorStr() in a
different thread from the thread in which the libjpeg error was thrown,
but that is not a supported use case.)
Apply the same fixes from 6709e4a0cf,
dd830b3ffe, and
6c9fb7064a to the PPM reader in
read_color_map*(). This prevents two issues:
1. An invalid PPM file could be created with dimensions larger than
UINT_MAX, which would cause 32-bit integer overflow and wraparound.
The worst case for such an issue would be that djpeg produces an
incorrect output file rather than throwing an error about the invalid
PPM file. (Note that UBSan did not catch the overflow.)
2. A valid PPM file with excessively large dimensions and fewer than N
unique colors (where N = MAXJ*SAMPLE + 1) could cause djpeg to spin
for an unreasonable amount of time as it continued reading until it
processed w * h pixels or reached the end of the file.
Hypothetically, a file with dimensions UINT_MAX * UINT_MAX could be
created. In reality, however, no one is going to create a 16-exabyte
file, so the worst case is probably a file with tens or hundreds of
gigabytes.
The issues were confined to the djpeg test program and did not represent
security vulnerabilities. At the end of the day, if you ask djpeg to
read a colormap from a 16-exabyte PPM file, then that's on you. This
commit merely user-proofs djpeg against such mistakes.
Closes#876
- For readability, wrap setjmp() calls with a macro.
- In tj3Destroy(), continue attempting to destroy the instance if
jpeg_destroy_compress() or jpeg_destroy_decompress() throws a libjpeg
error. (That can never happen with the malloc()/free() memory manager
used by libjpeg-turbo, but since tj3Destroy() handles libjpeg errors,
it might as well handle them correctly.)
- Reset the marker processor if jpeg_read_header() throws a libjpeg
error in tj3DecodeYUVPlanes8(). In general, it is a bad idea to
continue using a TurboJPEG instance after a fatal error is thrown.
However, in this case jpeg_read_header() isn't actually consuming
input. It is invoked with a dummy marker processor so that it can be
used solely to validate and advance the API state. (For example,
jpeg_read_header() could throw JERR_IMAGE_TOO_BIG if the width or
height passed to tj3DecodeYUVPlanes8() is greater than 65500. That is
essentially a recoverable argument error.)
Failing to reset the marker processor prevented subsequent invocations
of tj3DecompressHeader() from processing a JPEG image. To put it
another way, decode and decompress operations are supposed to be
independent, even if they share the same API instance. Thus, a
failure in one operation should (ideally) not cause the other to fail.
Closes#875
NOTE: These fixes are mainly intended to facilitate downstream
integration. libjpeg-turbo is not officially supported with any of
these macros undefined. (That is, it is not guaranteed to work. It is
expected that downstream integrators diagnose and report any issues that
may occur. Those issues will not be fixed proactively.)
- bmpsizetest.c: Fix build if C_LOSSLESS_SUPPORTED or
D_LOSSLESS_SUPPORTED is undefined.
- cjpeg.c, jpegtran.c: Fix compiler warnings if
C_MULTISCAN_FILES_SUPPORTED or C_PROGRESSIVE_SUPPORTED is undefined.
- jcdctmgr.c, jcinit.c, jddctmgr.c, jdmaster.c, simdcoverage.c: Fix
build if all DCT_*_SUPPORTED macros are undefined. (This allows for
building libjpeg-turbo with only lossless JPEG support.)
- jclhuff.c, jcmaster.c: Fix build if ENTROPY_OPT_SUPPORTED is
undefined.
- jcmaster.c: Fix compiler warning if C_PROGRESSIVE_SUPPORTED is
undefined.
- jcsample.c: Fix compiler warnings if INPUT_SMOOTHING_SUPPORTED is
undefined.
- jdapistd.c: Fix build if UPSAMPLE_MERGING_SUPPORTED is undefined.
- jmorecfg.h:
* Clarify that C_PROGRESSIVE_SUPPORTED requires
C_MULTISCAN_FILES_SUPPORTED and ENTROPY_OPT_SUPPORTED. (The
progressive Huffman encoder won't even build without the latter.)
* Clarify that D_PROGRESSIVE_SUPPORTED and D_LOSSLESS_SUPPORTED
require D_MULTISCAN_FILES_SUPPORTED. (The progressive huffman
decoder and difference controller won't even build without it.)
* Clarify that IDCT_SCALING_SUPPORTED requires DCT_ISLOW_SUPPORTED.
* Remove vestigial UPSAMPLE_SCALING_SUPPORTED macro. (I guess this
was intended for a feature that was never implemented.)
- jpegtran.c: Fix compiler warning is TRANSFORMS_SUPPORTED is 0.
- turbojpeg.c:
* Do not advertise any scaling factors other than {1, 1} if
IDCT_SCALING_SUPPORTED is undefined. Otherwise, calling
applications may erroneously expect a scaled image when an unscaled
image will be provided, which may cause them to provide too small of
a buffer.
* Fix build if SAVE_MARKERS_SUPPORTED is undefined.
* Fix build if TRANSFORMS_SUPPORTED is 0.
- turbojpeg-mp.c: Fix compiler warning if C_LOSSLESS_SUPPORTED is
undefined.
Normally the packed-pixel image writers are guarded by the JPEG
decompressor's dimension checks, but that isn't the case when the
writers are invoked by tj3SaveImage*(). This commit also adds a unit
test to verify that both the readers and writers limit image dimensions
appropriately.
This is a similar issue to the one fixed by
fa5259da1d. On ILP32 systems, writing a
24-bit BMP would cause the computation of bfSize to overflow a signed
long if width * height > INT_MAX / 3.
This wasn't an issue with other image readers, because their
finish_input() methods are no-ops. With the PNG reader, however, we
need to make sure that the finish_input() method is called if
tj3LoadImage*() or the cjpeg fuzzer throws an error. We also need to
advance the setjmp() pointer after calling jinit_read_*(), to ensure
that the source manager pointer remains initialized on the stack.
It doesn't make sense to allow packed-pixel images with dimensions
larger then JPEG_MAX_DIMENSION to be loaded, because the compressor will
eventually throw an error when attempting to compress those images.
It's better to catch that error in the image readers, so we don't have
to guard against 32-bit integer overflow.
This is a similar issue to the one fixed by the previous commit. On
ILP32 systems, loading a 16-bit-per-component PPM with
cinfo->data_precision==16 would cause the intermediate rescale array
calculations to overflow a signed long.
On ILP32 systems, it is possible for the BMP writer to request an
inversion array large enough that the intermediate calculations in
realize_virt_arrays() would overflow a signed long. (Specifically, this
would occur when writing a 24-bit BMP if width * height > INT_MAX / 3.)
The overflow was innocuous, because any values large enough to trigger
it would almost immediately cause the memory manager to exceed
MAX_ALLOC_CHUNK and throw a fatal error. However, the issue caused
UBSan to complain.
Fixes#874