From 3abe5e884b84b6421a0a7aa298046b5efba84481 Mon Sep 17 00:00:00 2001 From: Alexander Karpinsky Date: Mon, 26 Jul 2021 16:13:01 +0300 Subject: [PATCH 1/8] Add TypeError handling to pass corrupted dpi value in exif --- Tests/images/broken_exif_dpi.jpg | Bin 0 -> 7743 bytes Tests/test_file_jpeg.py | 9 +++++++++ src/PIL/JpegImagePlugin.py | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 Tests/images/broken_exif_dpi.jpg diff --git a/Tests/images/broken_exif_dpi.jpg b/Tests/images/broken_exif_dpi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c88b94630b598bb5d1e553c2c5d1177f4724ed3 GIT binary patch literal 7743 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 I@i2z}3!$1+p8x;= literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 68096e92d..15518756c 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -718,6 +718,15 @@ class TestFileJpeg: # This should return the default, and not raise a ZeroDivisionError assert im.info.get("dpi") == (72, 72) + def test_dpi_exif_string(self): + # Arrange + # 0x011A tag in this exif contains string '300300\x02' + with Image.open("Tests/images/broken_exif_dpi.jpg") as im: + + # Act / Assert + # This should return the default + assert im.info.get("dpi") == (72, 72) + def test_no_dpi_in_exif(self): # Arrange # This is photoshop-200dpi.jpg with resolution removed from EXIF: diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index b18e8126f..7d3cbbf49 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -168,7 +168,7 @@ def APP(self, marker): # 1 dpcm = 2.54 dpi dpi *= 2.54 self.info["dpi"] = dpi, dpi - except (KeyError, SyntaxError, ValueError, ZeroDivisionError): + except (TypeError, KeyError, SyntaxError, ValueError, ZeroDivisionError): # SyntaxError for invalid/unreadable EXIF # KeyError for dpi not included # ZeroDivisionError for invalid dpi rational value From feb3103bfd9dabe8e8e3bfa16c79de8f23ba6d8a Mon Sep 17 00:00:00 2001 From: Fariz Rahman Date: Fri, 30 Jul 2021 00:57:00 +0400 Subject: [PATCH 2/8] SGI save handler should not close output stream --- src/PIL/SgiImagePlugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index d0f7c9993..71e288022 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -193,8 +193,6 @@ def _save(im, fp, filename): for channel in im.split(): fp.write(channel.tobytes("raw", rawmode, 0, orientation)) - fp.close() - class SGI16Decoder(ImageFile.PyDecoder): _pulls_fd = True From 300f1ffc024fb7c7213d94d95963e0963d7e546f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 30 Jul 2021 20:29:07 +1000 Subject: [PATCH 3/8] Added test --- Tests/test_file_sgi.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index 0210dd4f1..6a5d8887d 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -73,6 +73,13 @@ def test_write(tmp_path): img.save(out, format="sgi") assert_image_equal_tofile(img, out) + out = str(tmp_path / "fp.sgi") + with open(out, "wb") as fp: + img.save(fp) + assert_image_equal_tofile(img, out) + + assert not fp.closed + for mode in ("L", "RGB", "RGBA"): roundtrip(hopper(mode)) From 9125631ff04e8925d1ae0f7a310728005f22f73f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 30 Jul 2021 20:25:07 +1000 Subject: [PATCH 4/8] Added flush --- src/PIL/SgiImagePlugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 71e288022..5f1ef6edc 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -193,6 +193,9 @@ def _save(im, fp, filename): for channel in im.split(): fp.write(channel.tobytes("raw", rawmode, 0, orientation)) + if hasattr(fp, "flush"): + fp.flush() + class SGI16Decoder(ImageFile.PyDecoder): _pulls_fd = True From eee0953bb33a5648c454b4628801034071d4f07d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 31 Jul 2021 13:55:28 +1000 Subject: [PATCH 5/8] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a3527de7d..6c2ba804d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 8.4.0 (unreleased) ------------------ +- Do not close file pointer when saving SGI images #5645 + [farizrahman4u, radarhere] + - Deprecate ImagePalette size parameter #5641 [radarhere, hugovk] From 3cf2fffa335921221dd5bb0f26ff7befaeb17934 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 Aug 2021 09:54:40 +1000 Subject: [PATCH 6/8] Added release notes for #5641 --- docs/releasenotes/8.4.0.rst | 41 +++++++++++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 42 insertions(+) create mode 100644 docs/releasenotes/8.4.0.rst diff --git a/docs/releasenotes/8.4.0.rst b/docs/releasenotes/8.4.0.rst new file mode 100644 index 000000000..42e57745b --- /dev/null +++ b/docs/releasenotes/8.4.0.rst @@ -0,0 +1,41 @@ +8.4.0 +----- + +API Changes +=========== + +Deprecations +^^^^^^^^^^^^ + +ImagePalette size parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``size`` parameter will be removed in Pillow 10.0.0 (2023-01-02). + +Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular lengths by +default, and the size parameter could be used to override that. Pillow 8.3.0 removed +the default required length, also removing the need for the size parameter. + +API Additions +============= + +TODO +^^^^ + +TODO + +Security +======== + +TODO +^^^^ + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index e60d26ec7..55c51a401 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -14,6 +14,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 8.4.0 8.3.1 8.3.0 8.2.0 From 8ab06b4daccedd23880f5ffa7bf5d734d46fe1d8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 Aug 2021 21:06:33 +1000 Subject: [PATCH 7/8] Updated comment [ci skip] --- src/PIL/JpegImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 7d3cbbf49..b8674eeed 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -172,7 +172,7 @@ def APP(self, marker): # SyntaxError for invalid/unreadable EXIF # KeyError for dpi not included # ZeroDivisionError for invalid dpi rational value - # ValueError for dpi being an invalid float + # ValueError or TypeError for dpi being an invalid float self.info["dpi"] = 72, 72 From 9f42a17b2d03631a8131ae0f7ebc27bed6309482 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 2 Aug 2021 21:42:03 +1000 Subject: [PATCH 8/8] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6c2ba804d..13afddc62 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 8.4.0 (unreleased) ------------------ +- Catch TypeError from corrupted DPI value in EXIF #5639 + [homm, radarhere] + - Do not close file pointer when saving SGI images #5645 [farizrahman4u, radarhere]