Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions SPECS/python3/CVE-2025-8194.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
From 72b6fd266e42bfa6196317bcf049666c5bad74a3 Mon Sep 17 00:00:00 2001
From: Alexander Urieles <[email protected]>
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)

Co-authored-by: Gregory P. Smith <[email protected]>
(cherry picked from commit 7040aa54f14676938970e10c5f74ea93cd56aa38)

Upstream Patch Reference: https://github.com/python/cpython/pull/137645
Signed-off-by: Kanishk Bansal <[email protected]>

---
Lib/tarfile.py | 3 +
Lib/test/test_tarfile.py | 156 ++++++++++++++++++
...-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

diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 7c9027dde849fd..209c206231dc97 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -1602,6 +1602,9 @@ def _block(self, count):
"""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
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 01d1e5e0ccf410..17d2239339ec1a 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -48,6 +48,7 @@ def sha256sum(data):
xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
tmpname = os.path.join(TEMPDIR, "tmp.tar")
dotlessname = os.path.join(TEMPDIR, "testtar")
+SPACE = b" "

sha256_regtype = (
"e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
@@ -4234,6 +4235,161 @@ def valueerror_filter(tarinfo, path):
self.expect_exception(TypeError) # errorlevel is not int


+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():
support.unlink(TEMPDIR)
os.makedirs(TEMPDIR)
diff --git a/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst b/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
new file mode 100644
index 00000000000000..342cabbc865dc4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
@@ -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`.)
10 changes: 8 additions & 2 deletions SPECS/python3/python3.spec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
Summary: A high-level scripting language
Name: python3
Version: 3.9.19
Release: 14%{?dist}
Release: 15%{?dist}
License: PSF
Vendor: Microsoft Corporation
Distribution: Mariner
Expand All @@ -36,6 +36,7 @@ Patch12: CVE-2025-1795.patch
Patch13: CVE-2025-6069.patch
Patch14: CVE-2025-4516.patch
Patch15: CVE-2025-4138.patch
Patch16: CVE-2025-8194.patch
# Patch for setuptools, resolved in 65.5.1
Patch1000: CVE-2022-40897.patch
Patch1001: CVE-2024-6345.patch
Expand Down Expand Up @@ -70,7 +71,8 @@ Provides: /bin/python3
Provides: %{name}-docs = %{version}-%{release}
Provides: python%{majmin} = %{version}-%{release}
Provides: python%{majmin_nodots} = %{version}-%{release}
%if %{with_check}

%if 0%{?with_check}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rpmspec was not able to parse the spec
image

  • Original: %if %{with_check}

    • This condition is true if the macro with_check is defined and non-zero. If with_check is not defined, this can produce an error like it is creating right now when we are doing rpmspec parse.
  • Changed to: %if 0%{?with_check}

    • 0%{?with_check} safely expands to 0 if with_check is not defined (so, false), or to the value of with_check if it is defined (still works as before if set).
    • The ? in %{?with_check} means "use the value if defined, otherwise nothing."
    • Prefixing with 0 ensures the macro always expands to a valid number (0/1), avoiding errors if undefined.

BuildRequires: iana-etc
BuildRequires: tzdata
%endif
Expand Down Expand Up @@ -196,6 +198,7 @@ The test package contains all regression tests for Python as well as the modules
%patch13 -p1
%patch14 -p1
%patch15 -p1
%patch16 -p1

%build
# Remove GCC specs and build environment linker scripts
Expand Down Expand Up @@ -371,6 +374,9 @@ make test TESTOPTS="-x test_multiprocessing_spawn -x test_socket -x test_email"
%{_libdir}/python%{majmin}/test/*

%changelog
* Thu Sep 18 2025 Kanishk Bansal <[email protected]> - 3.9.19-15
- Patch CVE-2025-8194

* Mon Jun 30 2025 Aninda Pradhan <[email protected]> - 3.9.19-14
- Addresses CVE-2025-6069, CVE-2025-4516, CVE-2025-50181, CVE-2023-5752, CVE-2023-45803
- Patch for CVE-2025-4138: Addresses CVE-2024-12718, CVE-2025-4138, CVE-2025-4330, CVE-2025-4517, CVE-2025-4435
Expand Down
8 changes: 4 additions & 4 deletions toolkit/resources/manifests/package/pkggen_core_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,10 @@ ca-certificates-base-2.0.0-22.cm2.noarch.rpm
ca-certificates-2.0.0-22.cm2.noarch.rpm
dwz-0.14-2.cm2.aarch64.rpm
unzip-6.0-22.cm2.aarch64.rpm
python3-3.9.19-14.cm2.aarch64.rpm
python3-devel-3.9.19-14.cm2.aarch64.rpm
python3-libs-3.9.19-14.cm2.aarch64.rpm
python3-setuptools-3.9.19-14.cm2.noarch.rpm
python3-3.9.19-15.cm2.aarch64.rpm
python3-devel-3.9.19-15.cm2.aarch64.rpm
python3-libs-3.9.19-15.cm2.aarch64.rpm
python3-setuptools-3.9.19-15.cm2.noarch.rpm
python3-pygments-2.4.2-7.cm2.noarch.rpm
which-2.21-8.cm2.aarch64.rpm
libselinux-3.2-1.cm2.aarch64.rpm
Expand Down
8 changes: 4 additions & 4 deletions toolkit/resources/manifests/package/pkggen_core_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,10 @@ ca-certificates-base-2.0.0-22.cm2.noarch.rpm
ca-certificates-2.0.0-22.cm2.noarch.rpm
dwz-0.14-2.cm2.x86_64.rpm
unzip-6.0-22.cm2.x86_64.rpm
python3-3.9.19-14.cm2.x86_64.rpm
python3-devel-3.9.19-14.cm2.x86_64.rpm
python3-libs-3.9.19-14.cm2.x86_64.rpm
python3-setuptools-3.9.19-14.cm2.noarch.rpm
python3-3.9.19-15.cm2.x86_64.rpm
python3-devel-3.9.19-15.cm2.x86_64.rpm
python3-libs-3.9.19-15.cm2.x86_64.rpm
python3-setuptools-3.9.19-15.cm2.noarch.rpm
python3-pygments-2.4.2-7.cm2.noarch.rpm
which-2.21-8.cm2.x86_64.rpm
libselinux-3.2-1.cm2.x86_64.rpm
Expand Down
18 changes: 9 additions & 9 deletions toolkit/resources/manifests/package/toolchain_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -510,28 +510,28 @@ procps-ng-devel-3.3.17-2.cm2.aarch64.rpm
procps-ng-lang-3.3.17-2.cm2.aarch64.rpm
pyproject-rpm-macros-1.0.0~rc1-4.cm2.noarch.rpm
python-markupsafe-debuginfo-2.1.0-1.cm2.aarch64.rpm
python3-3.9.19-14.cm2.aarch64.rpm
python3-3.9.19-15.cm2.aarch64.rpm
python3-audit-3.0.6-8.cm2.aarch64.rpm
python3-cracklib-2.9.7-5.cm2.aarch64.rpm
python3-curses-3.9.19-14.cm2.aarch64.rpm
python3-curses-3.9.19-15.cm2.aarch64.rpm
python3-Cython-0.29.33-2.cm2.aarch64.rpm
python3-debuginfo-3.9.19-14.cm2.aarch64.rpm
python3-devel-3.9.19-14.cm2.aarch64.rpm
python3-debuginfo-3.9.19-15.cm2.aarch64.rpm
python3-devel-3.9.19-15.cm2.aarch64.rpm
python3-gpg-1.16.0-2.cm2.aarch64.rpm
python3-jinja2-3.0.3-7.cm2.noarch.rpm
python3-libcap-ng-0.8.2-2.cm2.aarch64.rpm
python3-libs-3.9.19-14.cm2.aarch64.rpm
python3-libs-3.9.19-15.cm2.aarch64.rpm
python3-libxml2-2.10.4-8.cm2.aarch64.rpm
python3-lxml-4.9.1-1.cm2.aarch64.rpm
python3-magic-5.40-3.cm2.noarch.rpm
python3-markupsafe-2.1.0-1.cm2.aarch64.rpm
python3-newt-0.52.21-5.cm2.aarch64.rpm
python3-pip-3.9.19-14.cm2.noarch.rpm
python3-pip-3.9.19-15.cm2.noarch.rpm
python3-pygments-2.4.2-7.cm2.noarch.rpm
python3-rpm-4.18.0-4.cm2.aarch64.rpm
python3-setuptools-3.9.19-14.cm2.noarch.rpm
python3-test-3.9.19-14.cm2.aarch64.rpm
python3-tools-3.9.19-14.cm2.aarch64.rpm
python3-setuptools-3.9.19-15.cm2.noarch.rpm
python3-test-3.9.19-15.cm2.aarch64.rpm
python3-tools-3.9.19-15.cm2.aarch64.rpm
readline-8.1-1.cm2.aarch64.rpm
readline-debuginfo-8.1-1.cm2.aarch64.rpm
readline-devel-8.1-1.cm2.aarch64.rpm
Expand Down
18 changes: 9 additions & 9 deletions toolkit/resources/manifests/package/toolchain_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -516,28 +516,28 @@ procps-ng-devel-3.3.17-2.cm2.x86_64.rpm
procps-ng-lang-3.3.17-2.cm2.x86_64.rpm
pyproject-rpm-macros-1.0.0~rc1-4.cm2.noarch.rpm
python-markupsafe-debuginfo-2.1.0-1.cm2.x86_64.rpm
python3-3.9.19-14.cm2.x86_64.rpm
python3-3.9.19-15.cm2.x86_64.rpm
python3-audit-3.0.6-8.cm2.x86_64.rpm
python3-cracklib-2.9.7-5.cm2.x86_64.rpm
python3-curses-3.9.19-14.cm2.x86_64.rpm
python3-curses-3.9.19-15.cm2.x86_64.rpm
python3-Cython-0.29.33-2.cm2.x86_64.rpm
python3-debuginfo-3.9.19-14.cm2.x86_64.rpm
python3-devel-3.9.19-14.cm2.x86_64.rpm
python3-debuginfo-3.9.19-15.cm2.x86_64.rpm
python3-devel-3.9.19-15.cm2.x86_64.rpm
python3-gpg-1.16.0-2.cm2.x86_64.rpm
python3-jinja2-3.0.3-7.cm2.noarch.rpm
python3-libcap-ng-0.8.2-2.cm2.x86_64.rpm
python3-libs-3.9.19-14.cm2.x86_64.rpm
python3-libs-3.9.19-15.cm2.x86_64.rpm
python3-libxml2-2.10.4-8.cm2.x86_64.rpm
python3-lxml-4.9.1-1.cm2.x86_64.rpm
python3-magic-5.40-3.cm2.noarch.rpm
python3-markupsafe-2.1.0-1.cm2.x86_64.rpm
python3-newt-0.52.21-5.cm2.x86_64.rpm
python3-pip-3.9.19-14.cm2.noarch.rpm
python3-pip-3.9.19-15.cm2.noarch.rpm
python3-pygments-2.4.2-7.cm2.noarch.rpm
python3-rpm-4.18.0-4.cm2.x86_64.rpm
python3-setuptools-3.9.19-14.cm2.noarch.rpm
python3-test-3.9.19-14.cm2.x86_64.rpm
python3-tools-3.9.19-14.cm2.x86_64.rpm
python3-setuptools-3.9.19-15.cm2.noarch.rpm
python3-test-3.9.19-15.cm2.x86_64.rpm
python3-tools-3.9.19-15.cm2.x86_64.rpm
readline-8.1-1.cm2.x86_64.rpm
readline-debuginfo-8.1-1.cm2.x86_64.rpm
readline-devel-8.1-1.cm2.x86_64.rpm
Expand Down
Loading