diff --git a/src/jpegxl.imageio/jxlinput.cpp b/src/jpegxl.imageio/jxlinput.cpp index 3ac507fe80..9e268ca74f 100644 --- a/src/jpegxl.imageio/jxlinput.cpp +++ b/src/jpegxl.imageio/jxlinput.cpp @@ -208,6 +208,10 @@ JxlInput::open(const std::string& name, ImageSpec& newspec) jxl.reset(new uint8_t[size]); size_t result = m_io->read(jxl.get(), size); DBG std::cout << "result = " << result << "\n"; + if (result != size) { + errorfmt("Failed to read {} bytes from \"{}\"", size, m_filename); + return false; + } status = JxlDecoderSetInput(m_decoder.get(), jxl.get(), size); if (status != JXL_DEC_SUCCESS) { @@ -340,10 +344,11 @@ JxlInput::open(const std::string& name, ImageSpec& newspec) errorfmt("JxlDecoderImageOutBufferSize failed\n"); return false; } - if (buffer_size - != info.xsize * info.ysize * m_channels * bits / 8) { + size_t expected_size = size_t(info.xsize) * info.ysize * m_channels + * bits / 8; + if (buffer_size != expected_size) { errorfmt("Invalid out buffer size {} {}\n", buffer_size, - info.xsize * info.ysize * m_channels * bits / 8); + expected_size); return false; } @@ -381,6 +386,15 @@ JxlInput::open(const std::string& name, ImageSpec& newspec) return false; } + // A valid image always triggers JXL_DEC_NEED_IMAGE_OUT_BUFFER and allocates + // m_buffer during the decode loop above. If it didn't, the file provided + // basic info but no decodable pixels; fail rather than let a later + // read_native_scanline() dereference a null buffer. + if (!m_buffer) { + errorfmt("Possible corrupt file, no JPEG XL image data decoded\n"); + return false; + } + // Read ICC profile if (m_icc_profile.size() && m_icc_profile.data()) { m_spec.attribute("ICCProfile", diff --git a/testsuite/jxl/ref/out-jxl0.12.txt b/testsuite/jxl/ref/out-jxl0.12.txt index bcacb63c91..4c63c7c9ae 100644 --- a/testsuite/jxl/ref/out-jxl0.12.txt +++ b/testsuite/jxl/ref/out-jxl0.12.txt @@ -91,3 +91,6 @@ oiiotool ERROR: read : "src/crash-bfd2220.jxl": jpegxl image dimension 328438676 If you're sure this is a valid file, raise the OIIO global attribute "limits:resolution". Full command line was: > oiiotool -info -oiioattrib limits:imagesize_MB 16384 src/crash-bfd2220.jxl +oiiotool ERROR: read : "src/truncated.jxl": JPEG XL decoder error +Full command line was: +> oiiotool --info -v -a --no-metamatch DateTime|Software|OriginatingProgram|ImageHistory --hash src/truncated.jxl diff --git a/testsuite/jxl/ref/out.txt b/testsuite/jxl/ref/out.txt index 625b1288e3..9edf3492d2 100644 --- a/testsuite/jxl/ref/out.txt +++ b/testsuite/jxl/ref/out.txt @@ -91,3 +91,6 @@ oiiotool ERROR: read : "src/crash-bfd2220.jxl": jpegxl image dimension 328438676 If you're sure this is a valid file, raise the OIIO global attribute "limits:resolution". Full command line was: > oiiotool -info -oiioattrib limits:imagesize_MB 16384 src/crash-bfd2220.jxl +oiiotool ERROR: read : "src/truncated.jxl": JPEG XL decoder error +Full command line was: +> oiiotool --info -v -a --no-metamatch DateTime|Software|OriginatingProgram|ImageHistory --hash src/truncated.jxl diff --git a/testsuite/jxl/run.py b/testsuite/jxl/run.py index e6b05d30eb..6bb2a673a7 100755 --- a/testsuite/jxl/run.py +++ b/testsuite/jxl/run.py @@ -24,6 +24,10 @@ # Corrupt input that previously triggered an oversized allocation path in JXL decode command += oiiotool ("-info -oiioattrib limits:imagesize_MB 16384 src/crash-bfd2220.jxl", failureok=True) +# Truncated codestream: the decoder must report a clean error, not read +# uninitialized input or leave a null decode buffer for a later scanline read. +command += info_command ("src/truncated.jxl", failureok=True, safematch=True) + outputs = [ "test-jxl.icc", "out.txt" diff --git a/testsuite/jxl/src/truncated.jxl b/testsuite/jxl/src/truncated.jxl new file mode 100644 index 0000000000..6f7714b738 Binary files /dev/null and b/testsuite/jxl/src/truncated.jxl differ