diff --git a/Tests/images/psd-oob-write-x.psd b/Tests/images/psd-oob-write-x.psd new file mode 100644 index 00000000000..86359f4cb7e Binary files /dev/null and b/Tests/images/psd-oob-write-x.psd differ diff --git a/Tests/images/psd-oob-write-y.psd b/Tests/images/psd-oob-write-y.psd new file mode 100644 index 00000000000..73498266a7d Binary files /dev/null and b/Tests/images/psd-oob-write-y.psd differ diff --git a/Tests/images/psd-oob-write.psd b/Tests/images/psd-oob-write.psd new file mode 100644 index 00000000000..65a4472cf26 Binary files /dev/null and b/Tests/images/psd-oob-write.psd differ diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 38a88cd17a8..63db7b26a02 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -184,3 +184,20 @@ def test_layer_crashes(test_file: str) -> None: assert isinstance(im, PsdImagePlugin.PsdImageFile) with pytest.raises(SyntaxError): im.layers + + +@pytest.mark.parametrize( + "test_file", + [ + "Tests/images/psd-oob-write.psd", + "Tests/images/psd-oob-write-x.psd", + "Tests/images/psd-oob-write-y.psd", + ], +) +def test_bounds_crash(test_file: str) -> None: + with Image.open(test_file) as im: + assert isinstance(im, PsdImagePlugin.PsdImageFile) + im.seek(im.n_frames) + + with pytest.raises(ValueError): + im.load() diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 7dfb3abf986..2ef9fe2b9c4 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -169,6 +169,13 @@ def test_negative_offset(self) -> None: with pytest.raises(ValueError, match="Tile offset cannot be negative"): im.load() + @pytest.mark.parametrize("xy", ((-1, 0), (0, -1))) + def test_negative_tile_extents(self, xy: tuple[int, int]) -> None: + im = Image.new("1", (1, 1)) + fp = BytesIO() + with pytest.raises(SystemError, match="tile cannot extend outside image"): + ImageFile._save(im, fp, [ImageFile._Tile("raw", xy + (1, 1), 0, "1")]) + def test_no_format(self) -> None: buf = BytesIO(b"\x00" * 255) diff --git a/depends/install_libavif.sh b/depends/install_libavif.sh index 50ba0175567..a6686f3ef3a 100755 --- a/depends/install_libavif.sh +++ b/depends/install_libavif.sh @@ -7,6 +7,10 @@ version=1.3.0 pushd libavif-$version +# Apply patch for SVT-AV1 4.0 compatibility +# Pending release of https://github.com/AOMediaCodec/libavif/pull/2971 +patch -p1 < ../libavif-svt4.patch + if [ $(uname) == "Darwin" ] && [ -x "$(command -v brew)" ]; then PREFIX=$(brew --prefix) else diff --git a/depends/libavif-svt4.patch b/depends/libavif-svt4.patch new file mode 100644 index 00000000000..7abfc529970 --- /dev/null +++ b/depends/libavif-svt4.patch @@ -0,0 +1,14 @@ +--- a/src/codec_svt.c ++++ b/src/codec_svt.c +@@ -162,7 +162,11 @@ static avifResult svtCodecEncodeImage(avifEncoder * encoder, + #else + svt_config->logical_processors = encoder->maxThreads; + #endif ++#if SVT_AV1_CHECK_VERSION(4, 0, 0) ++ svt_config->aq_mode = 2; ++#else + svt_config->enable_adaptive_quantization = 2; ++#endif + // disable 2-pass + #if SVT_AV1_CHECK_VERSION(0, 9, 0) + svt_config->rc_stats_buffer = (SvtAv1FixedBuf) { NULL, 0 }; diff --git a/docs/releasenotes/12.1.1.rst b/docs/releasenotes/12.1.1.rst new file mode 100644 index 00000000000..9c119ceb8b4 --- /dev/null +++ b/docs/releasenotes/12.1.1.rst @@ -0,0 +1,24 @@ +12.1.1 +------ + +Security +======== + +:cve:`2021-25289`: Fix OOB write with invalid tile extents +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Check that tile extents do not use negative x or y offsets when decoding or encoding, +and raise an error if they do, rather than allowing an OOB write. + +An out-of-bounds write may be triggered when opening a specially crafted PSD image. +This only affects Pillow >= 10.3.0. Reported by +`Yarden Porat `__. + +Other changes +============= + +Patch libavif for svt-av1 4.0 compatibility +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A patch has been added to ``depends/install_libavif.sh``, to allow libavif 1.3.0 to be +compatible with the recently released svt-av1 4.0.0. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 4b25bb6a2d1..690be20729e 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -15,6 +15,7 @@ expected to be backported to earlier versions. :maxdepth: 2 versioning + 12.1.1 12.1.0 12.0.0 11.3.0 diff --git a/src/PIL/_version.py b/src/PIL/_version.py index b32ff446a9e..85d2abd6d2d 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,4 +1,4 @@ # Master version for Pillow from __future__ import annotations -__version__ = "12.1.0" +__version__ = "12.1.1" diff --git a/src/decode.c b/src/decode.c index 051623ed448..7ec461c0e2c 100644 --- a/src/decode.c +++ b/src/decode.c @@ -186,7 +186,8 @@ _setimage(ImagingDecoderObject *decoder, PyObject *args) { state->ysize = y1 - y0; } - if (state->xsize <= 0 || state->xsize + state->xoff > (int)im->xsize || + if (state->xoff < 0 || state->xsize <= 0 || + state->xsize + state->xoff > (int)im->xsize || state->yoff < 0 || state->ysize <= 0 || state->ysize + state->yoff > (int)im->ysize) { PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image"); return NULL; diff --git a/src/encode.c b/src/encode.c index 513309c8d7d..06e4a089380 100644 --- a/src/encode.c +++ b/src/encode.c @@ -254,7 +254,8 @@ _setimage(ImagingEncoderObject *encoder, PyObject *args) { state->ysize = y1 - y0; } - if (state->xsize <= 0 || state->xsize + state->xoff > im->xsize || + if (state->xoff < 0 || state->xsize <= 0 || + state->xsize + state->xoff > im->xsize || state->yoff < 0 || state->ysize <= 0 || state->ysize + state->yoff > im->ysize) { PyErr_SetString(PyExc_SystemError, "tile cannot extend outside image"); return NULL;