Skip to content

Commit 1865822

Browse files
committed
fix(psd): fixes against corrupt files with better validation
* Validate channel count against color mode to prevent heap buffer overflow. * Corrupted PSD files can declare a color mode (e.g. RGB, needing 3 channels) with transparency (needing +1) but report fewer channels in the header. This caused an out-of-bounds read in read_native_scanline when setup() built channel pointer arrays using mode_channel_count, which exceeded the actual m_image_data.channel_info size. Used Claude Code / Opus 4.6 to help narrow down and confirm the bug. Signed-off-by: Larry Gritz <lg@larrygritz.com> Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 92c6c68 commit 1865822

6 files changed

Lines changed: 61 additions & 23 deletions

File tree

src/psd.imageio/psdinput.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,7 +1468,9 @@ PSDInput::load_layers()
14681468
ok &= read_bige<int16_t>(layer_info.layer_count);
14691469
if (layer_info.layer_count < 0) {
14701470
m_image_data.transparency = true;
1471-
layer_info.layer_count = -layer_info.layer_count;
1471+
if (layer_info.layer_count == -32768)
1472+
return false; // will overflow when negated
1473+
layer_info.layer_count = -layer_info.layer_count;
14721474
}
14731475
m_layers.resize(layer_info.layer_count);
14741476
for (int16_t layer_nbr = 0; layer_nbr < layer_info.layer_count;
@@ -1853,7 +1855,9 @@ PSDInput::load_layers_16_32(uint64_t length)
18531855
ok &= read_bige<int16_t>(layer_info.layer_count);
18541856
if (layer_info.layer_count < 0) {
18551857
m_image_data.transparency = true;
1856-
layer_info.layer_count = -layer_info.layer_count;
1858+
if (layer_info.layer_count == -32768)
1859+
return false; // will overflow when negated
1860+
layer_info.layer_count = -layer_info.layer_count;
18571861
}
18581862
m_layers.resize(layer_info.layer_count);
18591863
for (int16_t layer_nbr = 0; layer_nbr < layer_info.layer_count;
@@ -1894,6 +1898,22 @@ PSDInput::load_image_data()
18941898
compression);
18951899
return false;
18961900
}
1901+
// Validate that the file has enough channels for its color mode.
1902+
// mode_channel_count gives the minimum channels required; if the layer
1903+
// info section indicated transparency, we need one more channel.
1904+
{
1905+
int required = (m_header.color_mode <= ColorMode_Lab)
1906+
? (int)mode_channel_count[m_header.color_mode]
1907+
: 0;
1908+
if (m_image_data.transparency)
1909+
required++;
1910+
if (m_header.channel_count < required) {
1911+
errorfmt(
1912+
"[Image Data Section] channel count {} is too few for color mode {}",
1913+
m_header.channel_count, m_header.color_mode);
1914+
return false;
1915+
}
1916+
}
18971917
m_image_data.channel_info.resize(m_header.channel_count);
18981918
// setup some generic properties and read any RLE lengths
18991919
// Image Data Section has RLE lengths for all channels stored first

testsuite/psd/ref/out-linuxarm.txt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,14 +1467,6 @@ src/layer-mask.psd : 10 x 10, 4 channel, uint8 psd
14671467
stEvt:instanceID: "xmp.iid:a64763c8-be7b-ff48-b857-0f1ee8e5da2b; xmp.iid:9847e4c5-ca7e-fa42-9c7f-5fe6373d31de; xmp.iid:ddf40b95-b12c-744e-a1a9-5e7724fe4ca9"
14681468
stEvt:softwareAgent: "Adobe Photoshop CC 2017 (Windows)"
14691469
stEvt:when: "2017-07-13T10:26:10+09:00; 2017-07-13T10:32:41+09:00; 2017-07-13T11:42:54+09:00"
1470-
oiiotool ERROR: read : Failed to decode Exif data
1471-
failed to open "src/crash-psd-exif-1632.psd": failed load_resources
1472-
Full command line was:
1473-
> oiiotool --info -v -a --hash src/crash-psd-exif-1632.psd
1474-
oiiotool ERROR: read : Corrupt thumbnail: 262304w * 24bpp does not match 480 width bytes
1475-
failed to open "src/crash-thumb-1626.psd": failed load_resources
1476-
Full command line was:
1477-
> oiiotool --info -v -a --hash src/crash-thumb-1626.psd
14781470
Reading src/Layers_8bit_RGB.psd
14791471
src/Layers_8bit_RGB.psd : 48 x 27, 3 channel, uint8 psd
14801472
4 subimages: 48x27 [u8,u8,u8], 48x27 [u8,u8,u8,u8], 48x27 [u8,u8,u8,u8], 48x27 [u8,u8,u8,u8]
@@ -2098,3 +2090,18 @@ src/Layers_32bit_RGB.psd : 48 x 27, 3 channel, float psd
20982090
stEvt:instanceID: "xmp.iid:68fcb000-4377-c148-974d-bdd193ca024d; xmp.iid:cbd904c5-53b4-0d4e-9ff8-37ac35429f46"
20992091
stEvt:softwareAgent: "Adobe Photoshop 23.3 (Windows)"
21002092
stEvt:when: "2024-04-01T19:35:16+02:00"
2093+
oiiotool ERROR: read : Failed to decode Exif data
2094+
failed to open "src/crash-psd-exif-1632.psd": failed load_resources
2095+
Full command line was:
2096+
> oiiotool --info -v -a --hash src/crash-psd-exif-1632.psd
2097+
oiiotool ERROR: read : Corrupt thumbnail: 262304w * 24bpp does not match 480 width bytes
2098+
failed to open "src/crash-thumb-1626.psd": failed load_resources
2099+
Full command line was:
2100+
> oiiotool --info -v -a --hash src/crash-thumb-1626.psd
2101+
oiiotool ERROR: read : failed to open "src/crash-005c.psd": failed load_layers
2102+
Full command line was:
2103+
> oiiotool --info -v -a --hash src/crash-005c.psd
2104+
oiiotool ERROR: read : [Image Data Section] channel count 3 is too few for color mode 3
2105+
failed to open "src/crash-8a15.psd": failed load_image_data
2106+
Full command line was:
2107+
> oiiotool --info -v -a --hash src/crash-8a15.psd

testsuite/psd/ref/out.txt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,14 +1467,6 @@ src/layer-mask.psd : 10 x 10, 4 channel, uint8 psd
14671467
stEvt:instanceID: "xmp.iid:a64763c8-be7b-ff48-b857-0f1ee8e5da2b; xmp.iid:9847e4c5-ca7e-fa42-9c7f-5fe6373d31de; xmp.iid:ddf40b95-b12c-744e-a1a9-5e7724fe4ca9"
14681468
stEvt:softwareAgent: "Adobe Photoshop CC 2017 (Windows)"
14691469
stEvt:when: "2017-07-13T10:26:10+09:00; 2017-07-13T10:32:41+09:00; 2017-07-13T11:42:54+09:00"
1470-
oiiotool ERROR: read : Failed to decode Exif data
1471-
failed to open "src/crash-psd-exif-1632.psd": failed load_resources
1472-
Full command line was:
1473-
> oiiotool --info -v -a --hash src/crash-psd-exif-1632.psd
1474-
oiiotool ERROR: read : Corrupt thumbnail: 262304w * 24bpp does not match 480 width bytes
1475-
failed to open "src/crash-thumb-1626.psd": failed load_resources
1476-
Full command line was:
1477-
> oiiotool --info -v -a --hash src/crash-thumb-1626.psd
14781470
Reading src/Layers_8bit_RGB.psd
14791471
src/Layers_8bit_RGB.psd : 48 x 27, 3 channel, uint8 psd
14801472
4 subimages: 48x27 [u8,u8,u8], 48x27 [u8,u8,u8,u8], 48x27 [u8,u8,u8,u8], 48x27 [u8,u8,u8,u8]
@@ -2098,3 +2090,18 @@ src/Layers_32bit_RGB.psd : 48 x 27, 3 channel, float psd
20982090
stEvt:instanceID: "xmp.iid:68fcb000-4377-c148-974d-bdd193ca024d; xmp.iid:cbd904c5-53b4-0d4e-9ff8-37ac35429f46"
20992091
stEvt:softwareAgent: "Adobe Photoshop 23.3 (Windows)"
21002092
stEvt:when: "2024-04-01T19:35:16+02:00"
2093+
oiiotool ERROR: read : Failed to decode Exif data
2094+
failed to open "src/crash-psd-exif-1632.psd": failed load_resources
2095+
Full command line was:
2096+
> oiiotool --info -v -a --hash src/crash-psd-exif-1632.psd
2097+
oiiotool ERROR: read : Corrupt thumbnail: 262304w * 24bpp does not match 480 width bytes
2098+
failed to open "src/crash-thumb-1626.psd": failed load_resources
2099+
Full command line was:
2100+
> oiiotool --info -v -a --hash src/crash-thumb-1626.psd
2101+
oiiotool ERROR: read : failed to open "src/crash-005c.psd": failed load_layers
2102+
Full command line was:
2103+
> oiiotool --info -v -a --hash src/crash-005c.psd
2104+
oiiotool ERROR: read : [Image Data Section] channel count 3 is too few for color mode 3
2105+
failed to open "src/crash-8a15.psd": failed load_image_data
2106+
Full command line was:
2107+
> oiiotool --info -v -a --hash src/crash-8a15.psd

testsuite/psd/run.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919
command += info_command ("src/different-mask-size.psd")
2020
command += info_command ("src/layer-mask.psd")
2121

22-
# This file has a corrupted Exif block
23-
command += info_command ("src/crash-psd-exif-1632.psd", failureok = 1)
24-
# Corrupted thumbnail clobbered memory
25-
command += info_command ("src/crash-thumb-1626.psd", failureok = 1)
26-
2722
# Test more modern (Photoshop 2023 files) with 16- and 32-bit files containing multiple sublayers
2823
command += info_command ("src/Layers_8bit_RGB.psd")
2924
command += info_command ("src/Layers_16bit_RGB.psd")
3025
command += info_command ("src/Layers_32bit_RGB.psd")
26+
27+
# This file has a corrupted Exif block
28+
command += info_command ("src/crash-psd-exif-1632.psd", failureok = 1)
29+
# Corrupted thumbnail clobbered memory
30+
command += info_command ("src/crash-thumb-1626.psd", failureok = 1)
31+
# Corruption caused an integer overflow
32+
command += info_command ("src/crash-005c.psd", failureok=True)
33+
# Corruption where the file didn't have enough channels for its color mode
34+
command += info_command ("src/crash-8a15.psd", failureok=True)

testsuite/psd/src/crash-005c.psd

43.6 KB
Binary file not shown.

testsuite/psd/src/crash-8a15.psd

22.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)