From 77da73c90fc9d3d5715ccf1361bda311350a394f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Apr 2016 03:08:22 -0700 Subject: [PATCH 1/2] Catch struct.errors when verifying png files, convert to SyntaxErrors, fixes #1755 --- PIL/PngImagePlugin.py | 40 ++++++++++++++++++++++++---------------- Tests/test_file_png.py | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index d6778821b..87847eebd 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -36,6 +36,7 @@ from __future__ import print_function import logging import re import zlib +import struct from PIL import Image, ImageFile, ImagePalette, _binary @@ -106,17 +107,20 @@ class ChunkStream(object): def read(self): "Fetch a new chunk. Returns header information." - - if self.queue: - cid, pos, length = self.queue[-1] - del self.queue[-1] - self.fp.seek(pos) - else: - s = self.fp.read(8) - cid = s[4:] - pos = self.fp.tell() - length = i32(s) - + cid = None + try: + if self.queue: + cid, pos, length = self.queue[-1] + del self.queue[-1] + self.fp.seek(pos) + else: + s = self.fp.read(8) + cid = s[4:] + pos = self.fp.tell() + length = i32(s) + except struct.error: + SyntaxError("truncated PNG file (chunk %s)" % repr(cid)) + if not is_cid(cid): raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) @@ -138,11 +142,15 @@ class ChunkStream(object): def crc(self, cid, data): "Read and verify checksum" - crc1 = Image.core.crc32(data, Image.core.crc32(cid)) - crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) - if crc1 != crc2: - raise SyntaxError("broken PNG file" - "(bad header checksum in %s)" % cid) + try: + crc1 = Image.core.crc32(data, Image.core.crc32(cid)) + crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) + if crc1 != crc2: + raise SyntaxError("broken PNG file (bad header checksum in %s)" + % cid) + except struct.error: + raise SyntaxError("broken PNG file (incomplete checksum in %s)" + % cid) def crc_skip(self, cid, data): "Read checksum. Used if the C module is not present" diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index cb72b2d73..63ec8a303 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -253,6 +253,22 @@ class TestFilePng(PillowTestCase): im.load() self.assertRaises(RuntimeError, im.verify) + def test_verify_struct_error(self): + # Check open/load/verify exception (#1755) + + # offsets to test, -10: breaks in i32() in read. + # -13: breaks in crc, txt chunk. + # -14: malformed chunk + + for offset in (-10, -13, -14): + with open(TEST_PNG_FILE,'rb') as f: + test_file = f.read()[:offset] + + im = Image.open(BytesIO(test_file)) + self.assertTrue(im.fp is not None) + self.assertRaises(SyntaxError, im.verify) + + def test_roundtrip_dpi(self): # Check dpi roundtripping From 90378c8298313bf9d4e2a99473e6210b859f9b43 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Apr 2016 05:40:37 -0700 Subject: [PATCH 2/2] Fixing compatibility with the truncated images tests --- PIL/PngImagePlugin.py | 28 +++++++++++++++------------- Tests/test_file_png.py | 4 ++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 87847eebd..cc58fbbc4 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -108,18 +108,16 @@ class ChunkStream(object): def read(self): "Fetch a new chunk. Returns header information." cid = None - try: - if self.queue: - cid, pos, length = self.queue[-1] - del self.queue[-1] - self.fp.seek(pos) - else: - s = self.fp.read(8) - cid = s[4:] - pos = self.fp.tell() - length = i32(s) - except struct.error: - SyntaxError("truncated PNG file (chunk %s)" % repr(cid)) + + if self.queue: + cid, pos, length = self.queue[-1] + del self.queue[-1] + self.fp.seek(pos) + else: + s = self.fp.read(8) + cid = s[4:] + pos = self.fp.tell() + length = i32(s) if not is_cid(cid): raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) @@ -165,7 +163,11 @@ class ChunkStream(object): cids = [] while True: - cid, pos, length = self.read() + try: + cid, pos, length = self.read() + except struct.error: + raise IOError("truncated PNG file") + if cid == endchunk: break self.crc(cid, ImageFile._safe_read(self.fp, length)) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 63ec8a303..f6a47c464 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -256,7 +256,7 @@ class TestFilePng(PillowTestCase): def test_verify_struct_error(self): # Check open/load/verify exception (#1755) - # offsets to test, -10: breaks in i32() in read. + # offsets to test, -10: breaks in i32() in read. (IOError) # -13: breaks in crc, txt chunk. # -14: malformed chunk @@ -266,7 +266,7 @@ class TestFilePng(PillowTestCase): im = Image.open(BytesIO(test_file)) self.assertTrue(im.fp is not None) - self.assertRaises(SyntaxError, im.verify) + self.assertRaises((IOError, SyntaxError), im.verify) def test_roundtrip_dpi(self):