Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions src/psd.imageio/psdinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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<uint32_t>(format) && read_bige<uint32_t>(width)
&& read_bige<uint32_t>(height) && read_bige<uint32_t>(widthbytes)
Expand Down Expand Up @@ -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;

Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Comment on lines +2011 to +2014
}
layer_info.length = length;

uint64_t begin = iotell();
Expand Down
16 changes: 16 additions & 0 deletions testsuite/psd/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions testsuite/psd/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Binary file added testsuite/psd/src/crash-chanlen-psb.psb
Binary file not shown.
Binary file added testsuite/psd/src/crash-chanlen.psd
Binary file not shown.
Binary file added testsuite/psd/src/crash-dup-layerinfo.psd
Binary file not shown.
Binary file added testsuite/psd/src/crash-thumb-oom.psd
Binary file not shown.
Loading