Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python314 for openSUSE:Factory checked in at 2025-08-03 13:37:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python314 (Old) and /work/SRC/openSUSE:Factory/.python314.new.1085 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python314" Sun Aug 3 13:37:35 2025 rev:20 rq:1297122 version:3.14.0~rc1 Changes: -------- --- /work/SRC/openSUSE:Factory/python314/python314.changes 2025-07-23 16:39:23.306062782 +0200 +++ /work/SRC/openSUSE:Factory/.python314.new.1085/python314.changes 2025-08-03 13:38:02.309013464 +0200 @@ -1,0 +2,7 @@ +Fri Aug 1 20:09:24 UTC 2025 - Matej Cepl <mc...@cepl.eu> + +- Add CVE-2025-8194-tarfile-no-neg-offsets.patch which now + validates archives to ensure member offsets are non-negative + (gh#python/cpython#130577, CVE-2025-8194, bsc#1247249). + +------------------------------------------------------------------- New: ---- CVE-2025-8194-tarfile-no-neg-offsets.patch ----------(New B)---------- New: - Add CVE-2025-8194-tarfile-no-neg-offsets.patch which now validates archives to ensure member offsets are non-negative ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python314.spec ++++++ --- /var/tmp/diff_new_pack.pdvZBw/_old 2025-08-03 13:38:03.265053119 +0200 +++ /var/tmp/diff_new_pack.pdvZBw/_new 2025-08-03 13:38:03.265053119 +0200 @@ -222,6 +222,9 @@ # PATCH-FIX-UPSTREAM bsc1243155-sphinx-non-determinism.patch bsc#1243155 mc...@suse.com # Doc: Generate ids for audit_events using docname Patch41: bsc1243155-sphinx-non-determinism.patch +# PATCH-FIX-UPSTREAM CVE-2025-8194-tarfile-no-neg-offsets.patch bsc#1247249 mc...@suse.com +# tarfile now validates archives to ensure member offsets are non-negative +Patch42: CVE-2025-8194-tarfile-no-neg-offsets.patch #### Python 3.14 DEVELOPMENT PATCHES BuildRequires: autoconf-archive BuildRequires: automake ++++++ CVE-2025-8194-tarfile-no-neg-offsets.patch ++++++ >From 28d130238bfb5604eef4b594d597f7b5ec951eba Mon Sep 17 00:00:00 2001 From: Alexander Urieles <aeuriel...@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:37:26 +0200 Subject: [PATCH] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) (cherry picked from commit 7040aa54f14676938970e10c5f74ea93cd56aa38) Co-authored-by: Alexander Urieles <aeuriel...@users.noreply.github.com> Co-authored-by: Gregory P. Smith <g...@krypto.org> --- Lib/tarfile.py | 3 Lib/test/test_tarfile.py | 156 ++++++++++ Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3 3 files changed, 162 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst Index: Python-3.14.0rc1/Lib/tarfile.py =================================================================== --- Python-3.14.0rc1.orig/Lib/tarfile.py 2025-08-01 22:10:15.833118580 +0200 +++ Python-3.14.0rc1/Lib/tarfile.py 2025-08-01 22:10:21.970557323 +0200 @@ -1647,6 +1647,9 @@ """Round up a byte count by BLOCKSIZE and return it, e.g. _block(834) => 1024. """ + # Only non-negative offsets are allowed + if count < 0: + raise InvalidHeaderError("invalid offset") blocks, remainder = divmod(count, BLOCKSIZE) if remainder: blocks += 1 Index: Python-3.14.0rc1/Lib/test/test_tarfile.py =================================================================== --- Python-3.14.0rc1.orig/Lib/test/test_tarfile.py 2025-08-01 22:10:17.621793551 +0200 +++ Python-3.14.0rc1/Lib/test/test_tarfile.py 2025-08-01 22:10:21.971238980 +0200 @@ -55,6 +55,7 @@ zstname = os.path.join(TEMPDIR, "testtar.tar.zst") tmpname = os.path.join(TEMPDIR, "tmp.tar") dotlessname = os.path.join(TEMPDIR, "testtar") +SPACE = b" " sha256_regtype = ( "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce" @@ -4602,6 +4603,161 @@ ar.extractall(self.testdir, filter='fully_trusted') +class OffsetValidationTests(unittest.TestCase): + tarname = tmpname + invalid_posix_header = ( + # name: 100 bytes + tarfile.NUL * tarfile.LENGTH_NAME + # mode, space, null terminator: 8 bytes + + b"000755" + SPACE + tarfile.NUL + # uid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # gid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # size, space: 12 bytes + + b"\xff" * 11 + SPACE + # mtime, space: 12 bytes + + tarfile.NUL * 11 + SPACE + # chksum: 8 bytes + + b"0011407" + tarfile.NUL + # type: 1 byte + + tarfile.REGTYPE + # linkname: 100 bytes + + tarfile.NUL * tarfile.LENGTH_LINK + # magic: 6 bytes, version: 2 bytes + + tarfile.POSIX_MAGIC + # uname: 32 bytes + + tarfile.NUL * 32 + # gname: 32 bytes + + tarfile.NUL * 32 + # devmajor, space, null terminator: 8 bytes + + tarfile.NUL * 6 + SPACE + tarfile.NUL + # devminor, space, null terminator: 8 bytes + + tarfile.NUL * 6 + SPACE + tarfile.NUL + # prefix: 155 bytes + + tarfile.NUL * tarfile.LENGTH_PREFIX + # padding: 12 bytes + + tarfile.NUL * 12 + ) + invalid_gnu_header = ( + # name: 100 bytes + tarfile.NUL * tarfile.LENGTH_NAME + # mode, null terminator: 8 bytes + + b"0000755" + tarfile.NUL + # uid, null terminator: 8 bytes + + b"0000001" + tarfile.NUL + # gid, space, null terminator: 8 bytes + + b"0000001" + tarfile.NUL + # size, space: 12 bytes + + b"\xff" * 11 + SPACE + # mtime, space: 12 bytes + + tarfile.NUL * 11 + SPACE + # chksum: 8 bytes + + b"0011327" + tarfile.NUL + # type: 1 byte + + tarfile.REGTYPE + # linkname: 100 bytes + + tarfile.NUL * tarfile.LENGTH_LINK + # magic: 8 bytes + + tarfile.GNU_MAGIC + # uname: 32 bytes + + tarfile.NUL * 32 + # gname: 32 bytes + + tarfile.NUL * 32 + # devmajor, null terminator: 8 bytes + + tarfile.NUL * 8 + # devminor, null terminator: 8 bytes + + tarfile.NUL * 8 + # padding: 167 bytes + + tarfile.NUL * 167 + ) + invalid_v7_header = ( + # name: 100 bytes + tarfile.NUL * tarfile.LENGTH_NAME + # mode, space, null terminator: 8 bytes + + b"000755" + SPACE + tarfile.NUL + # uid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # gid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # size, space: 12 bytes + + b"\xff" * 11 + SPACE + # mtime, space: 12 bytes + + tarfile.NUL * 11 + SPACE + # chksum: 8 bytes + + b"0010070" + tarfile.NUL + # type: 1 byte + + tarfile.REGTYPE + # linkname: 100 bytes + + tarfile.NUL * tarfile.LENGTH_LINK + # padding: 255 bytes + + tarfile.NUL * 255 + ) + valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT) + data_block = b"\xff" * tarfile.BLOCKSIZE + + def _write_buffer(self, buffer): + with open(self.tarname, "wb") as f: + f.write(buffer) + + def _get_members(self, ignore_zeros=None): + with open(self.tarname, "rb") as f: + with tarfile.open( + mode="r", fileobj=f, ignore_zeros=ignore_zeros + ) as tar: + return tar.getmembers() + + def _assert_raises_read_error_exception(self): + with self.assertRaisesRegex( + tarfile.ReadError, "file could not be opened successfully" + ): + self._get_members() + + def test_invalid_offset_header_validations(self): + for tar_format, invalid_header in ( + ("posix", self.invalid_posix_header), + ("gnu", self.invalid_gnu_header), + ("v7", self.invalid_v7_header), + ): + with self.subTest(format=tar_format): + self._write_buffer(invalid_header) + self._assert_raises_read_error_exception() + + def test_early_stop_at_invalid_offset_header(self): + buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header + self._write_buffer(buffer) + members = self._get_members() + self.assertEqual(len(members), 1) + self.assertEqual(members[0].name, "filename") + self.assertEqual(members[0].offset, 0) + + def test_ignore_invalid_archive(self): + # 3 invalid headers with their respective data + buffer = (self.invalid_gnu_header + self.data_block) * 3 + self._write_buffer(buffer) + members = self._get_members(ignore_zeros=True) + self.assertEqual(len(members), 0) + + def test_ignore_invalid_offset_headers(self): + for first_block, second_block, expected_offset in ( + ( + (self.valid_gnu_header), + (self.invalid_gnu_header + self.data_block), + 0, + ), + ( + (self.invalid_gnu_header + self.data_block), + (self.valid_gnu_header), + 1024, + ), + ): + self._write_buffer(first_block + second_block) + members = self._get_members(ignore_zeros=True) + self.assertEqual(len(members), 1) + self.assertEqual(members[0].name, "filename") + self.assertEqual(members[0].offset, expected_offset) + + def setUpModule(): os_helper.unlink(TEMPDIR) os.makedirs(TEMPDIR) Index: Python-3.14.0rc1/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ Python-3.14.0rc1/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst 2025-08-01 22:10:21.971763003 +0200 @@ -0,0 +1,3 @@ +:mod:`tarfile` now validates archives to ensure member offsets are +non-negative. (Contributed by Alexander Enrique Urieles Nieto in +:gh:`130577`.)