Merge pull request #5377 from hugovk/security-and-release-notes

Security fixes for 8.2.0
This commit is contained in:
Hugo van Kemenade 2021-04-01 20:00:22 +03:00 committed by GitHub
commit ee635befc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 294 additions and 99 deletions

View File

@ -52,6 +52,7 @@ class TestDecompressionBomb:
with Image.open(TEST_FILE):
pass
@pytest.mark.xfail(reason="different exception")
def test_exception_ico(self):
with pytest.raises(Image.DecompressionBombError):
with Image.open("Tests/images/decompression_bomb.ico"):

View File

@ -312,7 +312,7 @@ def test_apng_syntax_errors():
exception = e
assert exception is None
with pytest.raises(SyntaxError):
with pytest.raises(OSError):
with Image.open("Tests/images/apng/syntax_num_frames_high.png") as im:
im.seek(im.n_frames - 1)
im.load()

View File

@ -1,3 +1,5 @@
import pytest
from PIL import Image
from .helper import assert_image_equal_tofile
@ -16,3 +18,22 @@ def test_load_blp2_dxt1():
def test_load_blp2_dxt1a():
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
assert_image_equal_tofile(im, "Tests/images/blp/blp2_dxt1a.png")
@pytest.mark.parametrize(
"test_file",
[
"Tests/images/timeout-060745d3f534ad6e4128c51d336ea5489182c69d.blp",
"Tests/images/timeout-31c8f86233ea728339c6e586be7af661a09b5b98.blp",
"Tests/images/timeout-60d8b7c8469d59fc9ffff6b3a3dc0faeae6ea8ee.blp",
"Tests/images/timeout-8073b430977660cdd48d96f6406ddfd4114e69c7.blp",
"Tests/images/timeout-bba4f2e026b5786529370e5dfe9a11b1bf991f07.blp",
"Tests/images/timeout-d6ec061c4afdef39d3edf6da8927240bb07fe9b7.blp",
"Tests/images/timeout-ef9112a065e7183fa7faa2e18929b03e44ee16bf.blp",
],
)
def test_crashes(test_file):
with open(test_file, "rb") as f:
with Image.open(f) as im:
with pytest.raises(OSError):
im.load()

View File

@ -264,3 +264,15 @@ def test_emptyline():
assert image.mode == "RGB"
assert image.size == (460, 352)
assert image.format == "EPS"
@pytest.mark.timeout(timeout=5)
@pytest.mark.parametrize(
"test_file",
["Tests/images/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],
)
def test_timeout(test_file):
with open(test_file, "rb") as f:
with pytest.raises(Image.UnidentifiedImageError):
with Image.open(f):
pass

View File

@ -123,3 +123,18 @@ def test_seek():
im.seek(50)
assert_image_equal_tofile(im, "Tests/images/a_fli.png")
@pytest.mark.parametrize(
"test_file",
[
"Tests/images/timeout-9139147ce93e20eb14088fe238e541443ffd64b3.fli",
"Tests/images/timeout-bff0a9dc7243a8e6ede2408d2ffa6a9964698b87.fli",
],
)
@pytest.mark.timeout(timeout=3)
def test_timeouts(test_file):
with open(test_file, "rb") as f:
with Image.open(f) as im:
with pytest.raises(OSError):
im.load()

View File

@ -231,3 +231,19 @@ def test_parser_feed():
# Assert
assert p.image.size == (640, 480)
@pytest.mark.parametrize(
"test_file",
[
"Tests/images/crash-4fb027452e6988530aa5dabee76eecacb3b79f8a.j2k",
"Tests/images/crash-7d4c83eb92150fb8f1653a697703ae06ae7c4998.j2k",
"Tests/images/crash-ccca68ff40171fdae983d924e127a721cab2bd50.j2k",
"Tests/images/crash-d2c93af851d3ab9a19e34503626368b2ecde9c03.j2k",
],
)
def test_crashes(test_file):
with open(test_file, "rb") as f:
with Image.open(f) as im:
# Valgrind should not complain here
im.load()

View File

@ -130,3 +130,25 @@ def test_combined_larger_than_size():
with pytest.raises(OSError):
with Image.open("Tests/images/combined_larger_than_size.psd"):
pass
@pytest.mark.parametrize(
"test_file,raises",
[
(
"Tests/images/timeout-1ee28a249896e05b83840ae8140622de8e648ba9.psd",
Image.UnidentifiedImageError,
),
(
"Tests/images/timeout-598843abc37fc080ec36a2699ebbd44f795d3a6f.psd",
Image.UnidentifiedImageError,
),
("Tests/images/timeout-c8efc3fded6426986ba867a399791bae544f59bc.psd", OSError),
("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError),
],
)
def test_crashes(test_file, raises):
with open(test_file, "rb") as f:
with pytest.raises(raises):
with Image.open(f):
pass

View File

@ -625,9 +625,9 @@ class TestFileTiff:
)
def test_string_dimension(self):
# Assert that an error is raised if one of the dimensions is a string
with pytest.raises(ValueError):
with Image.open("Tests/images/string_dimension.tiff"):
pass
with Image.open("Tests/images/string_dimension.tiff") as im:
with pytest.raises(OSError):
im.load()
@pytest.mark.skipif(not is_win32(), reason="Windows only")

View File

@ -997,3 +997,16 @@ def test_freetype_deprecation(monkeypatch):
# Act / Assert
with pytest.warns(DeprecationWarning):
ImageFont.truetype(FONT_PATH, FONT_SIZE)
@pytest.mark.parametrize(
"test_file",
[
"Tests/fonts/oom-e8e927ba6c0d38274a37c1567560eb33baf74627.ttf",
],
)
def test_oom(test_file):
with open(test_file, "rb") as f:
font = ImageFont.truetype(BytesIO(f.read()))
with pytest.raises(Image.DecompressionBombError):
font.getmask("Test Text")

View File

@ -4,12 +4,6 @@
Deprecations
============
Tk/Tcl 8.4
^^^^^^^^^^
Support for Tk/Tcl 8.4 is deprecated and will be removed in Pillow 10.0.0 (2023-01-02),
when Tk/Tcl 8.5 will be the minimum supported.
Categories
^^^^^^^^^^
@ -20,6 +14,12 @@ along with the related ``Image.NORMAL``, ``Image.SEQUENCE`` and
To determine if an image has multiple frames or not,
``getattr(im, "is_animated", False)`` can be used instead.
Tk/Tcl 8.4
^^^^^^^^^^
Support for Tk/Tcl 8.4 is deprecated and will be removed in Pillow 10.0.0 (2023-01-02),
when Tk/Tcl 8.5 will be the minimum supported.
API Changes
===========
@ -48,7 +48,7 @@ These changes only affect :py:meth:`~PIL.Image.Image.getexif`, introduced in Pil
Image._MODEINFO
^^^^^^^^^^^^^^^
This internal dictionary has been deprecated by a comment since PIL, and is now
This internal dictionary had been deprecated by a comment since PIL, and is now
removed. Instead, ``Image.getmodebase()``, ``Image.getmodetype()``,
``Image.getmodebandnames()``, ``Image.getmodebands()`` or ``ImageMode.getmode()``
can be used.
@ -56,6 +56,20 @@ can be used.
API Additions
=============
getxmp() for JPEG images
^^^^^^^^^^^^^^^^^^^^^^^^
A new method has been added to return
`XMP data <https://en.wikipedia.org/wiki/Extensible_Metadata_Platform>`_ for JPEG
images. It reads the XML data into a dictionary of names and values.
For example::
>>> from PIL import Image
>>> with Image.open("Tests/images/xmp_test.jpg") as im:
>>> print(im.getxmp())
{'RDF': {}, 'Description': {'Version': '10.4', 'ProcessVersion': '10.0', ...}, ...}
ImageDraw.rounded_rectangle
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -71,17 +85,13 @@ create a circle, but not any other ellipse.
draw = ImageDraw.Draw(im)
draw.rounded_rectangle(xy=(10, 20, 190, 180), radius=30, fill="red")
ImageShow.IPythonViewer
^^^^^^^^^^^^^^^^^^^^^^^
ImageOps.autocontrast: preserve_tone
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If IPython is present, this new :py:class:`PIL.ImageShow.Viewer` subclass will be
registered. It displays images on all IPython frontends. This will be helpful
to users of Google Colab, allowing ``im.show()`` to display images.
It is lower in priority than the other default :py:class:`PIL.ImageShow.Viewer`
instances, so it will only be used by ``im.show()`` or :py:func:`.ImageShow.show()`
if none of the other viewers are available. This means that the behaviour of
:py:class:`PIL.ImageShow` will stay the same for most Pillow users.
The default behaviour of :py:meth:`~PIL.ImageOps.autocontrast` is to normalize
separate histograms for each color channel, changing the tone of the image. The new
``preserve_tone`` argument keeps the tone unchanged by using one luminance histogram
for all channels.
ImageShow.GmDisplayViewer
^^^^^^^^^^^^^^^^^^^^^^^^^
@ -95,6 +105,18 @@ counterpart. Thus, if both ImageMagick and GraphicsMagick are installed,
ImageMagick, i.e the behaviour stays the same for Pillow users having
ImageMagick installed.
ImageShow.IPythonViewer
^^^^^^^^^^^^^^^^^^^^^^^
If IPython is present, this new :py:class:`PIL.ImageShow.Viewer` subclass will be
registered. It displays images on all IPython frontends. This will be helpful
to users of Google Colab, allowing ``im.show()`` to display images.
It is lower in priority than the other default :py:class:`PIL.ImageShow.Viewer`
instances, so it will only be used by ``im.show()`` or :py:func:`.ImageShow.show()`
if none of the other viewers are available. This means that the behaviour of
:py:class:`PIL.ImageShow` will stay the same for most Pillow users.
Saving TIFF with ICC profile
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -104,32 +126,59 @@ be specified through a keyword argument::
im.save("out.tif", icc_profile=...)
ImageOps.autocontrast: preserve_tone
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The default behaviour of :py:meth:`~PIL.ImageOps.autocontrast` is to normalize
separate histograms for each color channel, changing the tone of the image. The new
``preserve_tone`` argument keeps the tone unchanged by using one luminance histogram
for all channels.
getxmp() for JPEG images
^^^^^^^^^^^^^^^^^^^^^^^^
A new method has been added to return
`XMP data <https://en.wikipedia.org/wiki/Extensible_Metadata_Platform>`_ for JPEG
images. It reads the XML data into a dictionary of names and values.
For example::
>>> from PIL import Image
>>> with Image.open("Tests/images/xmp_test.jpg") as im:
>>> print(im.getxmp())
{'RDF': {}, 'Description': {'Version': '10.4', 'ProcessVersion': '10.0', ...}, ...}
Security
========
TODO
These were all found with `OSS-Fuzz`_.
:cve:`CVE-2021-25287`, :cve:`CVE-2021-25288`: Fix OOB read in Jpeg2KDecode
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* For J2k images with multiple bands, it's legal to have different widths for each band,
e.g. 1 byte for ``L``, 4 bytes for ``A``.
* This dates to Pillow 2.4.0.
:cve:`CVE-2021-28675`: Fix DOS in PsdImagePlugin
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* :py:class:`.PsdImagePlugin.PsdImageFile` did not sanity check the number of input
layers with regard to the size of the data block, this could lead to a
denial-of-service on :py:meth:`~PIL.Image.open` prior to
:py:meth:`~PIL.Image.Image.load`.
* This dates to the PIL fork.
:cve:`CVE-2021-28676`: Fix FLI DOS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ``FliDecode.c`` did not properly check that the block advance was non-zero,
potentially leading to an infinite loop on load.
* This dates to the PIL fork.
:cve:`CVE-2021-28677`: Fix EPS DOS on _open
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The readline used in EPS has to deal with any combination of ``\r`` and ``\n`` as line
endings. It accidentally used a quadratic method of accumulating lines while looking
for a line ending.
* A malicious EPS file could use this to perform a denial-of-service of Pillow in the
open phase, before an image was accepted for opening.
* This dates to the PIL fork.
:cve:`CVE-2021-28678`: Fix BLP DOS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ``BlpImagePlugin`` did not properly check that reads after jumping to file offsets
returned data. This could lead to a denial-of-service where the decoder could be run a
large number of times on empty data.
* This dates to Pillow 5.1.0.
Fix memory DOS in ImageFont
^^^^^^^^^^^^^^^^^^^^^^^^^^^
* A corrupt or specially crafted TTF font could have font metrics that lead to
unreasonably large sizes when rendering text in font. ``ImageFont.py`` did not check
the image size before allocating memory for it.
* This dates to the PIL fork.
Other Changes
=============
@ -146,6 +195,12 @@ The pixel data is encoded using the format specified in the `CompuServe GIF stan
The older encoder used a variant of run-length encoding that was compatible but less
efficient.
GraphicsMagick
^^^^^^^^^^^^^^
The test suite can now be run on systems which have GraphicsMagick_ but not
ImageMagick_ installed. If both are installed, the tests prefer ImageMagick.
Libraqm and FriBiDi linking
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -170,11 +225,6 @@ PyQt6
Support has been added for PyQt6. If it is installed, it will be used instead of
PySide6, PyQt5 or PySide2.
GraphicsMagick
^^^^^^^^^^^^^^
The test suite can now be run on systems which have GraphicsMagick_ but not
ImageMagick_ installed. If both are installed, the tests prefer ImageMagick.
.. _GraphicsMagick: http://www.graphicsmagick.org/
.. _ImageMagick: https://imagemagick.org/
.. _OSS-Fuzz: https://github.com/google/oss-fuzz

View File

@ -286,33 +286,36 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
raise OSError("Truncated Blp file") from e
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):
@ -324,7 +327,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))
@ -346,10 +349,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)
@ -370,7 +373,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))
@ -384,20 +387,20 @@ 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), alpha=bool(self._blp_alpha_depth)
self._safe_read(linesize), alpha=bool(self._blp_alpha_depth)
):
data += d
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(

View File

@ -170,12 +170,12 @@ class PSFile:
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)
@ -183,7 +183,7 @@ class PSFile:
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):

View File

@ -545,12 +545,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 cannot 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))
@ -558,6 +564,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)

View File

@ -669,6 +669,7 @@ class FreeTypeFont:
)
size = size[0] + stroke_width * 2, size[1] + stroke_width * 2
offset = offset[0] - stroke_width, offset[1] - stroke_width
Image._decompression_bomb_check(size)
im = fill("RGBA" if mode == "RGBA" else "L", size, 0)
self.font.render(
text, im.id, mode, direction, features, language, stroke_width, ink

View File

@ -119,7 +119,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)
self.n_frames = len(self.layers)
self.is_animated = self.n_frames > 1
@ -171,11 +172,20 @@ class PsdImageFile(ImageFile.ImageFile):
self.__fp = None
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))
@ -186,7 +196,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
@ -219,16 +230,16 @@ 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:
file.seek(length - 16, io.SEEK_CUR)
fp.seek(length - 16, io.SEEK_CUR)
combined += length + 4
length = i32(read(4))
if length:
file.seek(length, io.SEEK_CUR)
fp.seek(length, io.SEEK_CUR)
combined += length + 4
length = i8(read(1))
@ -238,7 +249,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
@ -246,7 +257,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

View File

@ -243,6 +243,11 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt
return -1;
}
advance = I32(ptr);
if (advance == 0 ) {
// If there's no advance, we're in an infinite loop
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
if (advance < 0 || advance > bytes) {
state->errcode = IMAGING_CODEC_OVERRUN;
return -1;

View File

@ -644,7 +644,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
j2k_unpacker_t unpack = NULL;
size_t buffer_size = 0, tile_bytes = 0;
unsigned n, tile_height, tile_width;
int components;
int total_component_width = 0;
stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
@ -814,23 +814,40 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
goto quick_exit;
}
/* Sometimes the tile_info.datasize we get back from openjpeg
is less than numcomps*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;
components = tile_info.nb_comps == 3 ? 4 : tile_info.nb_comps;
if ((tile_width > UINT_MAX / components) ||
(tile_height > UINT_MAX / components) ||
(tile_width > UINT_MAX / (tile_height * components)) ||
(tile_height > UINT_MAX / (tile_width * components))) {
if (tile_info.nb_comps != image->numcomps) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
tile_bytes = tile_width * tile_height * components;
/* 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;