jchuff.c: Fix uninit read w/ AArch64, WITH_SIMD=0
Because ofbf01ed2fbc, the simd field in huff_entropy_encoder (and, by extension, the simd field in savable_state) is only initialized if WITH_SIMD is defined. Due to an oversight, the simd field in savable_state was queried in flush_bits() regardless of whether WITH_SIMD was defined. In most cases, both branches of the query have identical code, and the optimizer removes the branch. However, because the legacy Neon GAS Huffman encoder uses the older bit buffer logic from libjpeg-turbo 2.0.x and prior (refer to087c29e07f), the branches do not have identical code when building for AArch64 with NEON_INTRINSICS undefined (which will be the case if WITH_SIMD is undefined.) Thus, if libjpeg-turbo was built for AArch64 with the SIMD extensions disabled at build time, it was possible for the Neon GAS branch in flush_bits() to be taken, which would have set put_bits to a value that is incorrect for the C Huffman encoder. Referring to #728, a user reported that this issue sometimes caused libjpeg-turbo to generate bogus JPEG images if it was built for AArch64 without SIMD extensions and subsequently used through the Qt framework. (It should be noted, however, that disabling the SIMD extensions in AArch64 builds of libjpeg-turbo is inadvisable for performance reasons.) I was unable to reproduce the issue on Linux/AArch64 using libjpeg-turbo alone, despite testing various versions of GCC and Clang and various optimization levels. However, the issue is reproducible using MSan with -O0, so this commit also modifies the GitHub Actions workflow so that compiler optimization is disabled in the linux-msan job. That should prevent the issue or similar issues from re-emerging. Fixes #728
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -169,7 +169,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
pushd build
|
||||
cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_C_FLAGS_RELWITHDEBINFO="-O3 -g -fsanitize=memory -fno-sanitize-recover=all -fPIE" -DWITH_SIMD=0 ..
|
||||
cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_C_FLAGS_RELWITHDEBINFO="-O0 -g -fsanitize=memory -fno-sanitize-recover=all -fPIE" -DWITH_SIMD=0 ..
|
||||
export NUMCPUS=`grep -c '^processor' /proc/cpuinfo`
|
||||
make -j$NUMCPUS --load-average=$NUMCPUS
|
||||
make test
|
||||
|
||||
@@ -12,6 +12,12 @@ mathematical (but not necessarily perceptible) edge block errors when
|
||||
decompressing progressive JPEG images exactly two MCU blocks in width or that
|
||||
use vertical chrominance subsampling.
|
||||
|
||||
3. Fixed a regression introduced by 3.0 beta2[6] that, in rare cases, caused
|
||||
the C Huffman encoder (which is not used by default on x86 and Arm CPUs) to
|
||||
generate incorrect results if the Neon SIMD extensions were explicitly disabled
|
||||
at build time (by setting the `WITH_SIMD` CMake variable to `0`) in an AArch64
|
||||
build of libjpeg-turbo.
|
||||
|
||||
|
||||
3.0.0
|
||||
=====
|
||||
|
||||
20
jchuff.c
20
jchuff.c
@@ -108,7 +108,9 @@ typedef bit_buf_type simd_bit_buf_type;
|
||||
typedef struct {
|
||||
union {
|
||||
bit_buf_type c;
|
||||
#ifdef WITH_SIMD
|
||||
simd_bit_buf_type simd;
|
||||
#endif
|
||||
} put_buffer; /* current bit accumulation buffer */
|
||||
int free_bits; /* # of bits available in it */
|
||||
/* (Neon GAS: # of bits now in it) */
|
||||
@@ -133,7 +135,9 @@ typedef struct {
|
||||
long *ac_count_ptrs[NUM_HUFF_TBLS];
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SIMD
|
||||
int simd;
|
||||
#endif
|
||||
} huff_entropy_encoder;
|
||||
|
||||
typedef huff_entropy_encoder *huff_entropy_ptr;
|
||||
@@ -147,7 +151,9 @@ typedef struct {
|
||||
size_t free_in_buffer; /* # of byte spaces remaining in buffer */
|
||||
savable_state cur; /* Current bit buffer & DC state */
|
||||
j_compress_ptr cinfo; /* dump_buffer needs access to this */
|
||||
#ifdef WITH_SIMD
|
||||
int simd;
|
||||
#endif
|
||||
} working_state;
|
||||
|
||||
|
||||
@@ -511,6 +517,7 @@ flush_bits(working_state *state)
|
||||
simd_bit_buf_type put_buffer; int put_bits;
|
||||
int localbuf = 0;
|
||||
|
||||
#ifdef WITH_SIMD
|
||||
if (state->simd) {
|
||||
#if defined(__aarch64__) && !defined(NEON_INTRINSICS)
|
||||
put_bits = state->cur.free_bits;
|
||||
@@ -518,7 +525,9 @@ flush_bits(working_state *state)
|
||||
put_bits = SIMD_BIT_BUF_SIZE - state->cur.free_bits;
|
||||
#endif
|
||||
put_buffer = state->cur.put_buffer.simd;
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
put_bits = BIT_BUF_SIZE - state->cur.free_bits;
|
||||
put_buffer = state->cur.put_buffer.c;
|
||||
}
|
||||
@@ -536,6 +545,7 @@ flush_bits(working_state *state)
|
||||
EMIT_BYTE(temp)
|
||||
}
|
||||
|
||||
#ifdef WITH_SIMD
|
||||
if (state->simd) { /* and reset bit buffer to empty */
|
||||
state->cur.put_buffer.simd = 0;
|
||||
#if defined(__aarch64__) && !defined(NEON_INTRINSICS)
|
||||
@@ -543,7 +553,9 @@ flush_bits(working_state *state)
|
||||
#else
|
||||
state->cur.free_bits = SIMD_BIT_BUF_SIZE;
|
||||
#endif
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
state->cur.put_buffer.c = 0;
|
||||
state->cur.free_bits = BIT_BUF_SIZE;
|
||||
}
|
||||
@@ -719,7 +731,9 @@ encode_mcu_huff(j_compress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
state.free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
state.cur = entropy->saved;
|
||||
state.cinfo = cinfo;
|
||||
#ifdef WITH_SIMD
|
||||
state.simd = entropy->simd;
|
||||
#endif
|
||||
|
||||
/* Emit restart marker if needed */
|
||||
if (cinfo->restart_interval) {
|
||||
@@ -792,7 +806,9 @@ finish_pass_huff(j_compress_ptr cinfo)
|
||||
state.free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
state.cur = entropy->saved;
|
||||
state.cinfo = cinfo;
|
||||
#ifdef WITH_SIMD
|
||||
state.simd = entropy->simd;
|
||||
#endif
|
||||
|
||||
/* Flush out the last data */
|
||||
if (!flush_bits(&state))
|
||||
|
||||
Reference in New Issue
Block a user