Compare commits

...

No commits in common. 'epel9' and 'c8-beta' have entirely different histories.

72
.gitignore vendored

@ -1,71 +1 @@
/python-imaging-Pillow-1.7.8-137-g7866759.tar.gz
/python-imaging-Pillow-2.0.0-11-g2f4207c.tar.gz
/python-imaging-Pillow-2.0.0-26-g2e88848.tar.gz
/python-imaging-Pillow-2.0.0-28-gde210a2.tar.gz
/python-imaging-Pillow-2.0.0-58-g93a488e.tar.gz
/python-imaging-Pillow-2.0.0-105-gd1c6db8.tar.gz
/python-imaging-Pillow-2.1.0-0-g75af7e0.tar.gz
/python-imaging-Pillow-2.2.1-0-g3c2496e.tar.gz
/python-imaging-Pillow-2.3.0-0-gb1b88cf.tar.gz
/python-imaging-Pillow-2.3.1-0-gb8d4895.tar.gz
/python-imaging-Pillow-2.4.0-0-g72de37c.tar.gz
/python-pillow-Pillow-2.5.0-0-g80d6137.tar.gz
/python-pillow-Pillow-2.5.1-0-g1ab78b8.tar.gz
/python-pillow-Pillow-2.5.2-0-g4081f9f.tar.gz
/python-pillow-Pillow-2.5.3-0-g68c6904.tar.gz
/python-pillow-Pillow-2.6.0-0-g9634e43.tar.gz
/python-pillow-Pillow-2.6.1-0-g4a8471d.tar.gz
/python-pillow-Pillow-2.7.0-0-g0f05eb2.tar.gz
/python-pillow-Pillow-2.8.0-0-gd754598.tar.gz
/python-pillow-Pillow-2.8.1-0-g3f09b8f.tar.gz
/python-pillow-Pillow-2.8.2-0-g0222a05.tar.gz
/python-pillow-Pillow-2.9.0-0-g80672b6.tar.gz
/python-pillow-Pillow-3.0.0-0-g0177cce.tar.gz
/python-pillow-Pillow-3.1.0-0-gfff5536.tar.gz
/python-pillow-Pillow-3.1.1-0-geb72bf8.tar.gz
/python-pillow-Pillow-3.2.0-0-g344cb70.tar.gz
/Pillow-3.3.0.tar.gz
/Pillow-3.3.1.tar.gz
/Pillow-3.4.0.tar.gz
/Pillow-3.4.1.tar.gz
/Pillow-3.4.2.tar.gz
/Pillow-4.0.0.tar.gz
/Pillow-4.1.0.tar.gz
/Pillow-4.1.1.tar.gz
/Pillow-4.2.0.tar.gz
/Pillow-4.2.1.tar.gz
/Pillow-4.3.0.tar.gz
/Pillow-5.0.0.tar.gz
/Pillow-5.1.0.tar.gz
/Pillow-5.1.1.tar.gz
/Pillow-5.2.0.tar.gz
/Pillow-5.3.0.tar.gz
/Pillow-5.4.1.tar.gz
/Pillow-6.0.0.tar.gz
/Pillow-6.1.0.tar.gz
/Pillow-6.2.0.tar.gz
/python-pillow-6.2.1-1.fc32.src.rpm
/Pillow-6.2.1.tar.gz
/Pillow-7.0.0.tar.gz
/Pillow-7.1.1.tar.gz
/Pillow-7.1.2.tar.gz
/Pillow-7.2.0.tar.gz
/Pillow-8.0.0.tar.gz
/Pillow-8.0.1.tar.gz
/Pillow-8.1.0.tar.gz
/Pillow-8.1.1.tar.gz
/Pillow-8.1.2.tar.gz
/Pillow-8.2.0.tar.gz
/Pillow-8.3.1.tar.gz
/Pillow-8.3.2.tar.gz
/Pillow-8.4.0.tar.gz
/Pillow-9.0.0.tar.gz
/Pillow-9.0.1.tar.gz
/Pillow-9.1.0.tar.gz
/Pillow-9.1.1.tar.gz
/Pillow-9.2.0.tar.gz
/Pillow-9.3.0.tar.gz
/Pillow-9.4.0.tar.gz
/Pillow-9.5.0.tar.gz
/Pillow-10.0.0.tar.gz
/Pillow-10.0.1.tar.gz
SOURCES/Pillow-5.1.1.tar.gz

@ -0,0 +1 @@
2120f1bbf8d39cb0f1a5a50fb79b993e8b75851c SOURCES/Pillow-5.1.1.tar.gz

@ -0,0 +1,42 @@
From b78ede45a294b567d27d7198ff3354df86a5b7f1 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Tue, 11 Sep 2018 15:58:31 +0200
Subject: [PATCH 1/2] Fix potential un-terminated buffer problem (CWE-120)
---
src/libImaging/Histo.c | 4 +++-
src/libImaging/Palette.c | 3 ++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c
index 0bfc8dfe..2b35873e 100644
--- a/src/libImaging/Histo.c
+++ b/src/libImaging/Histo.c
@@ -41,7 +41,9 @@ ImagingHistogramNew(Imaging im)
/* Create histogram descriptor */
h = calloc(1, sizeof(struct ImagingHistogramInstance));
- strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH);
+ strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH-1);
+ h->mode[IMAGING_MODE_LENGTH-1] = 0;
+
h->bands = im->bands;
h->histogram = calloc(im->pixelsize, 256 * sizeof(long));
diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c
index 31c2c024..7aee6e8e 100644
--- a/src/libImaging/Palette.c
+++ b/src/libImaging/Palette.c
@@ -37,7 +37,8 @@ ImagingPaletteNew(const char* mode)
if (!palette)
return (ImagingPalette) ImagingError_MemoryError();
- strncpy(palette->mode, mode, IMAGING_MODE_LENGTH);
+ strncpy(palette->mode, mode, IMAGING_MODE_LENGTH-1);
+ palette->mode[IMAGING_MODE_LENGTH-1] = 0;
/* Initialize to ramp */
for (i = 0; i < 256; i++) {
--
2.17.1

@ -0,0 +1,258 @@
From 6adac809e96c8bfeb50a3bd14570a8118bcd5d65 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Thu, 13 Sep 2018 12:22:11 +0200
Subject: [PATCH 2/2] Fix potential leaked storage issues (CWE-772)
---
src/Tk/tkImaging.c | 2 ++
src/_imaging.c | 15 +++++++++++++--
src/encode.c | 12 +++++++++---
src/libImaging/Histo.c | 12 +++++++++---
src/libImaging/Quant.c | 2 ++
src/libImaging/QuantOctree.c | 1 +
src/libImaging/Resample.c | 2 ++
src/path.c | 8 ++++++--
8 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c
index f448be16..10090b0e 100644
--- a/src/Tk/tkImaging.c
+++ b/src/Tk/tkImaging.c
@@ -442,6 +442,7 @@ int load_tkinter_funcs(void)
/* Try loading from the main program namespace first */
main_program = dlopen(NULL, RTLD_LAZY);
if (_func_loader(main_program) == 0) {
+ dlclose(main_program);
return 0;
}
/* Clear exception triggered when we didn't find symbols above */
@@ -470,6 +471,7 @@ int load_tkinter_funcs(void)
/* dlclose probably safe because tkinter has been imported. */
dlclose(tkinter_lib);
exit:
+ dlclose(main_program);
Py_XDECREF(pModule);
Py_XDECREF(pString);
return ret;
diff --git a/src/_imaging.c b/src/_imaging.c
index 11f5f6ea..445470bf 100644
--- a/src/_imaging.c
+++ b/src/_imaging.c
@@ -856,8 +856,10 @@ _gaussian_blur(ImagingObject* self, PyObject* args)
if (!imOut)
return NULL;
- if (!ImagingGaussianBlur(imOut, imIn, radius, passes))
+ if (!ImagingGaussianBlur(imOut, imIn, radius, passes)) {
+ ImagingDelete(imOut);
return NULL;
+ }
return PyImagingNew(imOut);
}
@@ -1745,8 +1747,10 @@ _box_blur(ImagingObject* self, PyObject* args)
if (!imOut)
return NULL;
- if (!ImagingBoxBlur(imOut, imIn, radius, n))
+ if (!ImagingBoxBlur(imOut, imIn, radius, n)) {
+ ImagingDelete(imOut);
return NULL;
+ }
return PyImagingNew(imOut);
}
@@ -2386,6 +2390,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
return NULL;
}
@@ -2423,6 +2428,7 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 1 coordinate"
);
+ free(xy);
return NULL;
}
@@ -2458,6 +2464,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
return NULL;
}
@@ -2493,6 +2500,7 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
return NULL;
}
@@ -2674,6 +2682,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
return NULL;
}
@@ -2712,6 +2721,7 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args)
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain at least 2 coordinates"
);
+ free(xy);
return NULL;
}
@@ -2754,6 +2764,7 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
return NULL;
}
diff --git a/src/encode.c b/src/encode.c
index ae4277c0..9f7c6592 100644
--- a/src/encode.c
+++ b/src/encode.c
@@ -552,11 +552,15 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
dictionary = NULL;
encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE));
- if (encoder == NULL)
+ if (encoder == NULL) {
+ free(dictionary);
return NULL;
+ }
- if (get_packer(encoder, mode, rawmode) < 0)
+ if (get_packer(encoder, mode, rawmode) < 0) {
+ free(dictionary);
return NULL;
+ }
encoder->encode = ImagingZipEncode;
encoder->cleanup = ImagingZipEncodeCleanup;
@@ -717,8 +721,10 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
if (rawExif && rawExifLen > 0) {
/* malloc check ok, length is from python parsearg */
char* pp = malloc(rawExifLen); // Freed in JpegEncode, Case 5
- if (!pp)
+ if (!pp) {
+ if (extra) free(extra);
return PyErr_NoMemory();
+ }
memcpy(pp, rawExif, rawExifLen);
rawExif = pp;
} else
diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c
index 2b35873e..b7c1a983 100644
--- a/src/libImaging/Histo.c
+++ b/src/libImaging/Histo.c
@@ -82,8 +82,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
h->histogram[im->image8[y][x]]++;
ImagingSectionLeave(&cookie);
} else { /* yes, we need the braces. C isn't Python! */
- if (im->type != IMAGING_TYPE_UINT8)
+ if (im->type != IMAGING_TYPE_UINT8) {
+ ImagingHistogramDelete(h);
return ImagingError_ModeError();
+ }
ImagingSectionEnter(&cookie);
for (y = 0; y < im->ysize; y++) {
UINT8* in = (UINT8*) im->image32[y];
@@ -122,8 +124,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
ImagingSectionLeave(&cookie);
break;
case IMAGING_TYPE_INT32:
- if (!minmax)
+ if (!minmax) {
+ ImagingHistogramDelete(h);
return ImagingError_ValueError("min/max not given");
+ }
if (!im->xsize || !im->ysize)
break;
imin = ((INT32*) minmax)[0];
@@ -143,8 +147,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
ImagingSectionLeave(&cookie);
break;
case IMAGING_TYPE_FLOAT32:
- if (!minmax)
+ if (!minmax) {
+ ImagingHistogramDelete(h);
return ImagingError_ValueError("min/max not given");
+ }
if (!im->xsize || !im->ysize)
break;
fmin = ((FLOAT32*) minmax)[0];
diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c
index df313816..b94dc6e1 100644
--- a/src/libImaging/Quant.c
+++ b/src/libImaging/Quant.c
@@ -568,6 +568,8 @@ split(BoxNode *node)
left=malloc(sizeof(BoxNode));
right=malloc(sizeof(BoxNode));
if (!left||!right) {
+ free(left);
+ free(right);
return 0;
}
for(i=0;i<3;i++) {
diff --git a/src/libImaging/QuantOctree.c b/src/libImaging/QuantOctree.c
index e18ab3c6..d778c942 100644
--- a/src/libImaging/QuantOctree.c
+++ b/src/libImaging/QuantOctree.c
@@ -470,6 +470,7 @@ error:
free(qp);
free_color_cube(lookupCube);
free_color_cube(coarseLookupCube);
+ free(paletteBuckets);
free(paletteBucketsCoarse);
free(paletteBucketsFine);
free_color_cube(coarseCube);
diff --git a/src/libImaging/Resample.c b/src/libImaging/Resample.c
index cda005d9..b90395e8 100644
--- a/src/libImaging/Resample.c
+++ b/src/libImaging/Resample.c
@@ -538,6 +538,8 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize,
if ( ! ksize_vert) {
free(bounds_horiz);
free(kk_horiz);
+ free(bounds_vert);
+ free(kk_vert);
return NULL;
}
diff --git a/src/path.c b/src/path.c
index b56ea838..5984a3d1 100644
--- a/src/path.c
+++ b/src/path.c
@@ -82,12 +82,16 @@ path_new(Py_ssize_t count, double* xy, int duplicate)
xy = p;
}
- if (PyType_Ready(&PyPathType) < 0)
+ if (PyType_Ready(&PyPathType) < 0) {
+ free(xy);
return NULL;
+ }
path = PyObject_New(PyPathObject, &PyPathType);
- if (path == NULL)
+ if (path == NULL) {
+ free(xy);
return NULL;
+ }
path->count = count;
path->xy = xy;
--
2.17.1

@ -0,0 +1,50 @@
From e705cd1476f04a918aae34f638b502116cb12eba Mon Sep 17 00:00:00 2001
From: Jon Dufresne <jon.dufresne@gmail.com>
Date: Tue, 3 Apr 2018 20:36:09 -0700
Subject: [PATCH] Fix dereferencing type-punned pointer will break
strict-aliasing
Compiler warning appeared as:
src/path.c:574:22: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
Py_TYPE(&item)->tp_name);
^~~~~~~
As item is already of type PyObject*, and the Py_TYPE macro is
equivalent to (((PyObject*)(o))->ob_type), no need for the dereference.
https://docs.python.org/3/c-api/structures.html#c.Py_TYPE
---
Tests/test_imagepath.py | 5 +++++
src/path.c | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py
index 14cc4d14b..98a6d3416 100644
--- a/Tests/test_imagepath.py
+++ b/Tests/test_imagepath.py
@@ -17,6 +17,11 @@ def test_path(self):
self.assertEqual(p[0], (0.0, 1.0))
self.assertEqual(p[-1], (8.0, 9.0))
self.assertEqual(list(p[:1]), [(0.0, 1.0)])
+ with self.assertRaises(TypeError) as cm:
+ p['foo']
+ self.assertEqual(
+ str(cm.exception),
+ "Path indices must be integers, not str")
self.assertEqual(
list(p),
[(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)])
diff --git a/src/path.c b/src/path.c
index b56ea838e..d1c18c8ed 100644
--- a/src/path.c
+++ b/src/path.c
@@ -571,7 +571,7 @@ path_subscript(PyPathObject* self, PyObject* item) {
else {
PyErr_Format(PyExc_TypeError,
"Path indices must be integers, not %.200s",
- Py_TYPE(&item)->tp_name);
+ Py_TYPE(item)->tp_name);
return NULL;
}
}

@ -0,0 +1,56 @@
From f91c78960495efa04c7f12eeb916158d4bfbabc4 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 13 Jul 2020 15:40:11 +0200
Subject: [PATCH] CVE-2020-11538
---
src/libImaging/SgiRleDecode.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c
index 6367ae7..eb8fc84 100644
--- a/src/libImaging/SgiRleDecode.c
+++ b/src/libImaging/SgiRleDecode.c
@@ -28,6 +28,7 @@ static void read4B(UINT32* dest, UINT8* buf)
static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
{
UINT8 pixel, count;
+ int x = 0;
for (;n > 0; n--)
{
@@ -37,9 +38,10 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
count = pixel & RLE_MAX_RUN;
if (!count)
return count;
- if (count > xsize) {
+ if (x + count > xsize) {
return -1;
}
+ x += count;
if (pixel & RLE_COPY_FLAG) {
while(count--) {
*dest = *src++;
@@ -63,6 +65,7 @@ static int expandrow2(UINT16* dest, UINT16* src, int n, int z, int xsize)
{
UINT8 pixel, count;
+ int x = 0;
for (;n > 0; n--)
{
@@ -73,9 +76,10 @@ static int expandrow2(UINT16* dest, UINT16* src, int n, int z, int xsize)
count = pixel & RLE_MAX_RUN;
if (!count)
return count;
- if (count > xsize) {
+ if (x + count > xsize) {
return -1;
}
+ x += count;
if (pixel & RLE_COPY_FLAG) {
while(count--) {
*dest = *src++;
--
2.26.2

@ -0,0 +1,41 @@
From 7a0aea5806d57e0e7c5187fbc9c2937a16e0bca1 Mon Sep 17 00:00:00 2001
From: Eric Soroos <eric-github@soroos.net>
Date: Thu, 17 Dec 2020 00:17:53 +0100
Subject: [PATCH] Fix for CVE CVE-2020-35655 - Read Overflow in PCX Decoding.
* Don't trust the image to specify a buffer size
---
src/PIL/PcxImagePlugin.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py
index 564713a..17bbd18 100644
--- a/src/PIL/PcxImagePlugin.py
+++ b/src/PIL/PcxImagePlugin.py
@@ -63,9 +63,9 @@ class PcxImageFile(ImageFile.ImageFile):
version = i8(s[1])
bits = i8(s[3])
planes = i8(s[65])
- stride = i16(s, 66)
+ ignored_stride = i16(s, 66)
logger.debug("PCX version %s, bits %s, planes %s, stride %s",
- version, bits, planes, stride)
+ version, bits, planes, ignored_stride)
self.info["dpi"] = i16(s, 12), i16(s, 14)
@@ -102,6 +102,11 @@ class PcxImageFile(ImageFile.ImageFile):
self.mode = mode
self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]
+ # don't trust the passed in stride. Calculate for ourselves.
+ # CVE-2020-35653
+ stride = (self.size[0] * bits + 7) // 8
+ stride += stride % 2
+
bbox = (0, 0) + self.size
logger.debug("size: %sx%s", *self.size)
--
2.29.2

@ -0,0 +1,136 @@
From f276de1139ec16395dc8b382860fb58e331fbd53 Mon Sep 17 00:00:00 2001
From: Eric Soroos <eric-github@soroos.net>
Date: Thu, 29 Oct 2020 23:07:15 +0000
Subject: [PATCH 1/2] Fix for SGI Decode buffer overrun CVE-2020-35655
* Independently found by a contributor and sent to Tidelift, and by Google's OSS Fuzz.
---
src/libImaging/SgiRleDecode.c | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c
index eb8fc84..c256169 100644
--- a/src/libImaging/SgiRleDecode.c
+++ b/src/libImaging/SgiRleDecode.c
@@ -107,11 +107,27 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
int err = 0;
int status;
+ /* size check */
+ if (im->xsize > INT_MAX / im->bands ||
+ im->ysize > INT_MAX / im->bands) {
+ return IMAGING_CODEC_MEMORY;
+ }
+
/* Get all data from File descriptor */
c = (SGISTATE*)state->context;
_imaging_seek_pyFd(state->fd, 0L, SEEK_END);
c->bufsize = _imaging_tell_pyFd(state->fd);
c->bufsize -= SGI_HEADER_SIZE;
+
+ c->tablen = im->bands * im->ysize;
+ /* below, we populate the starttab and lentab into the bufsize,
+ each with 4 bytes per element of tablen
+ Check here before we allocate any memory
+ */
+ if (c->bufsize < 8*c->tablen) {
+ return IMAGING_CODEC_MEMORY;
+ }
+
ptr = malloc(sizeof(UINT8) * c->bufsize);
if (!ptr) {
return IMAGING_CODEC_MEMORY;
@@ -129,18 +145,11 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
state->ystep = 1;
}
- if (im->xsize > INT_MAX / im->bands ||
- im->ysize > INT_MAX / im->bands) {
- err = IMAGING_CODEC_MEMORY;
- goto sgi_finish_decode;
- }
-
/* Allocate memory for RLE tables and rows */
free(state->buffer);
state->buffer = NULL;
/* malloc overflow check above */
state->buffer = calloc(im->xsize * im->bands, sizeof(UINT8) * 2);
- c->tablen = im->bands * im->ysize;
c->starttab = calloc(c->tablen, sizeof(UINT32));
c->lengthtab = calloc(c->tablen, sizeof(UINT32));
if (!state->buffer ||
--
2.29.2
From 18aa14484fa63dabcafea63cf0b7bfb4066e979c Mon Sep 17 00:00:00 2001
From: Eric Soroos <eric-github@soroos.net>
Date: Fri, 30 Oct 2020 09:57:23 +0000
Subject: [PATCH 2/2] Make the SGI code return -1 as an error flag, error in
state
---
src/libImaging/SgiRleDecode.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c
index c256169..2259159 100644
--- a/src/libImaging/SgiRleDecode.c
+++ b/src/libImaging/SgiRleDecode.c
@@ -110,7 +110,8 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
/* size check */
if (im->xsize > INT_MAX / im->bands ||
im->ysize > INT_MAX / im->bands) {
- return IMAGING_CODEC_MEMORY;
+ state->errcode = IMAGING_CODEC_MEMORY;
+ return -1;
}
/* Get all data from File descriptor */
@@ -125,12 +126,14 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
Check here before we allocate any memory
*/
if (c->bufsize < 8*c->tablen) {
- return IMAGING_CODEC_MEMORY;
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
}
ptr = malloc(sizeof(UINT8) * c->bufsize);
if (!ptr) {
- return IMAGING_CODEC_MEMORY;
+ state->errcode = IMAGING_CODEC_MEMORY;
+ return -1;
}
_imaging_seek_pyFd(state->fd, SGI_HEADER_SIZE, SEEK_SET);
_imaging_read_pyFd(state->fd, (char*)ptr, c->bufsize);
@@ -178,7 +181,7 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
if (c->rleoffset + c->rlelength > c->bufsize) {
state->errcode = IMAGING_CODEC_OVERRUN;
- return -1;
+ goto sgi_finish_decode;
}
/* row decompression */
@@ -190,7 +193,7 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
}
if (status == -1) {
state->errcode = IMAGING_CODEC_OVERRUN;
- return -1;
+ goto sgi_finish_decode;
} else if (status == 1) {
goto sgi_finish_decode;
}
@@ -211,7 +214,8 @@ sgi_finish_decode: ;
free(c->lengthtab);
free(ptr);
if (err != 0){
- return err;
+ state->errcode=err;
+ return -1;
}
return state->count - c->bufsize;
}
--
2.29.2

@ -0,0 +1,83 @@
From 1dd0fb64bd3cc221b5877ece4ce2f300245b638f Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 17 Feb 2020 14:19:32 +0100
Subject: [PATCH] CVE-2020-5311
---
src/libImaging/SgiRleDecode.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c
index 39e7b3a..6367ae7 100644
--- a/src/libImaging/SgiRleDecode.c
+++ b/src/libImaging/SgiRleDecode.c
@@ -25,7 +25,7 @@ static void read4B(UINT32* dest, UINT8* buf)
*dest = (UINT32)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
}
-static int expandrow(UINT8* dest, UINT8* src, int n, int z)
+static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
{
UINT8 pixel, count;
@@ -37,6 +37,9 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z)
count = pixel & RLE_MAX_RUN;
if (!count)
return count;
+ if (count > xsize) {
+ return -1;
+ }
if (pixel & RLE_COPY_FLAG) {
while(count--) {
*dest = *src++;
@@ -56,7 +59,7 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z)
return 0;
}
-static int expandrow2(UINT16* dest, UINT16* src, int n, int z)
+static int expandrow2(UINT16* dest, UINT16* src, int n, int z, int xsize)
{
UINT8 pixel, count;
@@ -70,6 +73,9 @@ static int expandrow2(UINT16* dest, UINT16* src, int n, int z)
count = pixel & RLE_MAX_RUN;
if (!count)
return count;
+ if (count > xsize) {
+ return -1;
+ }
if (pixel & RLE_COPY_FLAG) {
while(count--) {
*dest = *src++;
@@ -95,6 +101,7 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
UINT8 *ptr;
SGISTATE *c;
int err = 0;
+ int status;
/* Get all data from File descriptor */
c = (SGISTATE*)state->context;
@@ -163,12 +170,16 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
/* row decompression */
if (c->bpc ==1) {
- if(expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands))
- goto sgi_finish_decode;
+ status = expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize);
}
else {
- if(expandrow2((UINT16*)&state->buffer[c->channo * 2], (UINT16*)&ptr[c->rleoffset], c->rlelength, im->bands))
- goto sgi_finish_decode;
+ status = expandrow2(&state->buffer[c->channo * 2], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize);
+ }
+ if (status == -1) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ } else if (status == 1) {
+ goto sgi_finish_decode;
}
state->count += c->rlelength;
--
2.24.1

@ -0,0 +1,183 @@
From cc9658731ba1ea291f83ea959acc27ac28384a2c Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Fri, 14 Feb 2020 11:14:53 +0100
Subject: [PATCH] Combined fix for CVE-2020-5312 and CVE-2019-16865
---
src/PIL/GifImagePlugin.py | 1 +
src/PIL/IcoImagePlugin.py | 1 +
src/PIL/PsdImagePlugin.py | 6 ++++--
src/PIL/TiffImagePlugin.py | 4 ++--
src/libImaging/FliDecode.c | 14 +++++++++++---
src/libImaging/PcxDecode.c | 8 ++++++++
src/libImaging/RawDecode.c | 11 +++++++++--
src/libImaging/SgiRleDecode.c | 5 +++++
8 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py
index c01adff..99af4a5 100644
--- a/src/PIL/GifImagePlugin.py
+++ b/src/PIL/GifImagePlugin.py
@@ -251,6 +251,7 @@ class GifImageFile(ImageFile.ImageFile):
self.dispose = None
elif self.disposal_method == 2:
# replace with background colour
+ Image._decompression_bomb_check(self.size)
self.dispose = Image.core.fill("P", self.size,
self.info["background"])
else:
diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py
index 428fdd4..2b6d1e0 100644
--- a/src/PIL/IcoImagePlugin.py
+++ b/src/PIL/IcoImagePlugin.py
@@ -167,6 +167,7 @@ class IcoFile(object):
else:
# XOR + AND mask bmp frame
im = BmpImagePlugin.DibImageFile(self.buf)
+ Image._decompression_bomb_check(im.size)
# change tile dimension to only encompass XOR image
im.size = (im.size[0], int(im.size[1] / 2))
diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py
index f6e04f7..fe2a2ff 100644
--- a/src/PIL/PsdImagePlugin.py
+++ b/src/PIL/PsdImagePlugin.py
@@ -209,9 +209,11 @@ def _layerinfo(file):
# skip over blend flags and extra information
filler = read(12)
name = ""
- size = i32(read(4))
+ size = i32(read(4)) # length of the extra data field
combined = 0
if size:
+ data_end = file.tell() + size
+
length = i32(read(4))
if length:
mask_y = i32(read(4))
@@ -233,7 +235,7 @@ def _layerinfo(file):
name = read(length).decode('latin-1', 'replace')
combined += length + 1
- file.seek(size - combined, 1)
+ file.seek(data_end)
layers.append((name, mode, (x0, y0, x1, y1)))
# get tiles
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
index f903918..b9a1ef7 100644
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -1170,8 +1170,8 @@ class TiffImageFile(ImageFile.ImageFile):
print("- fill_order:", fillorder)
# size
- xsize = self.tag_v2.get(IMAGEWIDTH)
- ysize = self.tag_v2.get(IMAGELENGTH)
+ xsize = int(self.tag_v2.get(IMAGEWIDTH))
+ ysize = int(self.tag_v2.get(IMAGELENGTH))
self.size = xsize, ysize
if DEBUG:
diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c
index 6d22c6c..a99aca8 100644
--- a/src/libImaging/FliDecode.c
+++ b/src/libImaging/FliDecode.c
@@ -30,7 +30,7 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
UINT8* ptr;
int framesize;
- int c, chunks;
+ int c, chunks, advance;
int l, lines;
int i, j, x = 0, y, ymax;
@@ -59,10 +59,16 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
chunks = I16(ptr+6);
ptr += 16;
+ bytes -= 16;
/* Process subchunks */
for (c = 0; c < chunks; c++) {
- UINT8 *data = ptr + 6;
+ UINT8* data;
+ if (bytes < 10) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+ data = ptr + 6;
switch (I16(ptr+4)) {
case 4: case 11:
/* FLI COLOR chunk */
@@ -198,7 +204,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
state->errcode = IMAGING_CODEC_UNKNOWN;
return -1;
}
- ptr += I32(ptr);
+ advance = I32(ptr);
+ ptr += advance;
+ bytes -= advance;
}
return -1; /* end of frame */
diff --git a/src/libImaging/PcxDecode.c b/src/libImaging/PcxDecode.c
index e5417f1..aaf5867 100644
--- a/src/libImaging/PcxDecode.c
+++ b/src/libImaging/PcxDecode.c
@@ -22,6 +22,14 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
UINT8 n;
UINT8* ptr;
+ if (strcmp(im->mode, "1") == 0 && state->xsize > state->bytes * 8) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ } else if (strcmp(im->mode, "P") == 0 && state->xsize > state->bytes) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
ptr = buf;
for (;;) {
diff --git a/src/libImaging/RawDecode.c b/src/libImaging/RawDecode.c
index 40c0cb7..d4b7994 100644
--- a/src/libImaging/RawDecode.c
+++ b/src/libImaging/RawDecode.c
@@ -33,8 +33,15 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* get size of image data and padding */
state->bytes = (state->xsize * state->bits + 7) / 8;
- rawstate->skip = (rawstate->stride) ?
- rawstate->stride - state->bytes : 0;
+ if (rawstate->stride) {
+ rawstate->skip = rawstate->stride - state->bytes;
+ if (rawstate->skip < 0) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+ } else {
+ rawstate->skip = 0;
+ }
/* check image orientation */
if (state->ystep < 0) {
diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c
index 9d8e563..39e7b3a 100644
--- a/src/libImaging/SgiRleDecode.c
+++ b/src/libImaging/SgiRleDecode.c
@@ -156,6 +156,11 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
c->rlelength = c->lengthtab[c->rowno + c->channo * im->ysize];
c->rleoffset -= SGI_HEADER_SIZE;
+ if (c->rleoffset + c->rlelength > c->bufsize) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
/* row decompression */
if (c->bpc ==1) {
if(expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands))
--
2.24.1

@ -0,0 +1,38 @@
From 697957b79a9473f0f643051265885fb048395127 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Wed, 4 Mar 2020 10:54:54 +0100
Subject: [PATCH] CVE-2020-5313
---
src/libImaging/FliDecode.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c
index a99aca8..72ba138 100644
--- a/src/libImaging/FliDecode.c
+++ b/src/libImaging/FliDecode.c
@@ -40,8 +40,7 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
return 0;
/* We don't decode anything unless we have a full chunk in the
- input buffer (on the other hand, the Python part of the driver
- makes sure this is always the case) */
+ input buffer */
ptr = buf;
@@ -52,6 +51,11 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* Make sure this is a frame chunk. The Python driver takes
case of other chunk types. */
+ if (bytes < 8) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
if (I16(ptr+4) != 0xF1FA) {
state->errcode = IMAGING_CODEC_UNKNOWN;
return -1;
--
2.24.1

@ -0,0 +1,130 @@
From 9c781aa2020eef838284dcb348f4528f3c3cc1ab Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 14 Jun 2021 09:06:07 +0200
Subject: [PATCH 1/5] CVE-2021-25287_25288
---
src/libImaging/Jpeg2KDecode.c | 78 +++++++++++++++++++++++++++--------
1 file changed, 61 insertions(+), 17 deletions(-)
diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c
index 9140e00..fdbd0c0 100644
--- a/src/libImaging/Jpeg2KDecode.c
+++ b/src/libImaging/Jpeg2KDecode.c
@@ -110,6 +110,7 @@ j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
if (shift < 0)
offset += 1 << (-shift - 1);
+ /* csiz*h*w + offset = tileinfo.datasize */
switch (csiz) {
case 1:
for (y = 0; y < h; ++y) {
@@ -557,8 +558,10 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
opj_dparameters_t params;
OPJ_COLOR_SPACE color_space;
j2k_unpacker_t unpack = NULL;
- size_t buffer_size = 0;
- unsigned n;
+ size_t buffer_size = 0, tile_bytes = 0;
+ unsigned n, tile_height, tile_width;
+ int total_component_width = 0;
+
stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
@@ -703,8 +706,62 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
tile_info.x1 = (tile_info.x1 + correction) >> context->reduce;
tile_info.y1 = (tile_info.y1 + correction) >> context->reduce;
+ /* Check the tile bounds; if the tile is outside the image area,
+ or if it has a negative width or height (i.e. the coordinates are
+ swapped), bail. */
+ if (tile_info.x0 >= tile_info.x1
+ || tile_info.y0 >= tile_info.y1
+ || tile_info.x0 < image->x0
+ || tile_info.y0 < image->y0
+ || tile_info.x1 - image->x0 > im->xsize
+ || tile_info.y1 - image->y0 > im->ysize) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ state->state = J2K_STATE_FAILED;
+ goto quick_exit;
+ }
+
+ if (tile_info.nb_comps != image->numcomps) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ state->state = J2K_STATE_FAILED;
+ goto quick_exit;
+ }
+
+ /* Sometimes the tile_info.datasize we get back from openjpeg
+ is less than sum(comp_bytes)*w*h, and we overflow in the
+ shuffle stage */
+
+ tile_width = tile_info.x1 - tile_info.x0;
+ tile_height = tile_info.y1 - tile_info.y0;
+
+ /* Total component width = sum (component_width) e.g, it's
+ legal for an la file to have a 1 byte width for l, and 4 for
+ a. and then a malicious file could have a smaller tile_bytes
+ */
+
+ for (n=0; n < tile_info.nb_comps; n++) {
+ // see csize /acsize calcs
+ int csize = (image->comps[n].prec + 7) >> 3;
+ csize = (csize == 3) ? 4 : csize;
+ total_component_width += csize;
+ }
+ if ((tile_width > UINT_MAX / total_component_width) ||
+ (tile_height > UINT_MAX / total_component_width) ||
+ (tile_width > UINT_MAX / (tile_height * total_component_width)) ||
+ (tile_height > UINT_MAX / (tile_width * total_component_width))) {
+
+ state->errcode = IMAGING_CODEC_BROKEN;
+ state->state = J2K_STATE_FAILED;
+ goto quick_exit;
+ }
+
+ tile_bytes = tile_width * tile_height * total_component_width;
+
+ if (tile_bytes > tile_info.data_size) {
+ tile_info.data_size = tile_bytes;
+ }
+
if (buffer_size < tile_info.data_size) {
- /* malloc check ok, tile_info.data_size from openjpeg */
+ /* malloc check ok, overflow and tile size sanity check above */
UINT8 *new = realloc (state->buffer, tile_info.data_size);
if (!new) {
state->errcode = IMAGING_CODEC_MEMORY;
@@ -715,6 +772,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
buffer_size = tile_info.data_size;
}
+
if (!opj_decode_tile_data(codec,
tile_info.tile_index,
(OPJ_BYTE *)state->buffer,
@@ -725,20 +783,6 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
goto quick_exit;
}
- /* Check the tile bounds; if the tile is outside the image area,
- or if it has a negative width or height (i.e. the coordinates are
- swapped), bail. */
- if (tile_info.x0 >= tile_info.x1
- || tile_info.y0 >= tile_info.y1
- || tile_info.x0 < image->x0
- || tile_info.y0 < image->y0
- || tile_info.x1 - image->x0 > im->xsize
- || tile_info.y1 - image->y0 > im->ysize) {
- state->errcode = IMAGING_CODEC_BROKEN;
- state->state = J2K_STATE_FAILED;
- goto quick_exit;
- }
-
unpack(image, &tile_info, state->buffer, im);
}
--
2.31.1

@ -0,0 +1,28 @@
From c558baf01a97aed376a67ff4641f1c3c864ae3f0 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Thu, 8 Apr 2021 17:55:26 +0200
Subject: [PATCH 1/4] CVE-2021-25290
---
src/libImaging/TiffDecode.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c
index f292da3..d17b557 100644
--- a/src/libImaging/TiffDecode.c
+++ b/src/libImaging/TiffDecode.c
@@ -36,6 +36,11 @@ tsize_t _tiffReadProc(thandle_t hdata, tdata_t buf, tsize_t size) {
TRACE(("_tiffReadProc: %d \n", (int)size));
dump_state(state);
+ if (state->loc > state->eof) {
+ TIFFError("_tiffReadProc", "Invalid Read at loc %d, eof: %d", state->loc, state->eof);
+ return 0;
+ }
+
to_read = min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc);
TRACE(("to_read: %d\n", (int)to_read));
--
2.30.2

@ -0,0 +1,27 @@
From f60b6ae79d3c2e759f54bb4acb62b4c49f89fef2 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Thu, 8 Apr 2021 17:59:21 +0200
Subject: [PATCH 2/4] CVE-2021-25292
---
src/PIL/PdfParser.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py
index b6938fd..189aed8 100644
--- a/src/PIL/PdfParser.py
+++ b/src/PIL/PdfParser.py
@@ -562,8 +562,9 @@ class PdfParser:
whitespace_or_hex = br"[\000\011\012\014\015\0400-9a-fA-F]"
whitespace_optional = whitespace + b"*"
whitespace_mandatory = whitespace + b"+"
+ whitespace_optional_no_nl = br"[\000\011\014\015\040]*" # no "\012" aka "\n"
newline_only = br"[\r\n]+"
- newline = whitespace_optional + newline_only + whitespace_optional
+ newline = whitespace_optional_no_nl + newline_only + whitespace_optional_no_nl
re_trailer_end = re.compile(whitespace_mandatory + br"trailer" + whitespace_optional + br"\<\<(.*\>\>)" + newline
+ br"startxref" + newline + br"([0-9]+)" + newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL)
re_trailer_prev = re.compile(whitespace_optional + br"trailer" + whitespace_optional + br"\<\<(.*?\>\>)" + newline
--
2.30.2

@ -0,0 +1,207 @@
From 1312c5426e7dd84e396ef2ff35aa09b64d92d382 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Fri, 9 Apr 2021 19:33:55 +0200
Subject: [PATCH 3/4] CVE-2021-25293
---
src/libImaging/SgiRleDecode.c | 88 +++++++++++++++++++++++++++++------
1 file changed, 75 insertions(+), 13 deletions(-)
diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c
index 2259159..85af456 100644
--- a/src/libImaging/SgiRleDecode.c
+++ b/src/libImaging/SgiRleDecode.c
@@ -25,13 +25,60 @@ static void read4B(UINT32* dest, UINT8* buf)
*dest = (UINT32)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
}
-static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
+/*
+ SgiRleDecoding is done in a single channel row oriented set of RLE chunks.
+
+ * The file is arranged as
+ - SGI Header
+ - Rle Offset Table
+ - Rle Length Table
+ - Scanline Data
+
+ * Each RLE atom is c->bpc bytes wide (1 or 2)
+
+ * Each RLE Chunk is [specifier atom] [ 1 or n data atoms ]
+
+ * Copy Atoms are a byte with the high bit set, and the low 7 are
+ the number of bytes to copy from the source to the
+ destination. e.g.
+
+ CBBBBBBBB or 0CHLHLHLHLHLHL (B=byte, H/L = Hi low bytes)
+
+ * Run atoms do not have the high bit set, and the low 7 bits are
+ the number of copies of the next atom to copy to the
+ destination. e.g.:
+
+ RB -> BBBBB or RHL -> HLHLHLHLHL
+
+ The upshot of this is, there is no way to determine the required
+ length of the input buffer from reloffset and rlelength without
+ going through the data at that scan line.
+
+ Furthermore, there's no requirement that individual scan lines
+ pointed to from the rleoffset table are in any sort of order or
+ used only once, or even disjoint. There's also no requirement that
+ all of the data in the scan line area of the image file be used
+
+ */
+
+static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize, UINT8 *end_of_buffer)
{
+ /*
+ * n here is the number of rlechunks
+ * z is the number of channels, for calculating the interleave
+ * offset to go to RGBA style pixels
+ * xsize is the row width
+ * end_of_buffer is the address of the end of the input buffer
+ */
+
UINT8 pixel, count;
int x = 0;
for (;n > 0; n--)
{
+ if (src > end_of_buffer) {
+ return -1;
+ }
pixel = *src++;
if (n == 1 && pixel != 0)
return n;
@@ -43,6 +90,9 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
}
x += count;
if (pixel & RLE_COPY_FLAG) {
+ if (src + count > end_of_buffer) {
+ return -1;
+ }
while(count--) {
*dest = *src++;
dest += z;
@@ -50,6 +100,9 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
}
else {
+ if (src > end_of_buffer) {
+ return -1;
+ }
pixel = *src++;
while (count--) {
*dest = pixel;
@@ -61,7 +114,7 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
return 0;
}
-static int expandrow2(UINT16* dest, UINT16* src, int n, int z, int xsize)
+static int expandrow2(UINT16* dest, UINT16* src, int n, int z, int xsize, UINT8 *end_of_buffer)
{
UINT8 pixel, count;
@@ -69,6 +122,9 @@ static int expandrow2(UINT16* dest, UINT16* src, int n, int z, int xsize)
for (;n > 0; n--)
{
+ if (src + 1 > end_of_buffer) {
+ return -1;
+ }
pixel = ((UINT8*)src)[1];
++src;
if (n == 1 && pixel != 0)
@@ -81,12 +137,18 @@ static int expandrow2(UINT16* dest, UINT16* src, int n, int z, int xsize)
}
x += count;
if (pixel & RLE_COPY_FLAG) {
+ if (src + 2 * count > end_of_buffer) {
+ return -1;
+ }
while(count--) {
*dest = *src++;
dest += z;
}
}
else {
+ if (src + 2 > end_of_buffer) {
+ return -1;
+ }
while (count--) {
*dest = *src;
dest += z;
@@ -136,8 +198,10 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
return -1;
}
_imaging_seek_pyFd(state->fd, SGI_HEADER_SIZE, SEEK_SET);
- _imaging_read_pyFd(state->fd, (char*)ptr, c->bufsize);
-
+ if (_imaging_read_pyFd(state->fd, (char *)ptr, c->bufsize) != c->bufsize) {
+ state->errcode = IMAGING_CODEC_UNKNOWN;
+ return -1;
+ }
/* decoder initialization */
state->count = 0;
@@ -168,8 +232,6 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
for (c->tabindex = 0, c->bufindex = c->tablen * sizeof(UINT32); c->tabindex < c->tablen; c->tabindex++, c->bufindex+=4)
read4B(&c->lengthtab[c->tabindex], &ptr[c->bufindex]);
- state->count += c->tablen * sizeof(UINT32) * 2;
-
/* read compressed rows */
for (c->rowno = 0; c->rowno < im->ysize; c->rowno++, state->y += state->ystep)
{
@@ -177,19 +239,21 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
{
c->rleoffset = c->starttab[c->rowno + c->channo * im->ysize];
c->rlelength = c->lengthtab[c->rowno + c->channo * im->ysize];
- c->rleoffset -= SGI_HEADER_SIZE;
- if (c->rleoffset + c->rlelength > c->bufsize) {
+ // Check for underflow of rleoffset-SGI_HEADER_SIZE
+ if (c->rleoffset < SGI_HEADER_SIZE) {
state->errcode = IMAGING_CODEC_OVERRUN;
goto sgi_finish_decode;
}
+ c->rleoffset -= SGI_HEADER_SIZE;
+
/* row decompression */
if (c->bpc ==1) {
- status = expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize);
+ status = expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize, &ptr[c->bufsize-1]);
}
else {
- status = expandrow2(&state->buffer[c->channo * 2], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize);
+ status = expandrow2(&state->buffer[c->channo * 2], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize, &ptr[c->bufsize-1]);
}
if (status == -1) {
state->errcode = IMAGING_CODEC_OVERRUN;
@@ -198,7 +262,6 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
goto sgi_finish_decode;
}
- state->count += c->rlelength;
}
/* store decompressed data in image */
@@ -206,7 +269,6 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
}
- c->bufsize++;
sgi_finish_decode: ;
@@ -217,5 +279,5 @@ sgi_finish_decode: ;
state->errcode=err;
return -1;
}
- return state->count - c->bufsize;
+ return 0;
}
--
2.30.2

@ -0,0 +1,58 @@
From 357fef8b4bd076e3a15e7ffc58a475626794c7e3 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Fri, 9 Apr 2021 19:41:58 +0200
Subject: [PATCH 4/4] CVE-2021-27921_27922_27923
---
src/PIL/BlpImagePlugin.py | 1 +
src/PIL/IcnsImagePlugin.py | 2 ++
src/PIL/IcoImagePlugin.py | 1 +
3 files changed, 4 insertions(+)
diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py
index ec358db..d56d46c 100644
--- a/src/PIL/BlpImagePlugin.py
+++ b/src/PIL/BlpImagePlugin.py
@@ -362,6 +362,7 @@ class BLP1Decoder(_BLPBaseDecoder):
data = jpeg_header + data
data = BytesIO(data)
image = JpegImageFile(data)
+ Image._decompression_bomb_check(image.size)
self.tile = image.tile # :/
self.fd = image.fp
self.mode = image.mode
diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py
index b382a73..2292584 100644
--- a/src/PIL/IcnsImagePlugin.py
+++ b/src/PIL/IcnsImagePlugin.py
@@ -110,6 +110,7 @@ def read_png_or_jpeg2000(fobj, start_length, size):
if sig[:8] == b'\x89PNG\x0d\x0a\x1a\x0a':
fobj.seek(start)
im = PngImagePlugin.PngImageFile(fobj)
+ Image._decompression_bomb_check(im.size)
return {"RGBA": im}
elif sig[:4] == b'\xff\x4f\xff\x51' \
or sig[:4] == b'\x0d\x0a\x87\x0a' \
@@ -122,6 +123,7 @@ def read_png_or_jpeg2000(fobj, start_length, size):
jp2kstream = fobj.read(length)
f = io.BytesIO(jp2kstream)
im = Jpeg2KImagePlugin.Jpeg2KImageFile(f)
+ Image._decompression_bomb_check(im.size)
if im.mode != 'RGBA':
im = im.convert('RGBA')
return {"RGBA": im}
diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py
index 2b6d1e0..30412ad 100644
--- a/src/PIL/IcoImagePlugin.py
+++ b/src/PIL/IcoImagePlugin.py
@@ -164,6 +164,7 @@ class IcoFile(object):
if data[:8] == PngImagePlugin._MAGIC:
# png frame
im = PngImagePlugin.PngImageFile(self.buf)
+ Image._decompression_bomb_check(im.size)
else:
# XOR + AND mask bmp frame
im = BmpImagePlugin.DibImageFile(self.buf)
--
2.30.2

@ -0,0 +1,146 @@
From 7fe3dff241c11206616bf6229be898854ce0d066 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 14 Jun 2021 11:33:36 +0200
Subject: [PATCH] CVE-2021-28675
---
src/PIL/ImageFile.py | 12 ++++++++++--
src/PIL/PsdImagePlugin.py | 33 +++++++++++++++++++++++----------
2 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py
index 1a3c4aa..2cef9ee 100644
--- a/src/PIL/ImageFile.py
+++ b/src/PIL/ImageFile.py
@@ -522,12 +522,18 @@ def _safe_read(fp, size):
:param fp: File handle. Must implement a <b>read</b> method.
:param size: Number of bytes to read.
- :returns: A string containing up to <i>size</i> bytes of data.
+ :returns: A string containing <i>size</i> bytes of data.
+
+ Raises an OSError if the file is truncated and the read can not be completed
+
"""
if size <= 0:
return b""
if size <= SAFEBLOCK:
- return fp.read(size)
+ data = fp.read(size)
+ if len(data) < size:
+ raise OSError("Truncated File Read")
+ return data
data = []
while size > 0:
block = fp.read(min(size, SAFEBLOCK))
@@ -535,6 +541,8 @@ def _safe_read(fp, size):
break
data.append(block)
size -= len(block)
+ if sum(len(d) for d in data) < size:
+ raise OSError("Truncated File Read")
return b"".join(data)
diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py
index fe2a2ff..add9996 100644
--- a/src/PIL/PsdImagePlugin.py
+++ b/src/PIL/PsdImagePlugin.py
@@ -18,6 +18,8 @@
__version__ = "0.4"
+import io
+
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16be as i16, i32be as i32
@@ -114,7 +116,8 @@ class PsdImageFile(ImageFile.ImageFile):
end = self.fp.tell() + size
size = i32(read(4))
if size:
- self.layers = _layerinfo(self.fp)
+ _layer_data = io.BytesIO(ImageFile._safe_read(self.fp, size))
+ self.layers = _layerinfo(_layer_data, size)
self.fp.seek(end)
#
@@ -164,11 +167,20 @@ class PsdImageFile(ImageFile.ImageFile):
Image.Image.load(self)
-def _layerinfo(file):
+def _layerinfo(fp, ct_bytes):
# read layerinfo block
layers = []
- read = file.read
- for i in range(abs(i16(read(2)))):
+
+ def read(size):
+ return ImageFile._safe_read(fp, size)
+
+ ct = i16(read(2))
+
+ # sanity check
+ if ct_bytes < (abs(ct) * 20):
+ raise SyntaxError("Layer block too short for number of layers requested")
+
+ for i in range(abs(ct)):
# bounding box
y0 = i32(read(4))
@@ -179,7 +191,8 @@ def _layerinfo(file):
# image info
info = []
mode = []
- types = list(range(i16(read(2))))
+ ct_types = i16(read(2))
+ types = list(range(ct_types))
if len(types) > 4:
continue
@@ -212,7 +225,7 @@ def _layerinfo(file):
size = i32(read(4)) # length of the extra data field
combined = 0
if size:
- data_end = file.tell() + size
+ data_end = fp.tell() + size
length = i32(read(4))
if length:
@@ -220,12 +233,12 @@ def _layerinfo(file):
mask_x = i32(read(4))
mask_h = i32(read(4)) - mask_y
mask_w = i32(read(4)) - mask_x
- file.seek(length - 16, 1)
+ fp.seek(length - 16, 1)
combined += length + 4
length = i32(read(4))
if length:
- file.seek(length, 1)
+ fp.seek(length, 1)
combined += length + 4
length = i8(read(1))
@@ -235,7 +248,7 @@ def _layerinfo(file):
name = read(length).decode('latin-1', 'replace')
combined += length + 1
- file.seek(data_end)
+ fp.seek(data_end)
layers.append((name, mode, (x0, y0, x1, y1)))
# get tiles
@@ -243,7 +256,7 @@ def _layerinfo(file):
for name, mode, bbox in layers:
tile = []
for m in mode:
- t = _maketile(file, m, bbox, 1)
+ t = _maketile(fp, m, bbox, 1)
if t:
tile.extend(t)
layers[i] = name, mode, bbox, tile
--
2.31.1

@ -0,0 +1,28 @@
From cedb7ba568161021bc2f2f48af95fcf33e262f77 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 14 Jun 2021 09:30:01 +0200
Subject: [PATCH 4/5] CVE-2021-28676
---
src/libImaging/FliDecode.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c
index 72ba138..9181b8b 100644
--- a/src/libImaging/FliDecode.c
+++ b/src/libImaging/FliDecode.c
@@ -209,6 +209,11 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
return -1;
}
advance = I32(ptr);
+ if (advance == 0 ) {
+ // If there's no advance, we're in in infinite loop
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
ptr += advance;
bytes -= advance;
}
--
2.31.1

@ -0,0 +1,41 @@
From 8ad7b436649c424e22689a8a874c1b0cd7c1c0fc Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 14 Jun 2021 09:22:45 +0200
Subject: [PATCH 3/5] CVE-2021-28677
---
src/PIL/EpsImagePlugin.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py
index b503487..5f5af15 100644
--- a/src/PIL/EpsImagePlugin.py
+++ b/src/PIL/EpsImagePlugin.py
@@ -167,12 +167,12 @@ class PSFile(object):
self.fp.seek(offset, whence)
def readline(self):
- s = self.char or b""
+ s = [self.char or b""]
self.char = None
c = self.fp.read(1)
- while c not in b"\r\n":
- s = s + c
+ while (c not in b"\r\n") and len(c):
+ s.append(c)
c = self.fp.read(1)
self.char = self.fp.read(1)
@@ -180,7 +180,7 @@ class PSFile(object):
if self.char in b"\r\n":
self.char = None
- return s.decode('latin-1')
+ return b"".join(s).decode("latin-1")
def _accept(prefix):
--
2.31.1

@ -0,0 +1,122 @@
From eaef29c3696cd021147e692360997f4c12377c60 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 14 Jun 2021 09:19:50 +0200
Subject: [PATCH 2/5] CVE-2021-28678
---
src/PIL/BlpImagePlugin.py | 43 +++++++++++++++++++++------------------
1 file changed, 23 insertions(+), 20 deletions(-)
diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py
index d56d46c..846c83d 100644
--- a/src/PIL/BlpImagePlugin.py
+++ b/src/PIL/BlpImagePlugin.py
@@ -294,33 +294,36 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
raise IOError("Truncated Blp file")
return 0, 0
+ def _safe_read(self, length):
+ return ImageFile._safe_read(self.fd, length)
+
def _read_palette(self):
ret = []
for i in range(256):
try:
- b, g, r, a = struct.unpack("<4B", self.fd.read(4))
+ b, g, r, a = struct.unpack("<4B", self._safe_read(4))
except struct.error:
break
ret.append((b, g, r, a))
return ret
def _read_blp_header(self):
- self._blp_compression, = struct.unpack("<i", self.fd.read(4))
+ self._blp_compression, = struct.unpack("<i", self._safe_read(4))
- self._blp_encoding, = struct.unpack("<b", self.fd.read(1))
- self._blp_alpha_depth, = struct.unpack("<b", self.fd.read(1))
- self._blp_alpha_encoding, = struct.unpack("<b", self.fd.read(1))
- self._blp_mips, = struct.unpack("<b", self.fd.read(1))
+ self._blp_encoding, = struct.unpack("<b", self._safe_read(1))
+ self._blp_alpha_depth, = struct.unpack("<b", self._safe_read(1))
+ self._blp_alpha_encoding, = struct.unpack("<b", self._safe_read(1))
+ self._blp_mips, = struct.unpack("<b", self._safe_read(1))
- self.size = struct.unpack("<II", self.fd.read(8))
+ self.size = struct.unpack("<II", self._safe_read(8))
if self.magic == b"BLP1":
# Only present for BLP1
- self._blp_encoding, = struct.unpack("<i", self.fd.read(4))
- self._blp_subtype, = struct.unpack("<i", self.fd.read(4))
+ self._blp_encoding, = struct.unpack("<i", self._safe_read(4))
+ self._blp_subtype, = struct.unpack("<i", self._safe_read(4))
- self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
- self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
+ self._blp_offsets = struct.unpack("<16I", self._safe_read(16 * 4))
+ self._blp_lengths = struct.unpack("<16I", self._safe_read(16 * 4))
class BLP1Decoder(_BLPBaseDecoder):
@@ -333,7 +336,7 @@ class BLP1Decoder(_BLPBaseDecoder):
if self._blp_encoding in (4, 5):
data = bytearray()
palette = self._read_palette()
- _data = BytesIO(self.fd.read(self._blp_lengths[0]))
+ _data = BytesIO(self._safe_read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
@@ -355,10 +358,10 @@ class BLP1Decoder(_BLPBaseDecoder):
def _decode_jpeg_stream(self):
from PIL.JpegImagePlugin import JpegImageFile
- jpeg_header_size, = struct.unpack("<I", self.fd.read(4))
- jpeg_header = self.fd.read(jpeg_header_size)
- self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
- data = self.fd.read(self._blp_lengths[0])
+ jpeg_header_size, = struct.unpack("<I", self._safe_read(4))
+ jpeg_header = self._safe_read(jpeg_header_size)
+ self._safe_read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
+ data = self._safe_read(self._blp_lengths[0])
data = jpeg_header + data
data = BytesIO(data)
image = JpegImageFile(data)
@@ -380,7 +383,7 @@ class BLP2Decoder(_BLPBaseDecoder):
# Uncompressed or DirectX compression
if self._blp_encoding == BLP_ENCODING_UNCOMPRESSED:
- _data = BytesIO(self.fd.read(self._blp_lengths[0]))
+ _data = BytesIO(self._safe_read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
@@ -394,7 +397,7 @@ class BLP2Decoder(_BLPBaseDecoder):
linesize = (self.size[0] + 3) // 4 * 8
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt1(
- self.fd.read(linesize),
+ self._safe_read(linesize),
alpha=bool(self._blp_alpha_depth)
):
data += d
@@ -402,13 +405,13 @@ class BLP2Decoder(_BLPBaseDecoder):
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT3:
linesize = (self.size[0] + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4):
- for d in decode_dxt3(self.fd.read(linesize)):
+ for d in decode_dxt3(self._safe_read(linesize)):
data += d
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT5:
linesize = (self.size[0] + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4):
- for d in decode_dxt5(self.fd.read(linesize)):
+ for d in decode_dxt5(self._safe_read(linesize)):
data += d
else:
raise BLPFormatError("Unsupported alpha encoding %r" % (
--
2.31.1

@ -0,0 +1,33 @@
diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c
index b3e48e5..cfed8ad 100644
--- a/src/libImaging/Convert.c
+++ b/src/libImaging/Convert.c
@@ -1338,9 +1338,8 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
return (Imaging) ImagingError_ValueError("conversion not supported");
#else
{
- static char buf[256];
- /* FIXME: may overflow if mode is too large */
- sprintf(buf, "conversion from %s to %s not supported", imIn->mode, mode);
+ static char buf[100];
+ snprintf(buf, 100, "conversion from %.10s to %.10s not supported", imIn->mode, mode);
return (Imaging) ImagingError_ValueError(buf);
}
#endif
@@ -1394,9 +1393,13 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
}
#else
{
- static char buf[256];
- /* FIXME: may overflow if mode is too large */
- sprintf(buf, "conversion from %s to %s not supported in convert_transparent", imIn->mode, mode);
+ static char buf[100];
+ snprintf(
+ buf,
+ 100,
+ "conversion from %.10s to %.10s not supported in convert_transparent",
+ imIn->mode,
+ mode);
return (Imaging) ImagingError_ValueError(buf);
}
#endif

@ -0,0 +1,69 @@
diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py
index 98a6d34..733fd29 100644
--- a/Tests/test_imagepath.py
+++ b/Tests/test_imagepath.py
@@ -67,6 +67,11 @@ class TestImagePath(PillowTestCase):
p = ImagePath.Path(arr.tostring())
self.assertEqual(list(p), [(0.0, 1.0)])
+ def test_getbbox(self):
+ for coords in (0,1):
+ p = ImagePath.Path(coords)
+ self.assertEqual(p.getbbox(), (0.0, 0.0, 0.0, 0.0))
+
def test_overflow_segfault(self):
# Some Pythons fail getting the argument as an integer, and it falls
# through to the sequence. Seeing this on 32-bit Windows.
diff --git a/src/path.c b/src/path.c
index eb1e065..5215f87 100644
--- a/src/path.c
+++ b/src/path.c
@@ -62,7 +62,7 @@ alloc_array(Py_ssize_t count)
PyErr_NoMemory();
return NULL;
}
- xy = malloc(2 * count * sizeof(double) + 1);
+ xy = calloc(2 * count + 1, sizeof(double));
if (!xy)
PyErr_NoMemory();
return xy;
@@ -330,18 +330,27 @@ path_getbbox(PyPathObject* self, PyObject* args)
xy = self->xy;
- x0 = x1 = xy[0];
- y0 = y1 = xy[1];
-
- for (i = 1; i < self->count; i++) {
- if (xy[i+i] < x0)
- x0 = xy[i+i];
- if (xy[i+i] > x1)
- x1 = xy[i+i];
- if (xy[i+i+1] < y0)
- y0 = xy[i+i+1];
- if (xy[i+i+1] > y1)
- y1 = xy[i+i+1];
+ if (self->count == 0) {
+ x0 = x1 = 0;
+ y0 = y1 = 0;
+ } else {
+ x0 = x1 = xy[0];
+ y0 = y1 = xy[1];
+
+ for (i = 1; i < self->count; i++) {
+ if (xy[i + i] < x0) {
+ x0 = xy[i + i];
+ }
+ if (xy[i + i] > x1) {
+ x1 = xy[i + i];
+ }
+ if (xy[i + i + 1] < y0) {
+ y0 = xy[i + i + 1];
+ }
+ if (xy[i + i + 1] > y1) {
+ y1 = xy[i + i + 1];
+ }
+ }
}
return Py_BuildValue("dddd", x0, y0, x1, y1);

@ -0,0 +1,41 @@
diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py
index 2329b74..d3b7ba3 100644
--- a/Tests/test_imagemath.py
+++ b/Tests/test_imagemath.py
@@ -58,6 +58,12 @@ class TestImageMath(PillowTestCase):
self.assertEqual(pixel(
ImageMath.eval("float(B)**33", images)), "F 8589934592.0")
+ def test_prevent_exec(self):
+ self.assertRaises(ValueError, ImageMath.eval("exec('pass')"))
+ self.assertRaises(ValueError, ImageMath.eval("(lambda: exec('pass'))()"))
+ self.assertRaises(ValueError, ImageMath.eval("(lambda: (lambda: exec('pass'))())()"))
+
+
def test_logical(self):
self.assertEqual(pixel(ImageMath.eval("not A", images)), 0)
self.assertEqual(pixel(ImageMath.eval("A and B", images)), "L 2")
diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py
index c5bea70..13839e4 100644
--- a/src/PIL/ImageMath.py
+++ b/src/PIL/ImageMath.py
@@ -263,7 +263,18 @@ def eval(expression, _dict={}, **kw):
if hasattr(v, "im"):
args[k] = _Operand(v)
- out = builtins.eval(expression, args)
+ compiled_code = compile(expression, "<string>", "eval")
+ def scan(code):
+ for const in code.co_consts:
+ if type(const) == type(compiled_code):
+ scan(const)
+
+ for name in code.co_names:
+ if name not in args and name != "abs":
+ raise ValueError(f"'{name}' not allowed")
+
+ scan(compiled_code)
+ out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args)
try:
return out.im
except AttributeError:

@ -0,0 +1,91 @@
From fc055dbef875b477c27196e10c61f98aeb23d62c Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Fri, 10 Nov 2023 15:39:41 +0100
Subject: [PATCH] CVE-2023-44271
---
docs/reference/ImageFont.rst | 9 +++++++++
src/PIL/ImageFont.py | 12 ++++++++++++
2 files changed, 21 insertions(+)
diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst
index 76fde44..21b9d9d 100644
--- a/docs/reference/ImageFont.rst
+++ b/docs/reference/ImageFont.rst
@@ -17,6 +17,15 @@ OpenType fonts (as well as other font formats supported by the FreeType
library). For earlier versions, TrueType support is only available as part of
the imToolkit package
+.. warning::
+ To protect against potential DOS attacks when using arbitrary strings as
+ text input, Pillow will raise a ``ValueError`` if the number of characters
+ is over a certain limit, :py:data:`MAX_STRING_LENGTH`.
+
+ This threshold can be changed by setting
+ :py:data:`MAX_STRING_LENGTH`. It can be disabled by setting
+ ``ImageFont.MAX_STRING_LENGTH = None``.
+
Example
-------
diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py
index f3b55e0..7e7b62f 100644
--- a/src/PIL/ImageFont.py
+++ b/src/PIL/ImageFont.py
@@ -39,6 +39,8 @@ class _imagingft_not_installed(object):
def __getattr__(self, id):
raise ImportError("The _imagingft C module is not installed")
+MAX_STRING_LENGTH = 1_000_000
+
try:
from . import _imagingft as core
@@ -46,6 +48,12 @@ except ImportError:
core = _imagingft_not_installed()
+def _string_length_check(text):
+ if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH:
+ msg = "too many characters in string"
+ raise ValueError(msg)
+
+
# FIXME: add support for pilfont2 format (see FontFile.py)
# --------------------------------------------------------------------
@@ -109,6 +117,7 @@ class ImageFont(object):
self.font = Image.core.font(image.im, data)
def getsize(self, text, *args, **kwargs):
+ _string_length_check(text)
return self.font.getsize(text)
def getmask(self, text, mode="", *args, **kwargs):
@@ -154,6 +163,7 @@ class FreeTypeFont(object):
return self.font.ascent, self.font.descent
def getsize(self, text, direction=None, features=None):
+ _string_length_check(text)
size, offset = self.font.getsize(text, direction, features)
return (size[0] + offset[0], size[1] + offset[1])
@@ -164,6 +174,7 @@ class FreeTypeFont(object):
return self.getmask2(text, mode, direction=direction, features=features)[0]
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None, *args, **kwargs):
+ _string_length_check(text)
size, offset = self.font.getsize(text, direction, features)
im = fill("L", size, 0)
self.font.render(text, im.id, mode == "1", direction, features)
@@ -205,6 +216,7 @@ class TransposedFont(object):
self.orientation = orientation # any 'transpose' argument, or None
def getsize(self, text, *args, **kwargs):
+ _string_length_check(text)
w, h = self.font.getsize(text)
if self.orientation in (Image.ROTATE_90, Image.ROTATE_270):
return h, w
--
2.41.0

@ -0,0 +1,46 @@
From 5c3db10f7a9cafd9b2d145a40864a445b2ee6edc Mon Sep 17 00:00:00 2001
From: Eric Soroos <eric-github@soroos.net>
Date: Thu, 25 Jan 2024 13:23:56 +0100
Subject: [PATCH] Don't allow __ or builtins in env dictionarys for
ImageMath.eval
---
Tests/test_imagemath.py | 5 +++++
src/PIL/ImageMath.py | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py
index d3b7ba3..2467078 100644
--- a/Tests/test_imagemath.py
+++ b/Tests/test_imagemath.py
@@ -63,6 +63,11 @@ class TestImageMath(PillowTestCase):
self.assertRaises(ValueError, ImageMath.eval("(lambda: exec('pass'))()"))
self.assertRaises(ValueError, ImageMath.eval("(lambda: (lambda: exec('pass'))())()"))
+ def test_prevent_double_underscores(self):
+ self.assertRaises(ValueError, ImageMath.eval("1", {"__": None}))
+
+ def test_prevent_builtins(self):
+ self.assertRaises(ValueError, ImageMath.eval("(lambda: exec('exit()'))()", {"exec": None}))
def test_logical(self):
self.assertEqual(pixel(ImageMath.eval("not A", images)), 0)
diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py
index 13839e4..94108cf 100644
--- a/src/PIL/ImageMath.py
+++ b/src/PIL/ImageMath.py
@@ -257,6 +257,11 @@ def eval(expression, _dict={}, **kw):
# build execution namespace
args = ops.copy()
+ for k in list(_dict.keys()) + list(kw.keys()):
+ if "__" in k or hasattr(builtins, k):
+ msg = f"'{k}' not allowed"
+ raise ValueError(msg)
+
args.update(_dict)
args.update(kw)
for k, v in list(args.items()):
--
2.43.0

@ -1,89 +1,147 @@
%global py3_incdir %(RPM_BUILD_ROOT= %{python3} -Ic 'import sysconfig; print(sysconfig.get_path("include"))')
%global py3_incdir %(python3 -c 'import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())')
%global py3_libbuilddir %(python3 -c 'import sys; import sysconfig; print("lib.{p}-{v[0]}.{v[1]}".format(p=sysconfig.get_platform(), v=sys.version_info))')
%global srcname pillow
# Dependencies are missing to build the documentation
%bcond_with doc
%if 0%{?rhel} || 0%{?flatpak}
%bcond_with mingw
%else
%bcond_without mingw
%endif
# bootstrap building docs (pillow is required by docutils, docutils are
# required by sphinx; pillow build-requires sphinx)
%global with_docs 1
Name: python-%{srcname}
Version: 10.0.1
Release: 1%{?dist}
Version: 5.1.1
Release: 20%{?dist}
Summary: Python image processing library
# License: see http://www.pythonware.com/products/pil/license.htm
License: MIT
URL: http://python-pillow.github.io/
Source0: https://github.com/python-pillow/Pillow/archive/%{version}/Pillow-%{version}.tar.gz
# MinGW build fixes
Patch0: pillow_mingw.patch
Patch0: 0001-Fix-potential-un-terminated-buffer-problem-CWE-120.patch
Patch1: 0002-Fix-potential-leaked-storage-issues-CWE-772.patch
Patch2: 0003-Fix-dereferencing-type-punned-pointer.patch
# Combined fixes for CVE-2020-5312 improperly restricted operations on memory buffer in libImaging/PcxDecode.c
# https://bugzilla.redhat.com/show_bug.cgi?id=1789533
# https://github.com/python-pillow/Pillow/commit/93b22b846e0269ee9594ff71a72bec02d2bea8fd
# and for CVE-2019-16865 reading specially crafted image files leads to allocation of large amounts of memory and denial of service
# https://bugzilla.redhat.com/show_bug.cgi?id=1774066
# https://github.com/python-pillow/Pillow/commit/cc16025e234b7a7a4dd3a86d2fdc0980698db9cc
# https://github.com/python-pillow/Pillow/commit/b36c1bc943d554ba223086c7efb502d080f73905
# https://github.com/python-pillow/Pillow/commit/f228d0ccbf6bf9392d7fcd51356ef2cfda80c75a
# https://github.com/python-pillow/Pillow/commit/b9693a51c99c260bd66d1affeeab4a226cf7e5a5
Patch3: CVE-2020-5312_CVE-2019-16865.patch
# Fix for CVE-2020-5311 - out-of-bounds write in expandrow in libImaging/SgiRleDecode.c
# https://bugzilla.redhat.com/show_bug.cgi?id=1789535
# https://github.com/python-pillow/Pillow/commit/a79b65c47c7dc6fe623aadf09aa6192fc54548f3
Patch4: CVE-2020-5311.patch
# CVE-2020-5313 out-of-bounds read in ImagingFliDecode when loading FLI images
# Upstream fix: https://github.com/python-pillow/Pillow/commit/a09acd0decd8a87ccce939d5ff65dab59e7d365b?patch
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=1789532
Patch5: CVE-2020-5313.patch
# CVE-2020-11538 out-of-bounds reads/writes in the parsing of SGI image files in expandrow/expandrow2
# Upstream fix: https://github.com/python-pillow/Pillow/pull/4504/
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=1852814
Patch6: CVE-2020-11538.patch
# CVE-2020-35653 decoding a crafted PCX file could result in buffer over-read
# Note that there is a wrong CVE number in the commit msg
# Upstream fix: https://github.com/python-pillow/Pillow/commit/2f409261eb1228e166868f8f0b5da5cda52e55bf
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=1915432
Patch7: CVE-2020-35653.patch
# CVE-2020-35655 decoding crafted SGI RLE image files could result in buffer over-read
# Upstream fix: https://github.com/python-pillow/Pillow/commit/120eea2e4547a7d1826afdf01563035844f0b7d5
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2020-35653
Patch8: CVE-2020-35655.patch
# CVE-2021-25290 negative-offset memcpy with an invalid size in TiffDecode.c
# Upstream fix: https://github.com/python-pillow/Pillow/commit/86f02f7c70862a0954bfe8133736d352db978eaa
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=1934685
Patch9: CVE-2021-25290.patch
# CVE-2021-25292 backtracking regex in PDF parser could be used as a DOS attack
# Upstream fix: https://github.com/python-pillow/Pillow/commit/3bce145966374dd39ce58a6fc0083f8d1890719c
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-25292
Patch10: CVE-2021-25292.patch
# CVE-2021-25293 out-of-bounds read in SGIRleDecode.c
# Upstream fix: https://github.com/python-pillow/Pillow/commit/4853e522bddbec66022c0915b9a56255d0188bf9
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-25293
Patch11: CVE-2021-25293.patch
# CVE-2021-27921 reported size of a contained image is not properly checked for a BLP container
# CVE-2021-27922 reported size of a contained image is not properly checked for an ICNS container
# CVE-2021-27923 reported size of a contained image is not properly checked for an ICO container
# Upstream fix: https://github.com/python-pillow/Pillow/commit/480f6819b592d7f07b9a9a52a7656c10bbe07442
# Tracking bugs:
# - https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-27921
# - https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-27922
# - https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-27923
Patch12: CVE-2021-27921_27922_27923.patch
# CVE-2021-25288 and 25287 out-of-bounds read in J2kDecode in j2ku_gray_i and j2ku_graya_la
# Upstream fixes this patch combines:
# - Original fix for the CVEs: https://github.com/python-pillow/Pillow/commit/3bf5eddb89afdf690eceaa52bc4d3546ba9a5f87
# - Older commit the fix is based on: https://github.com/python-pillow/Pillow/commit/cf6da6b79080a8c16984102fdc85f7ce28dca613
# Tracking bugs:
# - https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-25287
# - https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-25288
Patch13: CVE-2021-25287_25288.patch
# CVE-2021-28675 DoS in PsdImagePlugin
# Upstream fix: https://github.com/python-pillow/Pillow/commit/22e9bee4ef225c0edbb9323f94c26cee0c623497
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-28675
Patch14: CVE-2021-28675.patch
# CVE-2021-28676 infinite loop in FliDecode.c can lead to DoS
# Upstream fix: https://github.com/python-pillow/Pillow/commit/bb6c11fb889e6c11b0ee122b828132ee763b5856
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-28676
Patch15: CVE-2021-28676.patch
# CVE-2021-28677 DoS in the open phase via a malicious EPS file
# Upstream fix: https://github.com/python-pillow/Pillow/commit/5a5e6db0abf4e7a638fb1b3408c4e495a096cb92
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-28677
Patch16: CVE-2021-28677.patch
# CVE-2021-28678 improper check in BlpImagePlugin can lead to DoS
# Upstream fix: https://github.com/python-pillow/Pillow/commit/496245aa4365d0827390bd0b6fbd11287453b3a1
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2021-28678
Patch17: CVE-2021-28678.patch
# CVE-2021-34552: buffer overflow in Convert.c because it allow an attacker to pass
# controlled parameters directly into a convert function
# Upstream fix: https://github.com/python-pillow/Pillow/pull/5567
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=1982378
Patch18: CVE-2021-34552.patch
# CVE-2022-22817: PIL.ImageMath.eval allows evaluation of arbitrary expressions
# Upstream fixes:
# https://github.com/python-pillow/Pillow/commit/8531b01d6cdf0b70f256f93092caa2a5d91afc11
# https://github.com/python-pillow/Pillow/commit/f84ab3bb8a0a196a52e8a76bebed2853362629de
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=2042527
Patch19: CVE-2022-22817.patch
# CVE-2022-22815 python-pillow: improperly initializes ImagePath.Path in path_getbbox() in path.c
# CVE-2022-22816 python-pillow: buffer over-read during initialization of ImagePath.Path in path_getbbox() in path.c
# Upstream fix: https://github.com/python-pillow/Pillow/commit/5543e4e2d409cd9e409bc64cdc77be0af007a31f
# Memory issue fix: https://github.com/python-pillow/Pillow/pull/5958
# Tracking bugs:
# https://bugzilla.redhat.com/show_bug.cgi?id=2042511
# https://bugzilla.redhat.com/show_bug.cgi?id=2042522
Patch20: CVE-2022-22815_CVE-2022-22816.patch
# CVE-2023-44271 python-pillow: uncontrolled resource consumption when textlength
# in an ImageDraw instance operates on a long text argument
# Upstream fix: https://github.com/python-pillow/Pillow/commit/1fe1bb49c452b0318cad12ea9d97c3bef188e9a7
Patch21: CVE-2023-44271.patch
# CVE-2023-50447 python-pillow: pillow:Arbitrary Code Execution via the environment parameter
# Upstream fix: https://github.com/python-pillow/Pillow/commit/02c6183d41c68a8dd080f5739f566bd82485822d
# Patch rebased and tests converted from pytest to unittests.
Patch22: CVE-2023-50447.patch
BuildRequires: freetype-devel
BuildRequires: gcc
BuildRequires: ghostscript
BuildRequires: lcms2-devel
BuildRequires: libimagequant-devel
BuildRequires: libjpeg-devel
BuildRequires: libraqm-devel
BuildRequires: libtiff-devel
BuildRequires: libwebp-devel
BuildRequires: openjpeg2-devel
BuildRequires: tk-devel
BuildRequires: zlib-devel
BuildRequires: python%{python3_pkgversion}-cffi
BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-numpy
BuildRequires: python%{python3_pkgversion}-olefile
BuildRequires: python%{python3_pkgversion}-pytest
BuildRequires: python%{python3_pkgversion}-qt5
BuildRequires: python%{python3_pkgversion}-setuptools
%if %{with doc}
BuildRequires: make
BuildRequires: python%{python3_pkgversion}-sphinx
BuildRequires: python%{python3_pkgversion}-sphinx-copybutton
BuildRequires: python%{python3_pkgversion}-sphinx_rtd_theme
BuildRequires: python%{python3_pkgversion}-sphinx-removed-in
%endif
BuildRequires: python%{python3_pkgversion}-tkinter
%if %{with mingw}
BuildRequires: mingw32-filesystem >= 95
BuildRequires: mingw32-gcc
BuildRequires: mingw32-python3
BuildRequires: mingw32-python3-setuptools
BuildRequires: mingw32-dlfcn
BuildRequires: mingw32-freetype
BuildRequires: mingw32-lcms2
BuildRequires: mingw32-libimagequant
BuildRequires: mingw32-libjpeg
BuildRequires: mingw32-libtiff
BuildRequires: mingw32-libwebp
BuildRequires: mingw32-openjpeg2
BuildRequires: mingw32-tk
BuildRequires: mingw32-zlib
BuildRequires: mingw64-filesystem >= 95
BuildRequires: mingw64-gcc
BuildRequires: mingw64-python3
BuildRequires: mingw64-python3-setuptools
BuildRequires: mingw64-dlfcn
BuildRequires: mingw64-freetype
BuildRequires: mingw64-lcms2
BuildRequires: mingw64-libimagequant
BuildRequires: mingw64-libjpeg
BuildRequires: mingw64-libtiff
BuildRequires: mingw64-libwebp
BuildRequires: mingw64-openjpeg2
BuildRequires: mingw64-tk
BuildRequires: mingw64-zlib
%endif
BuildRequires: python3-cffi
BuildRequires: python3-devel
BuildRequires: python3-numpy
BuildRequires: python3-setuptools
%if 0%{?with_docs}
BuildRequires: python3-sphinx
BuildRequires: python3-sphinx_rtd_theme
%endif # with_docs
BuildRequires: python3-tkinter
# For EpsImagePlugin.py
Requires: ghostscript
@ -96,90 +154,57 @@ Python image processing library, fork of the Python Imaging Library (PIL)
This library provides extensive file format support, an efficient
internal representation, and powerful image processing capabilities.
There are four subpackages: tk (tk interface), qt (PIL image wrapper for Qt),
There are these subpackages: tk (tk interface),
devel (development) and doc (documentation).
%package -n python%{python3_pkgversion}-%{srcname}
%package -n python3-%{srcname}
Summary: Python 3 image processing library
%{?python_provide:%python_provide python%{python3_pkgversion}-%{srcname}}
Provides: python%{python3_pkgversion}-imaging = %{version}-%{release}
%{?python_provide:%python_provide python3-%{srcname}}
Provides: python3-imaging = %{version}-%{release}
# For MicImagePlugin.py, FpxImagePlugin.py
Requires: python%{python3_pkgversion}-olefile
%description -n python%{python3_pkgversion}-%{srcname}
%description -n python3-%{srcname}
Python image processing library, fork of the Python Imaging Library (PIL)
This library provides extensive file format support, an efficient
internal representation, and powerful image processing capabilities.
There are four subpackages: tk (tk interface), qt (PIL image wrapper for Qt),
There are these subpackages: tk (tk interface),
devel (development) and doc (documentation).
%package -n python%{python3_pkgversion}-%{srcname}-devel
%package -n python3-%{srcname}-devel
Summary: Development files for %{srcname}
Requires: python%{python3_pkgversion}-devel, libjpeg-devel, zlib-devel
Requires: python%{python3_pkgversion}-%{srcname}%{?_isa} = %{version}-%{release}
%{?python_provide:%python_provide python%{python3_pkgversion}-%{srcname}-devel}
Provides: python%{python3_pkgversion}-imaging-devel = %{version}-%{release}
Requires: python3-devel, libjpeg-devel, zlib-devel
Requires: python3-%{srcname}%{?_isa} = %{version}-%{release}
%{?python_provide:%python_provide python3-%{srcname}-devel}
Provides: python3-imaging-devel = %{version}-%{release}
%description -n python%{python3_pkgversion}-%{srcname}-devel
%description -n python3-%{srcname}-devel
Development files for %{srcname}.
%package -n python%{python3_pkgversion}-%{srcname}-doc
%package -n python3-%{srcname}-doc
Summary: Documentation for %{srcname}
BuildArch: noarch
Requires: python%{python3_pkgversion}-%{srcname} = %{version}-%{release}
%{?python_provide:%python_provide python%{python3_pkgversion}-%{srcname}-doc}
Provides: python%{python3_pkgversion}-imaging-doc = %{version}-%{release}
Requires: python3-%{srcname} = %{version}-%{release}
%{?python_provide:%python_provide python3-%{srcname}-doc}
Provides: python3-imaging-doc = %{version}-%{release}
%description -n python%{python3_pkgversion}-%{srcname}-doc
%description -n python3-%{srcname}-doc
Documentation for %{srcname}.
%package -n python%{python3_pkgversion}-%{srcname}-tk
%package -n python3-%{srcname}-tk
Summary: Tk interface for %{srcname}
Requires: python%{python3_pkgversion}-tkinter
Requires: python%{python3_pkgversion}-%{srcname}%{?_isa} = %{version}-%{release}
%{?python_provide:%python_provide python%{python3_pkgversion}-%{srcname}-tk}
Provides: python%{python3_pkgversion}-imaging-tk = %{version}-%{release}
%description -n python%{python3_pkgversion}-%{srcname}-tk
Tk interface for %{srcname}.
%package -n python%{python3_pkgversion}-%{srcname}-qt
Summary: Qt %{srcname} image wrapper
Requires: python%{python3_pkgversion}-qt5
Requires: python%{python3_pkgversion}-%{srcname}%{?_isa} = %{version}-%{release}
%{?python_provide:%python_provide python%{python3_pkgversion}-%{srcname}-qt}
Provides: python%{python3_pkgversion}-imaging-qt = %{version}-%{release}
%description -n python%{python3_pkgversion}-%{srcname}-qt
Qt %{srcname} image wrapper.
%if %{with mingw}
%package -n mingw32-python3-%{srcname}
Summary: MinGW Windows Python2 %{srcname} library
BuildArch: noarch
%description -n mingw32-python3-%{srcname}
MinGW Windows Python2 %{srcname} library.
%package -n mingw64-python3-%{srcname}
Summary: MinGW Windows Python2 %{srcname} library
BuildArch: noarch
Requires: python3-tkinter
Requires: python3-%{srcname}%{?_isa} = %{version}-%{release}
%{?python_provide:%python_provide python3-%{srcname}-tk}
Provides: python3-imaging-tk = %{version}-%{release}
%description -n mingw64-python3-%{srcname}
MinGW Windows Python2 %{srcname} library.
%{?mingw_debug_package}
%endif
%description -n python3-%{srcname}-tk
Tk interface for %{name}.
%prep
@ -187,70 +212,33 @@ MinGW Windows Python2 %{srcname} library.
%build
# Native build
%py3_build
# MinGW build
%if %{with mingw}
PKG_CONFIG=mingw32-pkg-config %{mingw32_py3_build}
PKG_CONFIG=mingw64-pkg-config %{mingw64_py3_build}
%endif
# Doc build
%if %{with doc}
PYTHONPATH=$(echo $PWD/build/lib.linux-*) make -C docs html BUILDDIR=_build_py3 SPHINXBUILD=sphinx-build-%python3_version
%if 0%{?with_docs}
PYTHONPATH=$PWD/build/%py3_libbuilddir make -C docs html BUILDDIR=_build_py3 SPHINXBUILD=sphinx-build-%python3_version
rm -f docs/_build_py3/html/.buildinfo
%endif
%endif # with_docs
%install
# Native build
install -d %{buildroot}/%{py3_incdir}/Imaging
install -m 644 src/libImaging/*.h %{buildroot}/%{py3_incdir}/Imaging
%py3_install
# MinGW build
%if %{with mingw}
install -d %{buildroot}/%{mingw32_py3_incdir}/Imaging
install -m 644 src/libImaging/*.h %{buildroot}/%{mingw32_py3_incdir}/Imaging
install -d %{buildroot}/%{mingw64_py3_incdir}/Imaging
install -m 644 src/libImaging/*.h %{buildroot}/%{mingw64_py3_incdir}/Imaging
%{mingw32_py3_install}
%{mingw64_py3_install}
# Remove sample scripts
rm -rf %{buildroot}%{mingw32_bindir}
rm -rf %{buildroot}%{mingw64_bindir}
%endif
%if %{with mingw}
%mingw_debug_install_post
%endif
%check
# Check Python 3 modules
ln -s $PWD/Images $(echo $PWD/build/lib.linux-*)/Images
cp -R $PWD/Tests $(echo $PWD/build/lib.linux-*)/Tests
cp -a $PWD/selftest.py $(echo $PWD/build/lib.linux-*)/selftest.py
pushd build/lib.linux-*
ln -s $PWD/Images $PWD/build/%py3_libbuilddir/Images
cp -R $PWD/Tests $PWD/build/%py3_libbuilddir/Tests
cp -R $PWD/selftest.py $PWD/build/%py3_libbuilddir/selftest.py
pushd build/%py3_libbuilddir
PYTHONPATH=$PWD %{__python3} selftest.py
popd
%ifnarch s390x
%pytest -v -k "not test_qt_image_qapplication" || :
%else
%pytest -v -k "not test_qt_image_qapplication" || :
%endif
%files -n python%{python3_pkgversion}-%{srcname}
%doc README.md CHANGES.rst
%files -n python3-%{srcname}
%doc README.rst CHANGES.rst
%license docs/COPYING
%{python3_sitearch}/PIL/
%{python3_sitearch}/Pillow-%{version}-py%{python3_version}.egg-info
%{python3_sitearch}/*
# These are in subpackages
%exclude %{python3_sitearch}/PIL/_imagingtk*
%exclude %{python3_sitearch}/PIL/ImageTk*
@ -260,238 +248,104 @@ popd
%exclude %{python3_sitearch}/PIL/__pycache__/SpiderImagePlugin*
%exclude %{python3_sitearch}/PIL/__pycache__/ImageQt*
%files -n python%{python3_pkgversion}-%{srcname}-devel
%files -n python3-%{srcname}-devel
%{py3_incdir}/Imaging/
%if %{with doc}
%files -n python%{python3_pkgversion}-%{srcname}-doc
%files -n python3-%{srcname}-doc
%if 0%{?with_docs}
%doc docs/_build_py3/html
%endif
%endif # with_docs
%files -n python%{python3_pkgversion}-%{srcname}-tk
%files -n python3-%{srcname}-tk
%{python3_sitearch}/PIL/_imagingtk*
%{python3_sitearch}/PIL/ImageTk*
%{python3_sitearch}/PIL/SpiderImagePlugin*
%{python3_sitearch}/PIL/__pycache__/ImageTk*
%{python3_sitearch}/PIL/__pycache__/SpiderImagePlugin*
%files -n python%{python3_pkgversion}-%{srcname}-qt
%{python3_sitearch}/PIL/ImageQt*
%{python3_sitearch}/PIL/__pycache__/ImageQt*
%if %{with mingw}
%files -n mingw32-python3-%{srcname}
%license docs/COPYING
%{mingw32_python3_sitearch}/PIL/
%{mingw32_python3_sitearch}/Pillow-%{version}-py%{mingw32_python3_version}.egg-info/
%{mingw32_py3_incdir}/Imaging/
%files -n mingw64-python3-%{srcname}
%license docs/COPYING
%{mingw64_python3_sitearch}/PIL/
%{mingw64_python3_sitearch}/Pillow-%{version}-py%{mingw64_python3_version}.egg-info/
%{mingw64_py3_incdir}/Imaging/
%endif
%changelog
* Mon Sep 18 2023 Sandro Mani <manisandro@gmail.com> - 10.0.1-1
- Update to 10.0.1
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 10.0.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
* Wed Jul 05 2023 Sandro Mani <manisandro@gmail.com> - 10.0.0-1
- Update to 10.0.0
* Fri Jun 16 2023 Python Maint <python-maint@redhat.com> - 9.5.0-2
- Rebuilt for Python 3.12
* Mon Apr 03 2023 Sandro Mani <manisandro@gmail.com> - 9.5.0-1
- Update to 9.5.0
* Sat Mar 04 2023 Sandro Mani <manisandro@gmail.com> - 9.4.0-3
- Rebuild (libimagequant)
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 9.4.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
* Mon Jan 02 2023 Sandro Mani <manisandro@gmail.com> - 9.4.0-1
- Update to 9.4.0
* Mon Oct 31 2022 Sandro Mani <manisandro@gmail.com> - 9.3.0-2
- Rebuild (mingw-python-3.11)
* Sun Oct 30 2022 Sandro Mani <manisandro@gmail.com> - 9.3.0-1
- Update to 9.3.0
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 9.2.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Sun Jul 03 2022 Sandro Mani <manisandro@gmail.com> - 9.2.0-1
- Update to 9.2.0
* Wed Jun 22 2022 Charalampos Stratakis <cstratak@redhat.com> - 9.1.1-4
- Fix FTBFS with setuptools >= 62.1
Resolves: rhbz#2097095
* Mon Jun 13 2022 Python Maint <python-maint@redhat.com> - 9.1.1-3
- Rebuilt for Python 3.11
* Fri May 20 2022 Sandro Mani <manisandro@gmail.com> - 9.1.1-2
- Rebuild for gdal-3.5.0 and/or openjpeg-2.5.0
* Fri May 20 2022 Sandro Mani <manisandro@gmail.com> - 9.1.1-1
- Update to 9.1.1
* Tue Apr 05 2022 Sandro Mani <manisandro@gmail.com> - 9.1.0-1
- Update to 9.1.0
* Fri Mar 25 2022 Sandro Mani <manisandro@gmail.com> - 9.0.1-7
- Rebuild with mingw-gcc-12
* Thu Mar 03 2022 Sandro Mani <manisandro@gmail.com> - 9.0.1-6
- Fix name -> srcname
* Thu Feb 24 2022 Sandro Mani <manisandro@gmail.com> - 9.0.1-5
- Make mingw subpackages noarch
* Thu Feb 24 2022 Sandro Mani <manisandro@gmail.com> - 9.0.1-4
- Add mingw subpackages
* Thu Feb 03 2022 Sandro Mani <manisandro@gmail.com> - 9.0.1-1
- Update to 9.0.1
* Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 9.0.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
* Mon Jan 03 2022 Sandro Mani <manisandro@gmail.com> - 9.0.0-1
- Update to 9.0.0
* Fri Oct 15 2021 Sandro Mani <manisandro@gmail.com> - 8.4.0-1
- Update to 8.4.0
* Fri Sep 03 2021 Sandro Mani <manisandro@gmail.com> - 8.3.2-1
- Update to 8.3.2
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 8.3.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Wed Jul 07 2021 Sandro Mani <manisandro@gmail.com> - 8.3.1-1
- Update to 8.3.1
* Thu Jun 03 2021 Python Maint <python-maint@redhat.com> - 8.2.0-3
- Rebuilt for Python 3.10
* Mon May 24 2021 Sandro Mani <manisandro@gmail.com> - 8.2.0-2
- Run full test suite
* Fri Apr 02 2021 Sandro Mani <manisandro@gmail.com> - 8.2.0-1
- Update to 8.2.0
* Sat Mar 06 2021 Sandro Mani <manisandro@gmail.com> - 8.1.2-1
- Update to 8.1.2
* Tue Mar 02 2021 Sandro Mani <manisandro@gmail.com> - 8.1.1-1
- Update to 8.1.1
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 8.1.0-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Sat Jan 09 2021 Robert-André Mauchin <zebob.m@gmail.com> - 8.1.0-2
- Add patch to fix the import error occurring with Python 3.10
- Fix: rhbz#1904379
* Sun Jan 03 2021 Sandro Mani <manisandro@gmail.com> - 8.1.0-1
- Update to 8.1.0
* Fri Oct 23 2020 Sandro Mani <manisandro@gmail.com> - 8.0.1-1
- Update to 8.0.1
* Thu Oct 15 2020 Sandro Mani <manisandro@gmail.com> - 8.0.0-1
- Update to 8.0.0
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 7.2.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Tue Jun 30 2020 Sandro Mani <manisandro@gmail.com> - 7.2.0-1
- Update to 7.2.0
* Sat May 23 2020 Miro Hrončok <mhroncok@redhat.com> - 7.1.2-2
- Rebuilt for Python 3.9
* Sat Apr 25 2020 Sandro Mani <manisandro@gmail.com> - 7.1.2-1
- Update to 7.1.2
* Tue Apr 21 2020 Charalampos Stratakis <cstratak@redhat.com> - 7.1.1-2
- Fix html docs build failure with Sphinx3 (rhbz#1823884)
* Thu Apr 02 2020 Sandro Mani <manisandro@gmail.com> - 7.1.1-1
- Update to 7.1.1
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 7.0.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Tue Jan 07 2020 Sandro Mani <manisandro@gmail.com> - 7.0.0-1
- Update to 7.0.0
- Drop python2 packages
* Mon Oct 21 2019 Sandro Mani <manisandro@gmail.com> - 6.2.1-1
- Update to 6.2.1
* Mon Oct 07 2019 Petr Viktorin <pviktori@redhat.com> - 6.2.0-2
- Remove optional build dependency on python2-cffi
* Tue Oct 01 2019 Sandro Mani <manisandro@gmail.com> - 6.2.0-1
- Update to 6.2.0
* Fri Aug 16 2019 Miro Hrončok <mhroncok@redhat.com> - 6.1.0-4
- Rebuilt for Python 3.8
* Mon Aug 12 2019 Sandro Mani <manisandro@gmail.com> - 6.1.0-3
- Drop python2-pillow-qt, python2-pillow-tk
* Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 6.1.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Mon Jul 01 2019 Sandro Mani <manisandro@gmail.com> - 6.1.0-1
- Update to 6.1.0
* Fri May 31 2019 Elliott Sales de Andrade <quantum.analyst@gmail.com> - 6.0.0-2
- Fix broken Python/C interop on s390x
* Tue Apr 02 2019 Sandro Mani <manisandro@gmail.com> - 6.0.0-1
- Update to 6.0.0
* Sun Mar 10 2019 Sandro Mani <manisandro@gmail.com> - 5.4.1-4
- Drop python2-pillow-doc
* Mon Mar 04 2019 Yatin Karel <ykarel@redhat.com> - 5.4.1-3
- Fix python3 conditional
* Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 5.4.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Mon Jan 07 2019 Sandro Mani <manisandro@gmail.com> - 5.4.1-1
- Update to 5.4.1
* Mon Oct 01 2018 Sandro Mani <manisandro@gmail.com> - 5.3.0-1
- Update to 5.3.0
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 5.2.0-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Mon Jul 02 2018 Miro Hrončok <mhroncok@redhat.com> - 5.2.0-2
- Rebuilt for Python 3.7
* Mon Jul 02 2018 Sandro Mani <manisandro@gmail.com> - 5.2.0-1
- Update to 5.2.0
* Wed Jun 27 2018 Miro Hrončok <mhroncok@redhat.com> - 5.1.1-3
- Fix the tkinter dependency
* Sat Jun 16 2018 Miro Hrončok <mhroncok@redhat.com> - 5.1.1-2
- Rebuilt for Python 3.7
* Thu Jan 25 2024 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-20
- Security fix for CVE-2023-50447
Resolves: RHEL-22240
* Fri Nov 10 2023 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-19
- Security fix for CVE-2023-44271
Resolves: RHEL-15460
* Fri Feb 11 2022 Charalampos Stratakis <cstratak@redhat.com> - 5.1.1-18
- Fixup for CVE-2022-22817
- Security fixes for CVE-2022-22815, CVE-2022-22816
Resolves: rhbz#2042511, rhbz#2042522
* Fri Feb 04 2022 Charalampos Stratakis <cstratak@redhat.com> - 5.1.1-17
- Fix for CVE-2022-22817
Resolves: rhbz#2042527
* Mon Aug 02 2021 Charalampos Stratakis <cstratak@redhat.com> - 5.1.1-16
- Fix for CVE-2021-34552
Resolves: rhbz#1982378
* Mon Jun 14 2021 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-15
- Fixes for CVE-2021-25288, CVE-2021-25287, CVE-2021-28675, CVE-2021-28676,
CVE-2021-28677 and CVE-2021-28678
Resolves: rhbz#1958231, rhbz#1958226, rhbz#1958240, rhbz#1958252, rhbz#1958257, rhbz#1958263
* Fri Apr 09 2021 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-14
- Fixes for CVE-2021-25290, CVE-2021-25292, CVE-2021-25293, CVE-2021-27921
CVE-2021-27922, and CVE-2021-27923
Resolves: rhbz#1934685 rhbz#1934699 rhbz#1934705 rhbz#1935384 rhbz#1935396 rhbz#1935401
* Thu Feb 18 2021 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-13
- Fixes for CVE-2020-35653 and CVE-2020-35655
Resolves: rhbz#1915420 rhbz#1915432
* Mon Jul 13 2020 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-12
- Fix for CVE-2020-11538
Resolves: rhbz#1852814
* Wed Mar 04 2020 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-11
- Fix for CVE-2020-5313
Resolves: rhbz#1789532
* Mon Feb 17 2020 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-10
- Bump and rebuild for gating to deliver CVE fixes
Resolves: rhbz#1789535
* Mon Feb 17 2020 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-9
- Fix for CVE-2020-5311 - out-of-bounds write in expandrow
Resolves: rhbz#1789535
* Fri Feb 14 2020 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-8
- Combined fixes for CVE-2020-5312 and CVE-2019-16865
Resolves: rhbz#1789533
Resolves: rhbz#1774066
* Tue Nov 27 2018 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-7
- Add upstream patch to solve build-time warning
- Move patches to dist-git
- Resolves: rhbz#1639348
* Mon Oct 15 2018 Lumír Balhar <lbalhar@redhat.com> - 5.1.1-6
- Add patches to fix issues found by static code analysis
- Resolves: rhbz#1602669
* Wed Jun 27 2018 Petr Viktorin <pviktori@redhat.com> - 5.1.1-5
- Correct dependency on python3-tkinter
* Tue Jun 19 2018 Petr Viktorin <pviktori@redhat.com> - 5.1.1-4
- Drop dependency on python3-olefile (breaking MicImagePlugin.py, FpxImagePlugin)
* Thu Jun 14 2018 Petr Viktorin <pviktori@redhat.com> - 5.1.1-3
- Remove the Python 2 subpackage
- Remove the libimagequant dependency
The imagequant library was only used to support a non-default image
quantization mode.
* Thu May 31 2018 Petr Viktorin <pviktori@redhat.com> - 5.1.1-2
- Remove the python2 version of docs
- Remove Qt subpackages
- Drop dependency on python2-olefile (breaking MicImagePlugin.py, FpxImagePlugin)
* Wed Apr 25 2018 Sandro Mani <manisandro@gmail.com> - 5.1.1-1
- Update to 5.1.1

@ -1,78 +0,0 @@
diff -rupN --no-dereference Pillow-10.0.1/setup.py Pillow-10.0.1-new/setup.py
--- Pillow-10.0.1/setup.py 2023-09-15 13:55:25.000000000 +0200
+++ Pillow-10.0.1-new/setup.py 2023-09-18 09:36:57.608297698 +0200
@@ -136,7 +136,7 @@ class RequiredDependencyException(Except
pass
-PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version
+PLATFORM_MINGW = "mingw32" in os.getenv("CC", "")
def _dbg(s, tp=None):
@@ -509,7 +509,7 @@ class pil_build_ext(build_ext):
#
# add platform directories
- if self.disable_platform_guessing:
+ if self.disable_platform_guessing or PLATFORM_MINGW:
pass
elif sys.platform == "cygwin":
@@ -600,7 +600,7 @@ class pil_build_ext(build_ext):
# FIXME: check /opt/stuff directories here?
# standard locations
- if not self.disable_platform_guessing:
+ if not self.disable_platform_guessing and not PLATFORM_MINGW:
_add_directory(library_dirs, "/usr/local/lib")
_add_directory(include_dirs, "/usr/local/include")
@@ -842,7 +842,7 @@ class pil_build_ext(build_ext):
if feature.xcb:
libs.append(feature.xcb)
defs.append(("HAVE_XCB", None))
- if sys.platform == "win32":
+ if sys.platform == "win32" or PLATFORM_MINGW:
libs.extend(["kernel32", "user32", "gdi32"])
if struct.unpack("h", b"\0\1")[0] == 1:
defs.append(("WORDS_BIGENDIAN", None))
@@ -857,6 +857,8 @@ class pil_build_ext(build_ext):
if feature.freetype:
srcs = []
libs = ["freetype"]
+ if sys.platform == "win32" or PLATFORM_MINGW:
+ libs.extend(["dl"])
defs = []
if feature.raqm:
if not feature.want_vendor("raqm"): # using system Raqm
@@ -879,7 +881,7 @@ class pil_build_ext(build_ext):
if feature.lcms:
extra = []
- if sys.platform == "win32":
+ if sys.platform == "win32" or PLATFORM_MINGW:
extra.extend(["user32", "gdi32"])
self._update_extension("PIL._imagingcms", [feature.lcms] + extra)
else:
@@ -898,7 +900,7 @@ class pil_build_ext(build_ext):
else:
self._remove_extension("PIL._webp")
- tk_libs = ["psapi"] if sys.platform in ("win32", "cygwin") else []
+ tk_libs = ["psapi"] if sys.platform in ("win32", "cygwin") or PLATFORM_MINGW else []
self._update_extension("PIL._imagingtk", tk_libs)
build_ext.build_extensions(self)
diff -rupN --no-dereference Pillow-10.0.1/src/libImaging/ImPlatform.h Pillow-10.0.1-new/src/libImaging/ImPlatform.h
--- Pillow-10.0.1/src/libImaging/ImPlatform.h 2023-09-15 13:55:25.000000000 +0200
+++ Pillow-10.0.1-new/src/libImaging/ImPlatform.h 2023-09-18 09:36:57.608297698 +0200
@@ -28,7 +28,7 @@
#if defined(_WIN32) || defined(__CYGWIN__) /* WIN */
#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
+#include <windows.h>
#ifdef __CYGWIN__
#undef _WIN64

@ -1 +0,0 @@
SHA512 (Pillow-10.0.1.tar.gz) = 27e2f0d86563c3b7f5a2e0ba2bbe98fc2cb2fa5871d0b6cbb5a0014e9d9eb03dde9969301419d806d1a22cd4881e624465a355ba9bc42b95746226e1f95712a9
Loading…
Cancel
Save