| Message ID | 20261102211142.319090-1-kris.gavvala@windriver.com |
|---|---|
| State | Under Review |
| Headers | show |
| Series | [meta-python] python3-pillow: patch CVE-2026-112393 | expand |
There is a pillow 13.0.0 release and I would rather see an upgrade than patches. If this is for a stable release branch, please include the branch in the subject line, e.g. [wrynose]. On Mon, Nov 2, 2026 at 13:12 Gavvala, Kris via openembedded-devel <Kris.Gavvala=windriver.com@lists.openembedded.org> wrote: > This patch applies the upstream fix and related tests in commit [1] > to address the issue in [2]. > > [1] > https://github.com/python-pillow/Pillow/commit/78c08bd4d9c08037f77465a61d90633f669013f4 > [2] > https://github.com/python-pillow/Pillow/security/advisories/GHSA-332p-7hrc-92wf > > Signed-off-by: Kris Gavvala <kris.gavvala@windriver.com> > --- > .../python3-pillow/CVE-2026-112393.patch | 124 ++++++++++++++++++ > .../python/python3-pillow_12.3.0.bb | 1 + > 2 files changed, 125 insertions(+) > create mode 100644 > meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > > diff --git > a/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > b/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > new file mode 100644 > index 0000000000..cb9be3b33e > --- /dev/null > +++ > b/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > @@ -0,0 +1,124 @@ > +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 > + > diff --git a/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > b/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > index 1c6668e53d..e4826da1a6 100644 > --- a/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > +++ b/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > @@ -7,6 +7,7 @@ LIC_FILES_CHKSUM = > "file://LICENSE;md5=a6f0ac3777cfc96ded1b825e32ae7c99" > > SRC_URI = "git:// > github.com/python-pillow/Pillow.git;branch=main;protocol=https;tag=${PV} > <http://github.com/python-pillow/Pillow.git;branch=main;protocol=https;tag=$%7BPV%7D> > \ > file://0001-support-cross-compiling.patch \ > + file://CVE-2026-112393.patch \ > " > SRCREV = "bb1d8e8ab8d29048624d96e3ee53cecf7c13d13d" > > -- > 2.43.0 > > > -=-=-=-=-=-=-=-=-=-=-=- > Links: You receive all messages sent to this group. > View/Reply Online (#131753): > https://lists.openembedded.org/g/openembedded-devel/message/131753 > Mute This Topic: https://lists.openembedded.org/mt/122066176/924729 > Group Owner: openembedded-devel+owner@lists.openembedded.org > Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [ > ticotimo@gmail.com] > -=-=-=-=-=-=-=-=-=-=-=- > >
Understood. I was going to send the patch to master before backporting to stable releases, but if upgrading is better for master then I will do that. Thanks, Kris Gavvala On 11/2/26 3:37 PM, Tim Orling wrote: > ** > *CAUTION: This email comes from a non Wind River email account!* > Do not click links or open attachments unless you recognize the sender > and know the content is safe. > There is a pillow 13.0.0 release and I would rather see an upgrade > than patches. If this is for a stable release branch, please include > the branch in the subject line, e.g. [wrynose]. > > On Mon, Nov 2, 2026 at 13:12 Gavvala, Kris via openembedded-devel > <Kris.Gavvala=windriver.com@lists.openembedded.org> wrote: > > This patch applies the upstream fix and related tests in commit [1] > to address the issue in [2]. > > [1] > https://github.com/python-pillow/Pillow/commit/78c08bd4d9c08037f77465a61d90633f669013f4 > <https://urldefense.com/v3/__https://github.com/python-pillow/Pillow/commit/78c08bd4d9c08037f77465a61d90633f669013f4__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCIaCNLIw$> > [2] > https://github.com/python-pillow/Pillow/security/advisories/GHSA-332p-7hrc-92wf > <https://urldefense.com/v3/__https://github.com/python-pillow/Pillow/security/advisories/GHSA-332p-7hrc-92wf__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCE5CP-7g$> > > Signed-off-by: Kris Gavvala <kris.gavvala@windriver.com> > --- > .../python3-pillow/CVE-2026-112393.patch | 124 > ++++++++++++++++++ > .../python/python3-pillow_12.3.0.bb > <https://urldefense.com/v3/__http://python3-pillow_12.3.0.bb__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCm5l_KMw$> > | 1 + > 2 files changed, 125 insertions(+) > create mode 100644 > meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > > diff --git > a/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > b/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > new file mode 100644 > index 0000000000..cb9be3b33e > --- /dev/null > +++ > b/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch > @@ -0,0 +1,124 @@ > +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 > <mailto:223556219%2BCopilot@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 > <https://urldefense.com/v3/__https://github.com/python-pillow/Pillow/commit/78c08bd4d9c08037f77465a61d90633f669013f4__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCIaCNLIw$>] > + > +Signed-off-by: Kris Gavvala <kris.gavvala@windriver.com> > +--- > + Tests/test_file_libtiff.py > <https://urldefense.com/v3/__http://test_file_libtiff.py__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjB4c4M0WA$> > | 36 ++++++++++++++++++++++++++++++++++++ > + src/libImaging/TiffDecode.c | 15 ++++++++++++++- > + 2 files changed, 50 insertions(+), 1 deletion(-) > + > +diff --git a/Tests/test_file_libtiff.py > <https://urldefense.com/v3/__http://test_file_libtiff.py__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjB4c4M0WA$> > b/Tests/test_file_libtiff.py > <https://urldefense.com/v3/__http://test_file_libtiff.py__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjB4c4M0WA$> > +index c9849a056..84e4299a1 100644 > +--- a/Tests/test_file_libtiff.py > <https://urldefense.com/v3/__http://test_file_libtiff.py__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjB4c4M0WA$> > ++++ b/Tests/test_file_libtiff.py > <https://urldefense.com/v3/__http://test_file_libtiff.py__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjB4c4M0WA$> > +@@ -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 > + > diff --git > a/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > <https://urldefense.com/v3/__http://python3-pillow_12.3.0.bb__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCm5l_KMw$> > b/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > <https://urldefense.com/v3/__http://python3-pillow_12.3.0.bb__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCm5l_KMw$> > index 1c6668e53d..e4826da1a6 100644 > --- a/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > <https://urldefense.com/v3/__http://python3-pillow_12.3.0.bb__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCm5l_KMw$> > +++ b/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb > <https://urldefense.com/v3/__http://python3-pillow_12.3.0.bb__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjCm5l_KMw$> > @@ -7,6 +7,7 @@ LIC_FILES_CHKSUM = > "file://LICENSE;md5=a6f0ac3777cfc96ded1b825e32ae7c99" > > SRC_URI = > "git://github.com/python-pillow/Pillow.git;branch=main;protocol=https;tag=${PV} > <https://urldefense.com/v3/__http://github.com/python-pillow/Pillow.git;branch=main;protocol=https;tag=$*7BPV*7D__;JSU!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjACWFfxyA$> > \ > file://0001-support-cross-compiling.patch \ > + file://CVE-2026-112393.patch \ > " > SRCREV = "bb1d8e8ab8d29048624d96e3ee53cecf7c13d13d" > > -- > 2.43.0 > > > -=-=-=-=-=-=-=-=-=-=-=- > Links: You receive all messages sent to this group. > View/Reply Online (#131753): > https://lists.openembedded.org/g/openembedded-devel/message/131753 > <https://urldefense.com/v3/__https://lists.openembedded.org/g/openembedded-devel/message/131753__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjD0zTu62g$> > Mute This Topic: > https://lists.openembedded.org/mt/122066176/924729 > <https://urldefense.com/v3/__https://lists.openembedded.org/mt/122066176/924729__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjDmoU38zg$> > Group Owner: openembedded-devel+owner@lists.openembedded.org > <mailto:openembedded-devel%2Bowner@lists.openembedded.org> > Unsubscribe: > https://lists.openembedded.org/g/openembedded-devel/unsub > <https://urldefense.com/v3/__https://lists.openembedded.org/g/openembedded-devel/unsub__;!!AjveYdw8EvQ!Yojr6TbFBodsGm8doMeEs_UcyjH7e95BjPHg8QK8OTo5A3Dy0OcRIOfKF7EtrhKYBBBDShpjtjSrBjC2b-x1sg$> > [ticotimo@gmail.com] > -=-=-=-=-=-=-=-=-=-=-=- >
diff --git a/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch b/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch new file mode 100644 index 0000000000..cb9be3b33e --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch @@ -0,0 +1,124 @@ +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 + diff --git a/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb b/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb index 1c6668e53d..e4826da1a6 100644 --- a/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb +++ b/meta-python/recipes-devtools/python/python3-pillow_12.3.0.bb @@ -7,6 +7,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=a6f0ac3777cfc96ded1b825e32ae7c99" SRC_URI = "git://github.com/python-pillow/Pillow.git;branch=main;protocol=https;tag=${PV} \ file://0001-support-cross-compiling.patch \ + file://CVE-2026-112393.patch \ " SRCREV = "bb1d8e8ab8d29048624d96e3ee53cecf7c13d13d"
This patch applies the upstream fix and related tests in commit [1] to address the issue in [2]. [1] https://github.com/python-pillow/Pillow/commit/78c08bd4d9c08037f77465a61d90633f669013f4 [2] https://github.com/python-pillow/Pillow/security/advisories/GHSA-332p-7hrc-92wf Signed-off-by: Kris Gavvala <kris.gavvala@windriver.com> --- .../python3-pillow/CVE-2026-112393.patch | 124 ++++++++++++++++++ .../python/python3-pillow_12.3.0.bb | 1 + 2 files changed, 125 insertions(+) create mode 100644 meta-python/recipes-devtools/python/python3-pillow/CVE-2026-112393.patch