mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-10-15 06:17:16 +00:00
python3-pillow: fix CVE-2026-42311
Details: https://nvd.nist.gov/vuln/detail/CVE-2026-42311 Pick patch from [1] & [2] also mentioned at Debian report in [3] [1] https://github.com/python-pillow/Pillow/commit/58f9a1d166dcb0c274807d4423522d205b0c35ea [2] https://github.com/python-pillow/Pillow/commit/3b1f70da [3] https://security-tracker.debian.org/tracker/CVE-2026-42311 Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
1 parent
90e88f77ab
commit
3e40d728ae
2 files changed
+357
No files matched your search
@@ -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"
|
||||
|
||||
|
||||
Reference in new issue
Block a user