diff --git a/src/psd.imageio/psdinput.cpp b/src/psd.imageio/psdinput.cpp index 28395bebab..37cd0ecf03 100644 --- a/src/psd.imageio/psdinput.cpp +++ b/src/psd.imageio/psdinput.cpp @@ -306,7 +306,7 @@ class PSDInput final : public ImageInput { // Interleave channels (RRRGGGBBB -> RGBRGBRGB) while copying from // channel_buffers[0..nchans-1] to dst. template - static void + static bool interleave_row(T* dst, cspan> channel_buffers, int width, int nchans); @@ -808,16 +808,25 @@ PSDInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, || m_header.color_mode == ColorMode_Grayscale) { switch (bps) { case 4: - interleave_row((float*)dst, channel_buffers, spec.width, - spec.nchannels); + if (!interleave_row((float*)dst, channel_buffers, spec.width, + spec.nchannels)) { + errorfmt("Corrupt PSD channel data for row {}", y + spec.y); + return false; + } break; case 2: - interleave_row((unsigned short*)dst, channel_buffers, spec.width, - spec.nchannels); + if (!interleave_row((unsigned short*)dst, channel_buffers, + spec.width, spec.nchannels)) { + errorfmt("Corrupt PSD channel data for row {}", y + spec.y); + return false; + } break; default: - interleave_row((unsigned char*)dst, channel_buffers, spec.width, - spec.nchannels); + if (!interleave_row((unsigned char*)dst, channel_buffers, + spec.width, spec.nchannels)) { + errorfmt("Corrupt PSD channel data for row {}", y + spec.y); + return false; + } break; } } else if (m_header.color_mode == ColorMode_CMYK) { @@ -825,8 +834,11 @@ PSDInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, switch (bps) { case 4: { std::unique_ptr cmyk(new float[cmyklen]); - interleave_row(cmyk.get(), channel_buffers, spec.width, - channel_count); + if (!interleave_row(cmyk.get(), channel_buffers, spec.width, + channel_count)) { + errorfmt("Corrupt PSD channel data for row {}", y + spec.y); + return false; + } cmyk_to_rgb(spec.width, make_cspan(cmyk.get(), cmyklen), channel_count, make_span((float*)dst, spec.width * spec.nchannels), @@ -835,8 +847,11 @@ PSDInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, } case 2: { std::unique_ptr cmyk(new unsigned short[cmyklen]); - interleave_row(cmyk.get(), channel_buffers, spec.width, - channel_count); + if (!interleave_row(cmyk.get(), channel_buffers, spec.width, + channel_count)) { + errorfmt("Corrupt PSD channel data for row {}", y + spec.y); + return false; + } cmyk_to_rgb(spec.width, make_cspan(cmyk.get(), cmyklen), channel_count, make_span((uint16_t*)dst, spec.width * spec.nchannels), @@ -845,8 +860,11 @@ PSDInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, } default: { std::unique_ptr cmyk(new unsigned char[cmyklen]); - interleave_row(cmyk.get(), channel_buffers, spec.width, - channel_count); + if (!interleave_row(cmyk.get(), channel_buffers, spec.width, + channel_count)) { + errorfmt("Corrupt PSD channel data for row {}", y + spec.y); + return false; + } cmyk_to_rgb(spec.width, make_cspan(cmyk.get(), cmyklen), channel_count, make_span((uint8_t*)dst, spec.width * spec.nchannels), @@ -2191,16 +2209,24 @@ PSDInput::read_channel_row(ChannelInfo& channel_info, uint32_t row, char* data) template -void +bool PSDInput::interleave_row(T* dst, cspan> channel_buffers, int width, int nchans) { + if (!dst || width < 0 || nchans < 0 + || span_size_t(channel_buffers.size()) < size_t(nchans)) + return false; + + const uint64_t row_bytes = uint64_t(width) * sizeof(T); for (int c = 0; c < nchans; ++c) { + if (channel_buffers[c].size() < row_bytes) + return false; const T* cbuf = reinterpret_cast(channel_buffers[c].data()); for (int x = 0; x < width; ++x) dst[nchans * x + c] = cbuf[x]; } + return true; } diff --git a/testsuite/psd/ref/out-linuxarm.txt b/testsuite/psd/ref/out-linuxarm.txt index 50a050ea38..1c514321e8 100644 --- a/testsuite/psd/ref/out-linuxarm.txt +++ b/testsuite/psd/ref/out-linuxarm.txt @@ -2117,3 +2117,9 @@ oiiotool ERROR: read : Read error: hit end of file in psd reader failed to open "src/crash-eofstring.psd": failed load_resources Full command line was: > oiiotool --info -v -a --hash src/crash-eofstring.psd +Reading src/crash-rowbounds-f999.psd +oiiotool ERROR: -info : SHA-1: Corrupt PSD channel data for row 0 +Full command line was: +> oiiotool --info -v -a --hash src/crash-rowbounds-f999.psd +src/crash-rowbounds-f999.psd : 10 x 111, 3 channel, uint8 psd + channel list: R, G, B diff --git a/testsuite/psd/ref/out.txt b/testsuite/psd/ref/out.txt index fb8bc906e5..ee9877bdbb 100644 --- a/testsuite/psd/ref/out.txt +++ b/testsuite/psd/ref/out.txt @@ -2117,3 +2117,9 @@ oiiotool ERROR: read : Read error: hit end of file in psd reader failed to open "src/crash-eofstring.psd": failed load_resources Full command line was: > oiiotool --info -v -a --hash src/crash-eofstring.psd +Reading src/crash-rowbounds-f999.psd +oiiotool ERROR: -info : SHA-1: Corrupt PSD channel data for row 0 +Full command line was: +> oiiotool --info -v -a --hash src/crash-rowbounds-f999.psd +src/crash-rowbounds-f999.psd : 10 x 111, 3 channel, uint8 psd + channel list: R, G, B diff --git a/testsuite/psd/run.py b/testsuite/psd/run.py index 33700e5ee5..83bab5d2e3 100755 --- a/testsuite/psd/run.py +++ b/testsuite/psd/run.py @@ -38,3 +38,5 @@ command += info_command ("src/crash-layerres.psd", failureok=True) # Corruption where eof was hit in the end of a string read command += info_command ("src/crash-eofstring.psd", failureok=True) +# Corruption where short per-channel rows could overrun interleaving +command += info_command ("src/crash-rowbounds-f999.psd", failureok=True) diff --git a/testsuite/psd/src/crash-rowbounds-f999.psd b/testsuite/psd/src/crash-rowbounds-f999.psd new file mode 100644 index 0000000000..1479c7b75f Binary files /dev/null and b/testsuite/psd/src/crash-rowbounds-f999.psd differ