diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 103d915c0..3a206e269 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,6 +29,7 @@ concurrency: env: COVERAGE_CORE: sysmon FORCE_COLOR: 1 + PIP_DISABLE_PIP_VERSION_CHECK: 1 jobs: build: diff --git a/Tests/images/psd-oob-write-x.psd b/Tests/images/psd-oob-write-x.psd new file mode 100644 index 000000000..86359f4cb 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 000000000..73498266a 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 000000000..65a4472cf 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 8f2ca58a6..8a2636dfe 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -182,3 +182,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 8a0abbd39..6656ee506 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -163,6 +163,13 @@ class TestImageFile: 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/installation/platform-support.rst b/docs/installation/platform-support.rst index 0789b02b7..7a8707b9a 100644 --- a/docs/installation/platform-support.rst +++ b/docs/installation/platform-support.rst @@ -75,7 +75,7 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+=============================+==================+==============+ -| macOS 26 Tahoe | 3.10, 3.11, 3.12, 3.13, 3.14| 12.0.0 |arm | +| macOS 26 Tahoe | 3.10, 3.11, 3.12, 3.13, 3.14| 12.1.1 |arm | | +-----------------------------+------------------+ | | | 3.9 | 11.3.0 | | +----------------------------------+-----------------------------+------------------+--------------+ diff --git a/docs/releasenotes/12.1.1.rst b/docs/releasenotes/12.1.1.rst new file mode 100644 index 000000000..86083b4ad --- /dev/null +++ b/docs/releasenotes/12.1.1.rst @@ -0,0 +1,24 @@ +12.1.1 +------ + +Security +======== + +:cve:`2026-25990`: 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 4b25bb6a2..690be2072 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/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index 15f712908..232adf3d3 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -210,8 +210,8 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: # # -------------------------------------------------------------------- -Image.register_save("Palm", _save) +Image.register_save("PALM", _save) -Image.register_extension("Palm", ".palm") +Image.register_extension("PALM", ".palm") -Image.register_mime("Palm", "image/palm") +Image.register_mime("PALM", "image/palm") diff --git a/src/decode.c b/src/decode.c index 051623ed4..7ec461c0e 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 513309c8d..06e4a0893 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;