From cd0111849fb32c40860e3ee3d57b9b1cee4260cf Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:26:20 +0100 Subject: [PATCH 1/3] Patch libavif for svt-av1 4.0 compatibility --- depends/install_libavif.sh | 4 ++++ depends/libavif-svt4.patch | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 depends/libavif-svt4.patch 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 }; From 9000313cc5d4a31bdcdd6d7f0781101abab553aa Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Wed, 11 Feb 2026 10:24:50 +1100 Subject: [PATCH 2/3] Fix OOB Write with invalid tile extents (#9427) Co-authored-by: Eric Soroos --- Tests/images/psd-oob-write-x.psd | Bin 0 -> 1126 bytes Tests/images/psd-oob-write-y.psd | Bin 0 -> 1126 bytes Tests/images/psd-oob-write.psd | Bin 0 -> 37212 bytes Tests/test_file_psd.py | 17 +++++++++++++++++ Tests/test_imagefile.py | 7 +++++++ docs/releasenotes/12.1.1.rst | 24 ++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + src/decode.c | 3 ++- src/encode.c | 3 ++- 9 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 Tests/images/psd-oob-write-x.psd create mode 100644 Tests/images/psd-oob-write-y.psd create mode 100644 Tests/images/psd-oob-write.psd create mode 100644 docs/releasenotes/12.1.1.rst diff --git a/Tests/images/psd-oob-write-x.psd b/Tests/images/psd-oob-write-x.psd new file mode 100644 index 0000000000000000000000000000000000000000..86359f4cb7e826a69a8e69a4b85947498ec18923 GIT binary patch literal 1126 zcma)5J!lkB5dL=WC-F>3z$=1SY;juU8Wp`VZp09|z;cO@XbSh|ZgXUJ@7TRX4pIuX z0SkW`qZT&S+FIBOg5VE`wTL!~HWJqFz0GA0$%PEez3@QFhrkNP zAuvV#TGJPoazEr{TAAgkKpC9EmzOT6P~@#DuSJ zUAwN0ePiq~9H(A1?WlXnFzSMFu>5&1Gvi%VuLMjIY_sPYVGiO`^5AHhE<`36}Q zS#8*4Tt){zOv#6s0b?jxZ==?^v(ltY=s@91lKeUijNJuxx0B@W<0RRA0^~jeuY!!< z*#T<5Y2VIll}EtTZQ#Z0%x2vKUfuy_K6TB|l>Z~PO>MP+pU;5FHQ>ZspmZbc8-2o$ zryqb7_Nx8{c<>N7<1+X9h9@HB$WwFjZhIlIc)`9T z6kgJL@)8%N^RTLn0liQ+`%UH?s%V)+x7mhM3JvQ>aRNJcNX=y?XE}2!cN#o<;Pc=tauOPS24s{WiA%d1_AHZ7(DiFOZT@ z1~9EBFYiTZJFF^Wz(S#J_M6N(Qqe4Z1=LwlA5GSi`m##ox9j~=340;B^S{69u$O-T DuU)5R literal 0 HcmV?d00001 diff --git a/Tests/images/psd-oob-write.psd b/Tests/images/psd-oob-write.psd new file mode 100644 index 0000000000000000000000000000000000000000..65a4472cf263a94277952c06903709afb0c8213f GIT binary patch literal 37212 zcmeI!I|{-;5CG8e2f;Js6jo_XXCVk)LDH$<2|S2L%6V+#=3`?OM1sW|nCvc@* zMR_>JEc#fa;nZao?RL4W`O0t5&U7$GptyKKZoln@|5fB*pk1ildPmiYor z3jqQI2oNCfHv$oNL4W`O0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ t009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D&I~yZ}A8uQLDu literal 0 HcmV?d00001 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/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/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; From 5158d98c807e719c5938aa3886913ef0ea6814e9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 11 Feb 2026 12:13:25 +1100 Subject: [PATCH 3/3] 12.1.1 version bump --- src/PIL/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"