Repository: python-pillow/Pillow

Issue:
GIF decoder can't handle zero-length extension blocks
To reproduce:

``` python
>>> import io, PIL.Image
>>> PIL.Image.open(io.BytesIO(bytes.fromhex('47 49 46 38 39 61 01 00 01 00 80 00 00 FF FF FF 00 00 00 21 FE 00 2C 00 00 00 00 01 00 01 00 00 02 02 44 01 00 3B')))
```



Discussion:
I presume that this image is able to be redistributed as a image in the test suite under Pillow's license?

Just to post information here, an EOFError is being raised at https://github.com/python-pillow/Pillow/blob/384d32969d3f6701477a616356ed5c180215103a/PIL/GifImagePlugin.py#L266

Resolving patch:
diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py
--- a/src/PIL/GifImagePlugin.py
+++ b/src/PIL/GifImagePlugin.py
@@ -201,7 +201,13 @@ def _seek(self, frame):
                     #
                     # comment extension
                     #
-                    info["comment"] = block
+                    while block:
+                        if "comment" in info:
+                            info["comment"] += block
+                        else:
+                            info["comment"] = block
+                        block = self.data()
+                    continue
                 elif i8(s) == 255:
                     #
                     # application extension
@@ -536,12 +542,14 @@ def _write_local_header(fp, im, offset, flags):
                  o8(0))
 
     if "comment" in im.encoderinfo and \
-       1 <= len(im.encoderinfo["comment"]) <= 255:
+       1 <= len(im.encoderinfo["comment"]):
         fp.write(b"!" +
-                 o8(254) +                # extension intro
-                 o8(len(im.encoderinfo["comment"])) +
-                 im.encoderinfo["comment"] +
-                 o8(0))
+                 o8(254))                 # extension intro
+        for i in range(0, len(im.encoderinfo["comment"]), 255):
+            subblock = im.encoderinfo["comment"][i:i+255]
+            fp.write(o8(len(subblock)) +
+                     subblock)
+        fp.write(o8(0))
     if "loop" in im.encoderinfo:
         number_of_loops = im.encoderinfo["loop"]
         fp.write(b"!" +


Test patch:
diff --git a/Tests/images/hopper_zero_comment_subblocks.gif b/Tests/images/hopper_zero_comment_subblocks.gif
new file mode 100644
Binary files /dev/null and b/Tests/images/hopper_zero_comment_subblocks.gif differ
diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py
--- a/Tests/test_file_gif.py
+++ b/Tests/test_file_gif.py
@@ -435,6 +435,23 @@ def test_comment(self):
 
         self.assertEqual(reread.info['comment'], im.info['comment'])
 
+    def test_comment_over_255(self):
+        out = self.tempfile('temp.gif')
+        im = Image.new('L', (100, 100), '#000')
+        comment = b"Test comment text"
+        while len(comment) < 256:
+            comment += comment
+        im.info['comment'] = comment
+        im.save(out)
+        reread = Image.open(out)
+
+        self.assertEqual(reread.info['comment'], comment)
+
+    def test_zero_comment_subblocks(self):
+        im = Image.open('Tests/images/hopper_zero_comment_subblocks.gif')
+        expected = Image.open(TEST_GIF)
+        self.assert_image_equal(im, expected)
+
     def test_version(self):
         out = self.tempfile('temp.gif')