For CVE-2016-9189
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-9189
Python Imaging allows context-dependent attackers to obtain sensitive
information by using the "crafted image file" approach, related to an
"Integer Overflow" issue affecting the Image.core.map_buffer in map.c
component.
Code changes based on those found upstream for Pillow at:
https://github.com/python-pillow/Pillow/pull/2146/commits/c50ebe6459a131a1ea8ca531f10da616d3ceaa0f
for:
map.c
https://github.com/python-pillow/Pillow/pull/2146/commits/445451c0b9347b50e0f603db33f196e207de470d
for:
PIL/Image.py
https://github.com/python-pillow/Pillow/pull/2146/commits/1a43da7a8bda884a597f3a1623364f4719d21c14
for:
PIL/EpsImagePlugin.py
PIL/IptcImagePlugin.py
PIL/JPegImagePlugin.py
PIL/PpmImagePlugin.py
_imaging.c
libImaging/File.c
--- Imaging-1.1.7/map.c.orig 2016-11-21 07:50:42.925380355 +0000
+++ Imaging-1.1.7/map.c 2016-11-21 07:53:34.182039527 +0000
@@ -339,8 +339,18 @@
stride = xsize * 4;
}
+ if (ysize > INT_MAX / stride) {
+ PyErr_SetString(PyExc_MemoryError, "Integer overflow in ysize");
+ return NULL;
+ }
+
size = ysize * stride;
+ if (offset > SIZE_MAX - size) {
+ PyErr_SetString(PyExc_MemoryError, "Integer overflow in offset");
+ return NULL;
+ }
+
/* check buffer size */
bytes = PyImaging_ReadBuffer(target, (const void**) &ptr);
if (bytes < 0) {
--- Imaging-1.1.7/PIL/Image.py.orig 2016-11-21 07:58:09.978008218 +0000
+++ Imaging-1.1.7/PIL/Image.py 2016-11-21 08:02:12.063288055 +0000
@@ -1740,6 +1740,25 @@
return Image()._new(core.wedge("L"))
+
+def _check_size(size):
+ """
+ Common check to enforce type and sanity check on size tuples
+
+ :param size: Should be a 2 tuple of (width, height)
+ :returns: True, or raises a ValueError
+ """
+
+ if not isinstance(size, tuple):
+ raise ValueError("Size must be a tuple")
+ if len(size) != 2:
+ raise ValueError("Size must be a tuple of length 2")
+ if size[0] <= 0 or size[1] <= 0:
+ raise ValueError("Width and Height must be > 0")
+
+ return True
+
+
##
# Creates a new image with the given mode and size.
#
@@ -1756,6 +1775,8 @@
def new(mode, size, color=0):
"Create a new image"
+ _check_size(size)
+
if color is None:
# don't initialize
return Image()._new(core.new(mode, size))
@@ -1792,6 +1813,8 @@
def fromstring(mode, size, data, decoder_name="raw", *args):
"Load image from string"
+ _check_size(size)
+
# may pass tuple instead of argument list
if len(args) == 1 and isTupleType(args[0]):
args = args[0]
@@ -1839,6 +1862,8 @@
def frombuffer(mode, size, data, decoder_name="raw", *args):
"Load image from string or buffer"
+ _check_size(size)
+
# may pass tuple instead of argument list
if len(args) == 1 and isTupleType(args[0]):
args = args[0]
--- Imaging-1.1.7/PIL/EpsImagePlugin.py.orig 2016-11-21 08:07:25.697709727 +0000
+++ Imaging-1.1.7/PIL/EpsImagePlugin.py 2016-11-21 08:12:17.177879463 +0000
@@ -74,12 +74,13 @@
status = gs.close()
if status:
raise IOError("gs failed (status %d)" % status)
- im = Image.core.open_ppm(file)
+ im = Image.open(outfile)
+ im.load()
finally:
try: os.unlink(file)
except: pass
- return im
+ return im.im.copy()
class PSFile:
--- Imaging-1.1.7/PIL/IptcImagePlugin.py.orig 2016-11-21 08:07:25.704128117 +0000
+++ Imaging-1.1.7/PIL/IptcImagePlugin.py 2016-11-21 08:14:00.062399442 +0000
@@ -192,14 +192,9 @@
o.close()
try:
- try:
- # fast
- self.im = Image.core.open_ppm(outfile)
- except:
- # slightly slower
- im = Image.open(outfile)
- im.load()
- self.im = im.im
+ _im = Image.open(outfile)
+ _im.load()
+ self.im = _im.im
finally:
try: os.unlink(outfile)
except: pass
--- Imaging-1.1.7/PIL/JpegImagePlugin.py.orig 2016-11-21 08:16:20.362838015 +0000
+++ Imaging-1.1.7/PIL/JpegImagePlugin.py 2016-11-21 08:19:06.852441772 +0000
@@ -352,7 +352,9 @@
raise ValueError("Invalid Filename")
try:
- self.im = Image.core.open_ppm(path)
+ _im = Image.open(path)
+ _im.load()
+ self.im = _im.im
finally:
try: os.unlink(path)
except: pass
--- Imaging-1.1.7/PIL/PpmImagePlugin.py.orig 2016-11-21 08:07:25.708512595 +0000
+++ Imaging-1.1.7/PIL/PpmImagePlugin.py 2016-11-21 08:19:55.525463844 +0000
@@ -93,11 +93,6 @@
self.fp.tell(),
(rawmode, 0, 1))]
- # ALTERNATIVE: load via builtin debug function
- # self.im = Image.core.open_ppm(self.filename)
- # self.mode = self.im.mode
- # self.size = self.im.size
-
#
# --------------------------------------------------------------------
--- Imaging-1.1.7/_imaging.c.orig 2016-11-21 08:07:25.710428542 +0000
+++ Imaging-1.1.7/_imaging.c 2016-11-21 08:21:03.690730505 +0000
@@ -684,17 +684,6 @@
}
static PyObject*
-_open_ppm(PyObject* self, PyObject* args)
-{
- char* filename;
-
- if (!PyArg_ParseTuple(args, "s", &filename))
- return NULL;
-
- return PyImagingNew(ImagingOpenPPM(filename));
-}
-
-static PyObject*
_blend(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
@@ -3215,9 +3204,6 @@
{"crc32", (PyCFunction)_crc32, 1},
{"getcodecstatus", (PyCFunction)_getcodecstatus, 1},
- /* Debugging stuff */
- {"open_ppm", (PyCFunction)_open_ppm, 1},
-
/* Special effects (experimental) */
#ifdef WITH_EFFECTS
{"effect_mandelbrot", (PyCFunction)_effect_mandelbrot, 1},
--- Imaging-1.1.7/libImaging/File.c.orig 2016-11-21 08:07:25.712276651 +0000
+++ Imaging-1.1.7/libImaging/File.c 2016-11-21 08:22:37.837774950 +0000
@@ -20,116 +20,6 @@
#include <ctype.h>
-Imaging
-ImagingOpenPPM(const char* infile)
-{
- FILE* fp;
- int i, c, v;
- char* mode;
- int x, y, max;
- Imaging im;
-
- if (!infile)
- return ImagingError_ValueError(NULL);
-
- fp = fopen(infile, "rb");
- if (!fp)
- return ImagingError_IOError();
-
- /* PPM magic */
- if (fgetc(fp) != 'P')
- goto error;
- switch (fgetc(fp)) {
- case '4': /* FIXME: 1-bit images are not yet supported */
- goto error;
- case '5':
- mode = "L";
- break;
- case '6':
- mode = "RGB";
- break;
- default:
- goto error;
- }
-
- i = 0;
- c = fgetc(fp);
-
- x = y = max = 0;
-
- while (i < 3) {
-
- /* Ignore optional comment fields */
- while (c == '\n') {
- c = fgetc(fp);
- if (c == '#') {
- do {
- c = fgetc(fp);
- if (c == EOF)
- goto error;
- } while (c != '\n');
- c = fgetc(fp);
- }
- }
-
- /* Skip forward to next value */
- while (isspace(c))
- c = fgetc(fp);
-
- /* And parse it */
- v = 0;
- while (isdigit(c)) {
- v = v * 10 + (c - '0');
- c = fgetc(fp);
- }
-
- if (c == EOF)
- goto error;
-
- switch (i++) {
- case 0:
- x = v;
- break;
- case 1:
- y = v;
- break;
- case 2:
- max = v;
- break;
- }
- }
-
- im = ImagingNew(mode, x, y);
- if (!im)
- return NULL;
-
- /* if (max != 255) ... FIXME: does anyone ever use this feature? */
-
- if (strcmp(im->mode, "L") == 0) {
-
- /* PPM "L" */
- for (y = 0; y < im->ysize; y++)
- if (fread(im->image[y], im->xsize, 1, fp) != 1)
- goto error;
-
- } else {
-
- /* PPM "RGB" or PyPPM mode */
- for (y = 0; y < im->ysize; y++)
- for (x = i = 0; x < im->xsize; x++, i += im->pixelsize)
- if (fread(im->image[y]+i, im->bands, 1, fp) != 1)
- goto error;
- }
-
- fclose(fp);
-
- return im;
-
-error:
- fclose(fp);
- return ImagingError_IOError();
-}
-
int
ImagingSaveRaw(Imaging im, FILE* fp)