Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acf1c8559b | ||
|
|
c62b544084 | ||
|
|
d00d8571c2 | ||
|
|
bebc58974b | ||
|
|
7df66aa5f7 | ||
|
|
eb72bf86fb | ||
|
|
ef68acb403 | ||
|
|
777ef4f523 | ||
|
|
28fd74742b | ||
|
|
c8f70e19e1 | ||
|
|
41fae6d9e2 | ||
|
|
5fdf23ab1c | ||
|
|
6dcbf5bd96 | ||
|
|
5bdf54b5a7 | ||
|
|
bcaaf97f4f | ||
|
|
a4065997df |
25
CHANGES.rst
25
CHANGES.rst
@ -1,7 +1,30 @@
|
||||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
3.1.0 (unreleased)
|
||||
3.1.2 (2016-04-01)
|
||||
------------------
|
||||
|
||||
- Fixed an integer overflow in Jpeg2KEncode.c causing a buffer overflow. CVE-2016-3076
|
||||
[wiredfool]
|
||||
|
||||
|
||||
|
||||
3.1.1 (2016-02-04)
|
||||
------------------
|
||||
|
||||
- Fixed an integer overflow in Resample.c causing writes in the Python heap.
|
||||
[nedwill]
|
||||
|
||||
- Fixed a buffer overflow in PcdDecode.c causing a segfault when opening PhotoCD files. CVE-2016-2533
|
||||
[wiredfool]
|
||||
|
||||
- Fixed a buffer overflow in FliDecode.c causing a segfault when opening FLI files. CVE-2016-0775
|
||||
[wiredfool]
|
||||
|
||||
- Fixed a buffer overflow in TiffDecode.c causing an arbitrary amount of memory to be overwritten when opening a specially crafted invalid TIFF file. CVE-2016-0740
|
||||
[wiredfool]
|
||||
|
||||
3.1.0 (2016-01-04)
|
||||
------------------
|
||||
|
||||
- Fixing test failures on Python 2.6/Windows #1633
|
||||
|
||||
@ -45,6 +45,7 @@ recursive-include Tests *.msp
|
||||
recursive-include Tests *.pbm
|
||||
recursive-include Tests *.pcf
|
||||
recursive-include Tests *.pcx
|
||||
recursive-include Tests *.pcd
|
||||
recursive-include Tests *.pgm
|
||||
recursive-include Tests *.pil
|
||||
recursive-include Tests *.png
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
# ;-)
|
||||
|
||||
VERSION = '1.1.7' # PIL version
|
||||
PILLOW_VERSION = '3.1.0' # Pillow
|
||||
PILLOW_VERSION = '3.1.2' # Pillow
|
||||
|
||||
_plugins = ['BmpImagePlugin',
|
||||
'BufrStubImagePlugin',
|
||||
|
||||
16
Tests/check_fli_overflow.py
Normal file
16
Tests/check_fli_overflow.py
Normal file
@ -0,0 +1,16 @@
|
||||
from helper import unittest, PillowTestCase
|
||||
from PIL import Image
|
||||
|
||||
TEST_FILE = "Tests/images/fli_overflow.fli"
|
||||
|
||||
|
||||
class TestFliOverflow(PillowTestCase):
|
||||
def test_fli_overflow(self):
|
||||
|
||||
# this should not crash with a malloc error or access violation
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
23
Tests/check_libtiff_segfault.py
Normal file
23
Tests/check_libtiff_segfault.py
Normal file
@ -0,0 +1,23 @@
|
||||
from helper import unittest, PillowTestCase
|
||||
from PIL import Image
|
||||
|
||||
TEST_FILE = "Tests/images/libtiff_segfault.tif"
|
||||
|
||||
class TestLibtiffSegfault(PillowTestCase):
|
||||
def test_segfault(self):
|
||||
""" This test should not segfault. It will on Pillow <= 3.1.0 and
|
||||
libtiff >= 4.0.0
|
||||
"""
|
||||
|
||||
try:
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
except IOError:
|
||||
self.assertTrue(True, "Got expected IOError")
|
||||
except Exception:
|
||||
self.fail("Should have returned IOError")
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
BIN
Tests/images/fli_overflow.fli
Normal file
BIN
Tests/images/fli_overflow.fli
Normal file
Binary file not shown.
BIN
Tests/images/hopper.pcd
Normal file
BIN
Tests/images/hopper.pcd
Normal file
Binary file not shown.
BIN
Tests/images/libtiff_segfault.tif
Normal file
BIN
Tests/images/libtiff_segfault.tif
Normal file
Binary file not shown.
21
Tests/test_file_pcd.py
Normal file
21
Tests/test_file_pcd.py
Normal file
@ -0,0 +1,21 @@
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
from PIL import Image
|
||||
|
||||
class TestFilePcd(PillowTestCase):
|
||||
|
||||
def test_load_raw(self):
|
||||
im = Image.open('Tests/images/hopper.pcd')
|
||||
im.load() # should not segfault.
|
||||
|
||||
# Note that this image was created with a resized hopper
|
||||
# image, which was then converted to pcd with imagemagick
|
||||
# and the colors are wonky in Pillow. It's unclear if this
|
||||
# is a pillow or a convert issue, as other images not generated
|
||||
# from convert look find on pillow and not imagemagick.
|
||||
|
||||
#target = hopper().resize((768,512))
|
||||
#self.assert_image_similar(im, target, 10)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
36
Tests/test_image_resample.py
Normal file
36
Tests/test_image_resample.py
Normal file
@ -0,0 +1,36 @@
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
from PIL import Image
|
||||
|
||||
class TestImagingCoreResize(PillowTestCase):
|
||||
#see https://github.com/python-pillow/Pillow/issues/1710
|
||||
def test_overflow(self):
|
||||
im = hopper('L')
|
||||
xsize = 0x100000008 // 4
|
||||
ysize = 1000 # unimportant
|
||||
try:
|
||||
# any resampling filter will do here
|
||||
im.im.resize((xsize, ysize), Image.LINEAR)
|
||||
self.fail("Resize should raise MemoryError on invalid xsize")
|
||||
except MemoryError:
|
||||
self.assertTrue(True, "Should raise MemoryError")
|
||||
|
||||
def test_invalid_size(self):
|
||||
im = hopper()
|
||||
|
||||
im.resize((100,100))
|
||||
self.assertTrue(True, "Should not Crash")
|
||||
|
||||
try:
|
||||
im.resize((-100,100))
|
||||
self.fail("Resize should raise a value error on x negative size")
|
||||
except ValueError:
|
||||
self.assertTrue(True, "Should raise ValueError")
|
||||
|
||||
try:
|
||||
im.resize((100,-100))
|
||||
self.fail("Resize should raise a value error on y negative size")
|
||||
except ValueError:
|
||||
self.assertTrue(True, "Should raise ValueError")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
18
Tests/test_j2k_overflow.py
Normal file
18
Tests/test_j2k_overflow.py
Normal file
@ -0,0 +1,18 @@
|
||||
from PIL import Image
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
class TestJ2kEncodeOverflow(PillowTestCase):
|
||||
def test_j2k_overflow(self):
|
||||
|
||||
im = Image.new('RGBA', (1024, 131584))
|
||||
target = self.tempfile('temp.jpc')
|
||||
try:
|
||||
im.save(target)
|
||||
self.assertTrue(False, "Expected IOError, save succeeded?")
|
||||
except IOError as err:
|
||||
self.assertTrue(True, "IOError is expected")
|
||||
except Exception as err:
|
||||
self.assertTrue(False, "Expected IOError, got %s" %type(err))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -71,7 +71,7 @@
|
||||
* See the README file for information on usage and redistribution.
|
||||
*/
|
||||
|
||||
#define PILLOW_VERSION "3.1.0"
|
||||
#define PILLOW_VERSION "3.1.2"
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
@ -1532,6 +1532,10 @@ _resize(ImagingObject* self, PyObject* args)
|
||||
|
||||
imIn = self->image;
|
||||
|
||||
if (xsize < 1 || ysize < 1) {
|
||||
return ImagingError_ValueError("height and width must be > 0");
|
||||
}
|
||||
|
||||
if (imIn->xsize == xsize && imIn->ysize == ysize) {
|
||||
imOut = ImagingCopy(imIn);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
version: 3.1.0.{build}
|
||||
version: 3.1.2.{build}
|
||||
clone_folder: c:\pillow
|
||||
init:
|
||||
- ECHO %PYTHON%
|
||||
|
||||
78
docs/releasenotes/3.1.1.rst
Normal file
78
docs/releasenotes/3.1.1.rst
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
3.1.1
|
||||
=====
|
||||
|
||||
CVE-2016-0740 -- Buffer overflow in TiffDecode.c
|
||||
------------------------------------------------
|
||||
|
||||
Pillow 3.1.0 and earlier when linked against libtiff >= 4.0.0 on x64
|
||||
may overflow a buffer when reading a specially crafted tiff file.
|
||||
|
||||
Specifically, libtiff >= 4.0.0 changed the return type of
|
||||
``TIFFScanlineSize`` from ``int32`` to machine dependent
|
||||
``int32|64``. If the scanline is sized so that it overflows an
|
||||
``int32``, it may be interpreted as a negative number, which will then
|
||||
pass the size check in ``TiffDecode.c`` line 236. To do this, the
|
||||
logical scanline size has to be > 2gb, and for the test file, the
|
||||
allocated buffer size is 64k against a roughly 4gb scan line size. Any
|
||||
image data over 64k is written over the heap, causing a segfault.
|
||||
|
||||
This issue was found by security researcher FourOne.
|
||||
|
||||
|
||||
CVE-2016-0775 -- Buffer overflow in FliDecode.c
|
||||
-----------------------------------------------
|
||||
|
||||
In all versions of Pillow, dating back at least to the last PIL 1.1.7
|
||||
release, FliDecode.c has a buffer overflow error.
|
||||
|
||||
Around line 192::
|
||||
|
||||
case 16:
|
||||
/* COPY chunk */
|
||||
for (y = 0; y < state->ysize; y++) {
|
||||
UINT8* buf = (UINT8*) im->image[y];
|
||||
memcpy(buf+x, data, state->xsize);
|
||||
data += state->xsize;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
The memcpy has error where ``x`` is added to the target buffer
|
||||
address. ``X`` is used in several internal temporary variable roles,
|
||||
but can take a value up to the width of the image. ``Im->image[y]``
|
||||
is a set of row pointers to segments of memory that are the size of
|
||||
the row. At the max ``y``, this will write the contents of the line
|
||||
off the end of the memory buffer, causing a segfault.
|
||||
|
||||
This issue was found by Alyssa Besseling at Atlassian
|
||||
|
||||
CVE-2016-2533 -- Buffer overflow in PcdDecode.c
|
||||
----------------------------------------------
|
||||
|
||||
In all versions of Pillow, dating back at least to the last PIL 1.1.7
|
||||
release, ``PcdDecode.c`` has a buffer overflow error.
|
||||
|
||||
The ``state.buffer`` for ``PcdDecode.c`` is allocated based on a 3
|
||||
bytes per pixel sizing, where ``PcdDecode.c`` wrote into the buffer
|
||||
assuming 4 bytes per pixel. This writes 768 bytes beyond the end of
|
||||
the buffer into other Python object storage. In some cases, this
|
||||
causes a segfault, in others an internal Python malloc error.
|
||||
|
||||
Integer overflow in Resample.c
|
||||
------------------------------
|
||||
|
||||
If a large value was passed into the new size for an image, it is
|
||||
possible to overflow an int32 value passed into malloc.
|
||||
|
||||
kk = malloc(xsize * kmax * sizeof(float));
|
||||
...
|
||||
xbounds = malloc(xsize * 2 * sizeof(int));
|
||||
|
||||
``xsize`` is trusted user input. These multiplications can overflow,
|
||||
leading the malloc'd buffer to be undersized. These allocations are
|
||||
followed by a loop that writes out of bounds. This can lead to
|
||||
corruption on the heap of the Python process with attacker controlled
|
||||
float data.
|
||||
|
||||
This issue was found by Ned Williamson.
|
||||
43
docs/releasenotes/3.1.2.rst
Normal file
43
docs/releasenotes/3.1.2.rst
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
3.1.2
|
||||
=====
|
||||
|
||||
CVE-2016-3076 -- Buffer overflow in Jpeg2KEncode.c
|
||||
--------------------------------------------------
|
||||
|
||||
Pillow between 2.5.0 and 3.1.1 may overflow a buffer when writing
|
||||
large Jpeg2000 files, allowing for code execution or other memory
|
||||
corruption.
|
||||
|
||||
This occurs specifically in the function ``j2k_encode_entry``, at the line::
|
||||
|
||||
state->buffer = malloc (tile_width * tile_height * components * prec / 8);
|
||||
|
||||
|
||||
This vulnerability requires a particular value for ``height * width``
|
||||
such that ``height * width * components * precision`` overflows, at
|
||||
which point the malloc will be for a smaller value than expected. The
|
||||
buffer that is allocated will be ``((height * width * components *
|
||||
precision) mod (2^31) / 8)``, where components is 1-4 and precision is
|
||||
either 8 or
|
||||
16. Common values would be 4 components at precision 8 for a standard
|
||||
``RGBA`` image.
|
||||
|
||||
The unpackers then split an image that is laid out::
|
||||
|
||||
RGBARGBARGBA....
|
||||
|
||||
into::
|
||||
|
||||
|
||||
RRR.
|
||||
GGG.
|
||||
BBB.
|
||||
AAA.
|
||||
|
||||
|
||||
If this buffer is smaller than expected, the jpeg2k unpacker functions
|
||||
will write outside the allocation and onto the heap, corrupting
|
||||
memory.
|
||||
|
||||
This issue was found by Alyssa Besseling at Atlassian.
|
||||
@ -6,6 +6,8 @@ Release Notes
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
3.1.2
|
||||
3.1.1
|
||||
3.1.0
|
||||
3.0.0
|
||||
2.8.0
|
||||
|
||||
@ -185,7 +185,7 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
||||
/* COPY chunk */
|
||||
for (y = 0; y < state->ysize; y++) {
|
||||
UINT8* buf = (UINT8*) im->image[y];
|
||||
memcpy(buf+x, data, state->xsize);
|
||||
memcpy(buf, data, state->xsize);
|
||||
data += state->xsize;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -265,6 +265,10 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
||||
|
||||
unsigned prec = 8;
|
||||
unsigned bpp = 8;
|
||||
unsigned _overflow_scale_factor;
|
||||
/* SIZE_MAX is not working in the conditionals unless it's a typed
|
||||
variable */
|
||||
unsigned _SIZE__MAX = SIZE_MAX;
|
||||
|
||||
stream = opj_stream_default_create(OPJ_FALSE);
|
||||
|
||||
@ -335,6 +339,11 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
||||
}
|
||||
|
||||
image = opj_image_create(components, image_params, color_space);
|
||||
if (!image) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
goto quick_exit;
|
||||
}
|
||||
|
||||
/* Setup compression context */
|
||||
context->error_msg = NULL;
|
||||
@ -471,7 +480,24 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
||||
tiles_y = (im->ysize + (params.image_offset_y0 - params.cp_ty0)
|
||||
+ tile_height - 1) / tile_height;
|
||||
|
||||
/* check for integer overflow for the malloc line, checking any expression
|
||||
that may multiply either tile_width or tile_height */
|
||||
_overflow_scale_factor = components * prec;
|
||||
if (( tile_width > _SIZE__MAX / _overflow_scale_factor ) ||
|
||||
( tile_height > _SIZE__MAX / _overflow_scale_factor ) ||
|
||||
( tile_width > _SIZE__MAX / (tile_height * _overflow_scale_factor )) ||
|
||||
( tile_height > _SIZE__MAX / (tile_width * _overflow_scale_factor ))) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
goto quick_exit;
|
||||
}
|
||||
/* malloc check ok, checked for overflow above */
|
||||
state->buffer = malloc (tile_width * tile_height * components * prec / 8);
|
||||
if (!state->buffer) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
goto quick_exit;
|
||||
}
|
||||
|
||||
tile_ndx = 0;
|
||||
for (y = 0; y < tiles_y; ++y) {
|
||||
|
||||
@ -47,7 +47,7 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
||||
out[0] = ptr[x];
|
||||
out[1] = ptr[(x+4*state->xsize)/2];
|
||||
out[2] = ptr[(x+5*state->xsize)/2];
|
||||
out += 4;
|
||||
out += 3;
|
||||
}
|
||||
|
||||
state->shuffle((UINT8*) im->image[state->y],
|
||||
@ -62,7 +62,7 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
||||
out[0] = ptr[x+state->xsize];
|
||||
out[1] = ptr[(x+4*state->xsize)/2];
|
||||
out[2] = ptr[(x+5*state->xsize)/2];
|
||||
out += 4;
|
||||
out += 3;
|
||||
}
|
||||
|
||||
state->shuffle((UINT8*) im->image[state->y],
|
||||
|
||||
@ -138,11 +138,23 @@ ImagingResampleHorizontal(Imaging imIn, int xsize, int filter)
|
||||
/* maximum number of coofs */
|
||||
kmax = (int) ceil(support) * 2 + 1;
|
||||
|
||||
// check for overflow
|
||||
if (kmax > 0 && xsize > SIZE_MAX / kmax)
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
|
||||
// sizeof(float) should be greater than 0
|
||||
if (xsize * kmax > SIZE_MAX / sizeof(float))
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
|
||||
/* coefficient buffer */
|
||||
kk = malloc(xsize * kmax * sizeof(float));
|
||||
if ( ! kk)
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
|
||||
// sizeof(int) should be greater than 0 as well
|
||||
if (xsize > SIZE_MAX / (2 * sizeof(int)))
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
|
||||
xbounds = malloc(xsize * 2 * sizeof(int));
|
||||
if ( ! xbounds) {
|
||||
free(kk);
|
||||
|
||||
@ -169,7 +169,7 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
|
||||
char *filename = "tempfile.tif";
|
||||
char *mode = "r";
|
||||
TIFF *tiff;
|
||||
int size;
|
||||
tsize_t size;
|
||||
|
||||
|
||||
/* buffer is the encoded file, bytes is the length of the encoded file */
|
||||
@ -222,8 +222,8 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
|
||||
|
||||
if (clientstate->ifd){
|
||||
int rv;
|
||||
unsigned int ifdoffset = clientstate->ifd;
|
||||
TRACE(("reading tiff ifd %d\n", ifdoffset));
|
||||
uint32 ifdoffset = clientstate->ifd;
|
||||
TRACE(("reading tiff ifd %u\n", ifdoffset));
|
||||
rv = TIFFSetSubDirectory(tiff, ifdoffset);
|
||||
if (!rv){
|
||||
TRACE(("error in TIFFSetSubDirectory"));
|
||||
|
||||
@ -32,10 +32,13 @@ typedef struct {
|
||||
toff_t loc; /* toff_t == uint32 */
|
||||
tsize_t size; /* tsize_t == int32 */
|
||||
int fp;
|
||||
int ifd; /* offset of the ifd, used for multipage */
|
||||
TIFF *tiff; /* Used in write */
|
||||
uint32 ifd; /* offset of the ifd, used for multipage
|
||||
* Should be uint32 for libtiff 3.9.x
|
||||
* uint64 for libtiff 4.0.x
|
||||
*/
|
||||
TIFF *tiff; /* Used in write */
|
||||
toff_t eof;
|
||||
int flrealloc; /* may we realloc */
|
||||
int flrealloc;/* may we realloc */
|
||||
} TIFFSTATE;
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user