diff --git a/src/psd.imageio/psdinput.cpp b/src/psd.imageio/psdinput.cpp index ab171a1428..dadf6d610a 100644 --- a/src/psd.imageio/psdinput.cpp +++ b/src/psd.imageio/psdinput.cpp @@ -1094,6 +1094,11 @@ PSDInput::load_color_data() return false; if (m_color_data.length) { + // Check if the reported size of the color data is less than the + // remaining size of the file before we allocate. + int64_t file_size = ioproxy() ? ioproxy()->size() : 0; + if (iotell() + int64_t(m_color_data.length) > file_size) + return false; m_color_data.data.reset(new uint8_t[m_color_data.length]); return ioread(m_color_data.data.get(), m_color_data.length); } @@ -1455,7 +1460,21 @@ PSDInput::load_resource_thumbnail(uint32_t length, bool isBGR) uint32_t compressed_size; uint16_t bpp; uint16_t planes; + + // The 28-byte thumbnail header must fit within the resource. + if (length < 28) { + errorfmt("[Image Resource] [Thumbnail] resource length {} too small", + length); + return false; + } uint32_t jpeg_length = length - 28; + int64_t file_size = ioproxy() ? ioproxy()->size() : 0; + if (int64_t(jpeg_length) > file_size - iotell()) { + errorfmt( + "[Image Resource] [Thumbnail] data length {} exceeds remaining file size", + jpeg_length); + return false; + } bool ok = read_bige(format) && read_bige(width) && read_bige(height) && read_bige(widthbytes) @@ -1760,6 +1779,18 @@ PSDInput::load_layer_channel(Layer& layer, ChannelInfo& channel_info) channel_info.height = height; channel_info.data_pos = iotell(); + + // A channel's stored data, including the 2 byte compression tag already + // read, cannot extend past the end of the file. Compare unsigned: for PSB + // the length is 64 bits and a corrupt value would wrap negative if cast. + int64_t file_size = ioproxy() ? ioproxy()->size() : 0; + int64_t remaining = file_size - start_pos; + if (remaining < 0 || channel_info.data_length > uint64_t(remaining)) { + errorfmt("[Layer Channel] data length {} exceeds remaining file size", + channel_info.data_length); + return false; + } + channel_info.row_pos.resize(height); channel_info.row_length = (width * m_header.depth + 7) / 8; @@ -1784,7 +1815,13 @@ PSDInput::load_layer_channel(Layer& layer, ChannelInfo& channel_info) // channel data is located after the RLE lengths channel_info.data_pos = iotell(); - // subtract the RLE lengths read above + // subtract the RLE lengths read above, which a corrupt file may + // declare to be longer than the channel itself + if (uint64_t(channel_info.data_pos - start_pos) + > channel_info.data_length) { + errorfmt("[Layer Channel] RLE lengths exceed channel data length"); + return false; + } channel_info.data_length = channel_info.data_length - (channel_info.data_pos - start_pos); if (height) { @@ -1971,7 +2008,11 @@ PSDInput::load_layers_16_32(uint64_t length) LayerMaskInfo::LayerInfo& layer_info = m_layer_mask_info.layer_info; // The layer info length must have been 0 in the actual layer info section - OIIO_ASSERT(layer_info.length == 0); + if (layer_info.length != 0) { + errorfmt("[Global Additional Layer Info] unexpected second layer info " + "section"); + return false; + } layer_info.length = length; uint64_t begin = iotell(); diff --git a/testsuite/psd/ref/out.txt b/testsuite/psd/ref/out.txt index 5440e0b0eb..45b5eda4db 100644 --- a/testsuite/psd/ref/out.txt +++ b/testsuite/psd/ref/out.txt @@ -2126,3 +2126,19 @@ src/crash-rowbounds-f999.psd : 10 x 111, 3 channel, uint8 psd oiiotool ERROR: read : "src/crash-bomb-30000.psd": psd header claims a 2574 MB image from a 40 byte file; probably a corrupt or malicious header Full command line was: > oiiotool --info -v -a --hash src/crash-bomb-30000.psd +oiiotool ERROR: read : [Image Resource] [Thumbnail] data length 4294966414 exceeds remaining file size +failed to open "src/crash-thumb-oom.psd": failed load_resources +Full command line was: +> oiiotool --info -v -a --hash src/crash-thumb-oom.psd +oiiotool ERROR: read : [Layer Channel] data length 4294967280 exceeds remaining file size +failed to open "src/crash-chanlen.psd": failed load_layers +Full command line was: +> oiiotool --info -v -a --hash src/crash-chanlen.psd +oiiotool ERROR: read : [Layer Channel] data length 9223372036854775808 exceeds remaining file size +failed to open "src/crash-chanlen-psb.psb": failed load_layers +Full command line was: +> oiiotool --info -v -a --hash src/crash-chanlen-psb.psb +oiiotool ERROR: read : [Global Additional Layer Info] unexpected second layer info section +failed to open "src/crash-dup-layerinfo.psd": failed load_global_additional +Full command line was: +> oiiotool --info -v -a --hash src/crash-dup-layerinfo.psd diff --git a/testsuite/psd/run.py b/testsuite/psd/run.py index 5cfb80a90d..e0f2bdae7f 100755 --- a/testsuite/psd/run.py +++ b/testsuite/psd/run.py @@ -42,3 +42,11 @@ command += info_command ("src/crash-rowbounds-f999.psd", failureok=True) # Tiny file declaring a huge composite (decompression bomb) is rejected command += info_command ("src/crash-bomb-30000.psd", failureok=True) +# Corrupt thumbnail resource length underflowed into a multi-GB allocation +command += info_command ("src/crash-thumb-oom.psd", failureok=True) +# Layer channel declaring more data than the file holds +command += info_command ("src/crash-chanlen.psd", failureok=True) +# Same, but a PSB whose 64-bit channel length has the sign bit set +command += info_command ("src/crash-chanlen-psb.psb", failureok=True) +# Both a normal layer info section and an Lr16 block (used to assert) +command += info_command ("src/crash-dup-layerinfo.psd", failureok=True) diff --git a/testsuite/psd/src/crash-chanlen-psb.psb b/testsuite/psd/src/crash-chanlen-psb.psb new file mode 100644 index 0000000000..6d921f8da6 Binary files /dev/null and b/testsuite/psd/src/crash-chanlen-psb.psb differ diff --git a/testsuite/psd/src/crash-chanlen.psd b/testsuite/psd/src/crash-chanlen.psd new file mode 100644 index 0000000000..51435eb765 Binary files /dev/null and b/testsuite/psd/src/crash-chanlen.psd differ diff --git a/testsuite/psd/src/crash-dup-layerinfo.psd b/testsuite/psd/src/crash-dup-layerinfo.psd new file mode 100644 index 0000000000..5c18a36fc0 Binary files /dev/null and b/testsuite/psd/src/crash-dup-layerinfo.psd differ diff --git a/testsuite/psd/src/crash-thumb-oom.psd b/testsuite/psd/src/crash-thumb-oom.psd new file mode 100644 index 0000000000..7908da3015 Binary files /dev/null and b/testsuite/psd/src/crash-thumb-oom.psd differ