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] Fix OOB Write with invalid tile extents (#9427) Co-authored-by: Eric Soroos CVE: CVE-2026-25990 Upstream-Status: Backport [https://github.com/python-pillow/Pillow/commit/9000313cc5d4a31bdcdd6d7f0781101abab553aa] Signed-off-by: Hitendra Prajapati --- Tests/test_file_psd.py | 17 +++++++++++++++++ Tests/test_imagefile.py | 7 +++++++ src/decode.c | 3 ++- src/encode.c | 3 ++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 484a1be8f..1a98daffe 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -167,3 +167,20 @@ def test_crashes(test_file: str, raises) -> None: with pytest.raises(raises): with Image.open(f): pass + + +@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 ddcae80d6..8aa102729 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -135,6 +135,13 @@ class TestImageFile: with pytest.raises(OSError): p.close() + @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/src/decode.c b/src/decode.c index ea2f3af80..43fa0ae3e 100644 --- a/src/decode.c +++ b/src/decode.c @@ -185,7 +185,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 c7dd51015..87426cdec 100644 --- a/src/encode.c +++ b/src/encode.c @@ -250,7 +250,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; -- 2.50.1