python3-pillow: upgrade 12.3.0 -> 13.0.0

Home Assistant 2026.11 pins Pillow 13.0.0.

Drop CVE-2026-112393.patch. It backported the upstream fix [1] to
12.3.0. That commit is an ancestor of the 13.0.0 tag, so 13.0.0
contains the fix, and the patch no longer applies.

[1] https://github.com/python-pillow/Pillow/commit/78c08bd4d9c08037f77465a61d90633f669013f4

AI-Generated: Claude Code, Claude Opus 5.5
Signed-off-by: Thomas Roos <thomas@roosesweb.de>
Signed-off-by: Khem Raj <khem.raj@oss.qualcomm.com>
This commit is contained in:
Thomas Roos authored and Khem Raj committed 2026-11-08 16:03:27 -08:00
1 parent 76e0e51b5d
commit 668a51afd4
2 files changed
+1 -126

No files matched your search

@@ -1,124 +0,0 @@
From 9b693ea929423da2bebef5523881ae3850c3877e Mon Sep 17 00:00:00 2001
From: Jeffrey 'Alex' Clark <aclark@aclark.net>
Date: Mon, 12 Oct 2026 21:18:41 -0400
Subject: [PATCH] Fix heap buffer overflow in _tiffWriteProc when saving TIFF
to BytesIO
libtiff seeks past the current end of data when writing the directory
and writes tag values there. _tiffWriteProc sized its realloc() from
state->size + size instead of the write position state->loc + size,
so the first out-of-line tag value written after such a seek could
overrun the allocation by up to the size of the directory. The bytes
skipped by the seek were also never zeroed, leaking stale heap
contents into the saved file.
Size the buffer from the write position and zero the space added by
realloc, matching the semantics of writing to a real file. Reject
writes where the position is still beyond the buffer for the
non-reallocating case.
Reported-by: carlini
Reported-by: Tomer-PL
Fixes: GHSA-332p-7hrc-92wf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CVE: CVE-2026-112393
Upstream-Status: Backport [https://github.com/python-pillow/Pillow/commit/78c08bd4d9c08037f77465a61d90633f669013f4]
Signed-off-by: Kris Gavvala <kris.gavvala@windriver.com>
---
Tests/test_file_libtiff.py | 36 ++++++++++++++++++++++++++++++++++++
src/libImaging/TiffDecode.c | 15 ++++++++++++++-
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py
index c9849a056..84e4299a1 100644
--- a/Tests/test_file_libtiff.py
+++ b/Tests/test_file_libtiff.py
@@ -757,6 +757,42 @@ class TestFileLibTiff(LibTiffTestCase):
save_bytesio("packbits")
save_bytesio("tiff_lzw")
+
+ def test_save_bytesio_seek_past_end(self, tmp_path: Path) -> None:
+ # When writing the directory, libtiff seeks past the end of the data
+ # written so far, and then writes the tag values there. Check that the
+ # in-memory write buffer grows to fit, and that the bytes that are
+ # skipped over are zero, as they would be when writing to a file.
+
+ # 7 distinct bytes per row: PackBits emits 8 bytes per row, so the strip
+ # plus the header are exactly 64 KiB, the initial size of the buffer
+ im = Image.new("L", (7, 8191))
+ im.putdata([(i * 37) & 0xFF for i in range(7 * 8191)])
+ kwargs: dict[str, Any] = {
+ "compression": "packbits",
+ "description": "D" * 65535,
+ }
+
+ outfile = tmp_path / "temp.tif"
+ im.save(outfile, **kwargs)
+ buffer_io = io.BytesIO()
+ im.save(buffer_io, format="tiff", **kwargs)
+ assert buffer_io.getvalue() == outfile.read_bytes()
+
+ # odd-length tag values, so that libtiff pads between them. Large enough
+ # that the buffer has to be reallocated
+ im = hopper().resize((300, 300))
+ kwargs = {
+ "compression": "tiff_lzw",
+ "description": "abcdef",
+ "software": "xyz0",
+ "tiffinfo": {315: "artist", 33432: "copyright!"},
+ }
+ im.save(outfile, **kwargs)
+ buffer_io = io.BytesIO()
+ im.save(buffer_io, format="tiff", **kwargs)
+ assert buffer_io.getvalue() == outfile.read_bytes()
+
def test_save_ycbcr(self, tmp_path: Path) -> None:
im = hopper("YCbCr")
outfile = tmp_path / "temp.tif"
diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c
index 72e0d7b30..e21327f02 100644
--- a/src/libImaging/TiffDecode.c
+++ b/src/libImaging/TiffDecode.c
@@ -88,10 +88,12 @@ _tiffWriteProc(thandle_t hdata, tdata_t buf, tsize_t size) {
dump_state(state);
to_write = min(size, state->size - (tsize_t)state->loc);
+ // libtiff may have seeked past the end of the buffer, e.g. when writing the
+ // directory, so the required size is based on the current location.
if (state->flrealloc && size > to_write) {
tdata_t new_data;
tsize_t newsize = state->size;
- while (newsize < (size + state->size)) {
+ while (newsize < (size + (tsize_t)state->loc)) {
if (newsize > INT_MAX - 64 * 1024) {
return 0;
}
@@ -105,9 +107,20 @@ _tiffWriteProc(thandle_t hdata, tdata_t buf, tsize_t size) {
// fail out
return 0;
}
+ // zero the new space, so that any bytes libtiff seeks over without
+ // writing are 0 in the output, as they would be in a file
+ memset((UINT8 *)new_data + state->size, 0, newsize - state->size);
state->data = new_data;
state->size = newsize;
to_write = size;
+ } else if (to_write < 0) {
+ TIFFError(
+ "_tiffWriteProc",
+ "Invalid Write at loc %" PRIu64 ", size: %" PRIu64,
+ state->loc,
+ (toff_t)state->size
+ );
+ return 0;
}
TRACE(("to_write: %d\n", (int)to_write));
--
2.43.0