@@ -1323,6 +1323,62 @@ def test_realloc_overflow(self, monkeypatch: pytest.MonkeyPatch) -> None:
13231323 with pytest .raises (OSError , match = "decoder error -9" ):
13241324 im .load ()
13251325
1326+ @pytest .mark .parametrize (
1327+ "data" ,
1328+ (
1329+ # 16x16 8-bit greyscale tile, with a PhotometricInterpretation tag that
1330+ # has two values. Pillow reads it as YCbCr (rawmode RGBX, 4 bytes/pixel)
1331+ # while libtiff ignores it and decodes 1 byte/pixel.
1332+ (
1333+ b"SUkqAAgAAAAKAAABBAABAAAAEAAAAAEBBAABAAAAEAAAAAIBAwABAAAACAAA"
1334+ b"AAMBAwABAAAACAAAAAYBAwACAAAABgAGABUBAwABAAAAAQAAAEIBBAABAAAA"
1335+ b"EAAAAEMBBAABAAAAEAAAAEQBBAABAAAAhgAAAEUBBAABAAAACwEAAAAAAAB4"
1336+ b"nAEAAf/+AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUm"
1337+ b"JygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJT"
1338+ b"VFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+A"
1339+ b"gYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6yt"
1340+ b"rq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna"
1341+ b"29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+foAAQIDBJ9Bepo="
1342+ ),
1343+ # 16x16 strip with duplicated BitsPerSample and PhotometricInterpretation
1344+ # tags. libtiff keeps the first (8-bit YCbCr, read as 4 bytes/pixel RGBA)
1345+ # while Pillow keeps the last (16-bit RGB, rawmode RGB;16N, 6 bytes/pixel).
1346+ (
1347+ b"SUkqAAgAAAAMAAABAwABAAAAEAAAAAEBAwABAAAAEAAAAAIBAwADAAAAugEA"
1348+ b"AAIBAwADAAAAwAEAAAMBAwABAAAACAAAAAYBAwABAAAABgAAAAYBAwABAAAA"
1349+ b"AgAAABEBBAABAAAAngAAABUBAwABAAAAAwAAABYBAwABAAAAEAAAABcBBAAB"
1350+ b"AAAAHAEAABICAwACAAAAAQABAAAAAAB4nGNgZGJmYWVj5+Dk4ubh5eMXEBQS"
1351+ b"FhEVE5eQlJKWkZWTV1BUUlZRVVPX0NTS1tHV0zcwNDI2MTUzt7C0sraxtbN3"
1352+ b"cHRydnF1c/fw9PL28fXzDwgMCg4JDQuPiIyKjomNi09ITEpOSU1Lz8jMys7J"
1353+ b"zcsvKCwqLiktK6+orKquqa2rb2hsam5pbWvv6Ozq7unt658wcdLkKVOnTZ8x"
1354+ b"c9bsOXPnzV+wcNHiJUuXLV+xctXqNWvXrd+wcdPmLVu3bd+xc9fuPXv37T9w"
1355+ b"8NDhI0ePHT9x8tTpM2fPnb9w8dLlK1evXb9x89btO3fv3X/w8NHjJ0+fPX/x"
1356+ b"8tXrN2/fvf/w8dPnL1+/ff/x89fvP3///WcY9f+I9j8AoGJ+kAgACAAIABAA"
1357+ b"EAAQAA=="
1358+ ),
1359+ # 4x2 PackBits strip with duplicated BitsPerSample, Photometric
1360+ # Interpretation and SamplesPerPixel tags. libtiff keeps the first
1361+ # (8-bit YCbCr, read as 4 bytes/pixel RGBA) while Pillow keeps the last
1362+ # (6 channel RGB, rawmode RGBAXX, 6 bytes/pixel).
1363+ (
1364+ b"SUkqAAgAAAAPAAABBAABAAAABAAAAAEBBAABAAAAAgAAAAIBAwADAAAAwgAA"
1365+ b"AAIBAwAGAAAAyAAAAAMBAwABAAAABYAAAAYBAwABAAAABgAAAAYBAwABAAAA"
1366+ b"AgAAABEBBAABAAAA2gAAABUBAwABAAAAAwAAABUBAwABAAAABgAAABYBBAAB"
1367+ b"AAAAAgAAABcBBAABAAAAGQAAABwBAwABAAAAAQAAAFIBAwADAAAA1AAAABIC"
1368+ b"AwACAAAAAQABAAAAAAAIAAgACAAIAAgACAAIAAgACAACAAAAAAAXAAECAwQF"
1369+ b"BgcICQoLDA0ODxAREhMUFRYX"
1370+ ),
1371+ ),
1372+ )
1373+ @pytest .mark .filterwarnings ("ignore:Metadata Warning" )
1374+ def test_unpacker_row_wider_than_libtiff_row (self , data : bytes ) -> None :
1375+ # The unpacker chosen by Pillow reads more bytes per row than libtiff
1376+ # decoded, so the decoder must reject the image rather than read past
1377+ # the end of its buffer
1378+ with Image .open (io .BytesIO (base64 .b64decode (data ))) as im :
1379+ with pytest .raises (OSError , match = "decoder error -2" ):
1380+ im .load ()
1381+
13261382 @pytest .mark .parametrize ("compression" , ("tiff_adobe_deflate" , "jpeg" ))
13271383 def test_save_multistrip (self , compression : str , tmp_path : Path ) -> None :
13281384 im = hopper ("RGB" ).resize ((256 , 256 ))
0 commit comments