new file mode 100644
@@ -0,0 +1,356 @@
+From 4bada07dc6c24319edd1eb76f1dd28d968d58207 Mon Sep 17 00:00:00 2001
+From: Andrew Murray <radarhere@users.noreply.github.com>
+Date: Wed, 18 Feb 2026 22:24:03 +1100
+Subject: [PATCH] Avoid overflow by not adding extents together
+
+Reference : https://security-tracker.debian.org/tracker/DSA-6357-1
+
+CVE: CVE-2026-42311
+Upstream-Status: Backport [https://github.com/python-pillow/Pillow/commit/58f9a1d166dcb0c274807d4423522d205b0c35ea]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ Tests/test_imagefile.py | 70 +++++++++++++++++++++++++++++++++++++++--
+ src/PIL/Image.py | 4 +--
+ src/PIL/ImageFile.py | 18 ++++-------
+ src/decode.c | 61 +++++++++++++++++++++++------------
+ src/encode.c | 60 ++++++++++++++++++++++-------------
+ 5 files changed, 156 insertions(+), 57 deletions(-)
+
+diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py
+index 8aa102729..a00f111e1 100644
+--- a/Tests/test_imagefile.py
++++ b/Tests/test_imagefile.py
+@@ -142,6 +142,27 @@ class TestImageFile:
+ with pytest.raises(SystemError, match="tile cannot extend outside image"):
+ ImageFile._save(im, fp, [ImageFile._Tile("raw", xy + (1, 1), 0, "1")])
+
++ def test_extents_none(self) -> None:
++ with Image.open("Tests/images/hopper.jpg") as im:
++ im.tile = [im.tile[0]._replace(extents=None)]
++ im.load()
++
++ for extents in ("invalid", (0,), ("0", "0", "0", "0")):
++ with Image.open("Tests/images/hopper.jpg") as im:
++ im.tile = [im.tile[0]._replace(extents=extents)] # type: ignore[arg-type]
++ with pytest.raises(ValueError, match="invalid extents"):
++ im.load()
++
++ im2 = Image.new("L", (1, 1))
++ fp = BytesIO()
++ tile = ImageFile._Tile("jpeg", None, 0, "L")
++ ImageFile._save(im2, fp, [tile])
++
++ for extents in ("invalid", (0,), ("0", "0", "0", "0")):
++ tile = tile._replace(extents=extents) # type: ignore[arg-type]
++ with pytest.raises(ValueError, match="invalid extents"):
++ ImageFile._save(im2, fp, [tile])
++
+ def test_no_format(self) -> None:
+ buf = BytesIO(b"\x00" * 255)
+
+@@ -279,7 +300,20 @@ class TestPyDecoder(CodecsTest):
+ assert MockPyDecoder.last.state.xsize == 200
+ assert MockPyDecoder.last.state.ysize == 200
+
+- def test_negsize(self) -> None:
++ def test_negative_offset(self) -> None:
++ buf = BytesIO(b"\x00" * 255)
++
++ im = MockImageFile(buf)
++ im.tile = [ImageFile._Tile("MOCK", (-10, yoff, xsize, ysize), 32, None)]
++
++ with pytest.raises(ValueError):
++ im.load()
++
++ im.tile = [ImageFile._Tile("MOCK", (xoff, -10, xsize, ysize), 32, None)]
++ with pytest.raises(ValueError):
++ im.load()
++
++ def test_negative_size(self) -> None:
+ buf = BytesIO(b"\x00" * 255)
+
+ im = MockImageFile(buf)
+@@ -341,7 +375,39 @@ class TestPyEncoder(CodecsTest):
+ assert MockPyEncoder.last.state.xsize == 200
+ assert MockPyEncoder.last.state.ysize == 200
+
+- def test_negsize(self) -> None:
++ def test_negative_offset(self) -> None:
++ buf = BytesIO(b"\x00" * 255)
++
++ im = MockImageFile(buf)
++
++ fp = BytesIO()
++ MockPyEncoder.last = None
++ with pytest.raises(ValueError):
++ ImageFile._save(
++ im,
++ fp,
++ [
++ ImageFile._Tile(
++ "MOCK", (-10, yoff, xoff + xsize, yoff + ysize), 0, "RGB"
++ )
++ ],
++ )
++ last: MockPyEncoder | None = MockPyEncoder.last
++ assert last
++ assert last.cleanup_called
++
++ with pytest.raises(ValueError):
++ ImageFile._save(
++ im,
++ fp,
++ [
++ ImageFile._Tile(
++ "MOCK", (xoff, -10, xoff + xsize, yoff + ysize), 0, "RGB"
++ )
++ ],
++ )
++
++ def test_negative_size(self) -> None:
+ buf = BytesIO(b"\x00" * 255)
+
+ im = MockImageFile(buf)
+diff --git a/src/PIL/Image.py b/src/PIL/Image.py
+index baef0aa11..94e021a48 100644
+--- a/src/PIL/Image.py
++++ b/src/PIL/Image.py
+@@ -759,7 +759,7 @@ class Image:
+
+ # unpack data
+ e = _getencoder(self.mode, encoder_name, args)
+- e.setimage(self.im)
++ e.setimage(self.im, (0, 0) + self.size)
+
+ bufsize = max(65536, self.size[0] * 4) # see RawEncode.c
+
+@@ -822,7 +822,7 @@ class Image:
+
+ # unpack data
+ d = _getdecoder(self.mode, decoder_name, args)
+- d.setimage(self.im)
++ d.setimage(self.im, (0, 0) + self.size)
+ s = d.decode(data)
+
+ if s[0] >= 0:
+diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py
+index 0283fa2fd..ac14d53ed 100644
+--- a/src/PIL/ImageFile.py
++++ b/src/PIL/ImageFile.py
+@@ -666,28 +666,22 @@ class PyCodec:
+
+ if extents:
+ (x0, y0, x1, y1) = extents
+- else:
+- (x0, y0, x1, y1) = (0, 0, 0, 0)
+
+- if x0 == 0 and x1 == 0:
+- self.state.xsize, self.state.ysize = self.im.size
+- else:
++ if x0 < 0 or y0 < 0 or x1 > self.im.size[0] or y1 > self.im.size[1]:
++ msg = "Tile cannot extend outside image"
++ raise ValueError(msg)
++
+ self.state.xoff = x0
+ self.state.yoff = y0
+ self.state.xsize = x1 - x0
+ self.state.ysize = y1 - y0
++ else:
++ self.state.xsize, self.state.ysize = self.im.size
+
+ if self.state.xsize <= 0 or self.state.ysize <= 0:
+ msg = "Size cannot be negative"
+ raise ValueError(msg)
+
+- if (
+- self.state.xsize + self.state.xoff > self.im.size[0]
+- or self.state.ysize + self.state.yoff > self.im.size[1]
+- ):
+- msg = "Tile cannot extend outside image"
+- raise ValueError(msg)
+-
+
+ class PyDecoder(PyCodec):
+ """
+diff --git a/src/decode.c b/src/decode.c
+index 43fa0ae3e..2b12a29bf 100644
+--- a/src/decode.c
++++ b/src/decode.c
+@@ -154,44 +154,65 @@ PyImaging_AsImaging(PyObject *op);
+
+ static PyObject *
+ _setimage(ImagingDecoderObject *decoder, PyObject *args) {
+- PyObject *op;
++ PyObject *op, *extents;
+ Imaging im;
+ ImagingCodecState state;
+ int x0, y0, x1, y1;
+
+- x0 = y0 = x1 = y1 = 0;
+-
+ /* FIXME: should publish the ImagingType descriptor */
+- if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) {
++ if (!PyArg_ParseTuple(args, "OO", &op, &extents)) {
+ return NULL;
+ }
+ im = PyImaging_AsImaging(op);
+ if (!im) {
+ return NULL;
+ }
+-
+- decoder->im = im;
+-
+- state = &decoder->state;
+-
+- /* Setup decoding tile extent */
+- if (x0 == 0 && x1 == 0) {
+- state->xsize = im->xsize;
+- state->ysize = im->ysize;
++ if (extents == Py_None) {
++ x0 = 0;
++ y0 = 0;
++ x1 = im->xsize;
++ y1 = im->ysize;
+ } else {
+- state->xoff = x0;
+- state->yoff = y0;
+- state->xsize = x1 - x0;
+- state->ysize = y1 - y0;
++ if (!PyTuple_Check(extents) || PyTuple_GET_SIZE(extents) != 4) {
++ PyErr_SetString(PyExc_ValueError, "invalid extents");
++ return NULL;
++ }
++ for (int i = 0; i < 4; i++) {
++ PyObject *extent = PyTuple_GetItem(extents, i);
++ if (!PyLong_Check(extent)) {
++ PyErr_SetString(PyExc_ValueError, "invalid extents");
++ return NULL;
++ }
++ int e = (int)PyLong_AsLong(extent);
++
++ if (i == 0) {
++ x0 = e;
++ } else if (i == 1) {
++ y0 = e;
++ } else if (i == 2) {
++ x1 = e;
++ } else {
++ y1 = e;
++ }
++ }
+ }
+
+- 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) {
++ if (x0 < 0 || y0 < 0 || x1 <= x0 || y1 <= y0 || x1 > (int)im->xsize ||
++ y1 > (int)im->ysize) {
+ PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image");
+ return NULL;
+ }
+
++ decoder->im = im;
++
++ state = &decoder->state;
++
++ /* Setup decoding tile extent */
++ state->xoff = x0;
++ state->yoff = y0;
++ state->xsize = x1 - x0;
++ state->ysize = y1 - y0;
++
+ /* Allocate memory buffer (if bits field is set) */
+ if (state->bits > 0) {
+ if (!state->bytes) {
+diff --git a/src/encode.c b/src/encode.c
+index 87426cdec..360f26f97 100644
+--- a/src/encode.c
++++ b/src/encode.c
+@@ -218,45 +218,63 @@ PyImaging_AsImaging(PyObject *op);
+
+ static PyObject *
+ _setimage(ImagingEncoderObject *encoder, PyObject *args) {
+- PyObject *op;
++ PyObject *op, *extents;
+ Imaging im;
+ ImagingCodecState state;
+ Py_ssize_t x0, y0, x1, y1;
+
+- /* Define where image data should be stored */
+-
+- x0 = y0 = x1 = y1 = 0;
+-
+ /* FIXME: should publish the ImagingType descriptor */
+- if (!PyArg_ParseTuple(args, "O|(nnnn)", &op, &x0, &y0, &x1, &y1)) {
++ if (!PyArg_ParseTuple(args, "OO", &op, &extents)) {
+ return NULL;
+ }
+ im = PyImaging_AsImaging(op);
+ if (!im) {
+ return NULL;
+ }
+-
+- encoder->im = im;
+-
+- state = &encoder->state;
+-
+- if (x0 == 0 && x1 == 0) {
+- state->xsize = im->xsize;
+- state->ysize = im->ysize;
++ if (extents == Py_None) {
++ x0 = 0;
++ y0 = 0;
++ x1 = im->xsize;
++ y1 = im->ysize;
+ } else {
+- state->xoff = x0;
+- state->yoff = y0;
+- state->xsize = x1 - x0;
+- state->ysize = y1 - y0;
++ if (!PyTuple_Check(extents) || PyTuple_GET_SIZE(extents) != 4) {
++ PyErr_SetString(PyExc_ValueError, "invalid extents");
++ return NULL;
++ }
++ for (int i = 0; i < 4; i++) {
++ PyObject *extent = PyTuple_GetItem(extents, i);
++ if (!PyLong_Check(extent)) {
++ PyErr_SetString(PyExc_ValueError, "invalid extents");
++ return NULL;
++ }
++ Py_ssize_t e = (Py_ssize_t)PyLong_AsLong(extent);
++
++ if (i == 0) {
++ x0 = e;
++ } else if (i == 1) {
++ y0 = e;
++ } else if (i == 2) {
++ x1 = e;
++ } else {
++ y1 = e;
++ }
++ }
+ }
+
+- 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) {
++ if (x0 < 0 || y0 < 0 || x1 <= x0 || y1 <= y0 || x1 > im->xsize || y1 > im->ysize) {
+ PyErr_SetString(PyExc_SystemError, "tile cannot extend outside image");
+ return NULL;
+ }
+
++ encoder->im = im;
++
++ state = &encoder->state;
++
++ state->xoff = x0;
++ state->yoff = y0;
++ state->xsize = x1 - x0;
++ state->ysize = y1 - y0;
++
+ /* Allocate memory buffer (if bits field is set) */
+ if (state->bits > 0) {
+ if (state->xsize > ((INT_MAX / state->bits) - 7)) {
+--
+2.50.1
+
@@ -10,6 +10,7 @@ SRC_URI = "git://github.com/python-pillow/Pillow.git;branch=main;protocol=https
file://run-ptest \
file://CVE-2026-25990.patch \
file://CVE-2026-40192.patch \
+ file://CVE-2026-42311.patch \
"
SRCREV = "5c89d88eee199ba53f64581ea39b6a1bc52feb1a"
Details: https://nvd.nist.gov/vuln/detail/CVE-2026-42311 Pick patch from [1] also mentioned at Debian report in [2] [1] https://github.com/python-pillow/Pillow/commit/58f9a1d166dcb0c274807d4423522d205b0c35ea [2] https://security-tracker.debian.org/tracker/CVE-2026-42311 Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> --- .../python3-pillow/CVE-2026-42311.patch | 356 ++++++++++++++++++ .../python/python3-pillow_10.3.0.bb | 1 + 2 files changed, 357 insertions(+) create mode 100644 meta-python/recipes-devtools/python/python3-pillow/CVE-2026-42311.patch