mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-10-21 20:19:33 +00:00
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>
357 lines
11 KiB
Diff
357 lines
11 KiB
Diff
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
|
|
|