From fa35eab33fbb9876369a15bacc44e671466e9631 Mon Sep 17 00:00:00 2001 From: digit50 <93092234+digit50@users.noreply.github.com> Date: Sat, 17 Oct 2026 22:33:13 +0300 Subject: [PATCH] gh-159643: Fix EOFError in tarfile stream mode with bz2, xz and zstd In stream mode the bz2, xz and zstd decompressors were fed again after reaching the end of their data, which raised a bare EOFError. Stop reading once the decompressor reports eof, as the code did before it was rewritten. Add stream-mode tests for concatenated archives with ignore_zeros and for an archive without end-of-archive blocks. --- Lib/tarfile.py | 2 ++ Lib/test/test_tarfile.py | 20 +++++++++++++++++++ ...17-19-33-13.gh-issue-159643.EJfe0IXetV.rst | 3 +++ 3 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-10-17-19-33-13.gh-issue-159643.EJfe0IXetV.rst diff --git a/Lib/tarfile.py b/Lib/tarfile.py index c0b9f3f429c10e..16d3c60cd1b488 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -593,6 +593,8 @@ def _read(self, size): else: # Other decompressors have needs_input. # decompress() can buffer data internally. + if self.cmp.eof: + break if self.cmp.needs_input: cbuf = self.fileobj.read(self.bufsize) if not cbuf: diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 2b4cf748dffc1a..479772b5c8a89a 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1060,6 +1060,26 @@ def test_compare_members(self): finally: tar1.close() + def test_ignore_zeros_concatenated(self): + # gh-159643: reading past the end of the compressed data + # must not raise EOFError. + with self.open(tmpname, "wb") as fobj: + for name in ("a", "b"): + with tarfile.open(fileobj=fobj, mode="w") as tar: + tar.addfile(tarfile.TarInfo(name)) + with tarfile.open(tmpname, self.mode, ignore_zeros=True) as tar: + self.assertEqual(tar.getnames(), ["a", "b"]) + + def test_no_end_of_archive_blocks(self): + # gh-159643 + tarinfo = tarfile.TarInfo("a") + tarinfo.size = 5 + with self.open(tmpname, "wb") as fobj: + fobj.write(tarinfo.tobuf()) + fobj.write(b"hello".ljust(tarfile.BLOCKSIZE, b"\0")) + with tarfile.open(tmpname, self.mode) as tar: + self.assertEqual(tar.getnames(), ["a"]) + class GzipStreamReadTest(GzipTest, GzipReadTestBase, StreamReadTest): pass diff --git a/Misc/NEWS.d/next/Library/2026-10-17-19-33-13.gh-issue-159643.EJfe0IXetV.rst b/Misc/NEWS.d/next/Library/2026-10-17-19-33-13.gh-issue-159643.EJfe0IXetV.rst new file mode 100644 index 00000000000000..a76e54de38cc4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-17-19-33-13.gh-issue-159643.EJfe0IXetV.rst @@ -0,0 +1,3 @@ +Fix :exc:`EOFError` raised by :mod:`tarfile` in stream mode with bz2, xz or +zstd compression when reading past the end of the compressed data, for +example with *ignore_zeros* or an archive without end-of-archive blocks.