Skip to content

Commit b174f1e

Browse files
Kanishk-BansalKanishk Bansal
andauthored
Patch python3 for CVE-2025-8194 [High] (microsoft#14691)
Signed-off-by: Kanishk Bansal <[email protected]> Co-authored-by: Kanishk Bansal <[email protected]>
1 parent 0249490 commit b174f1e

File tree

6 files changed

+250
-28
lines changed

6 files changed

+250
-28
lines changed

SPECS/python3/CVE-2025-8194.patch

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
From 72b6fd266e42bfa6196317bcf049666c5bad74a3 Mon Sep 17 00:00:00 2001
2+
From: Alexander Urieles <[email protected]>
3+
Date: Mon, 28 Jul 2025 17:37:26 +0200
4+
Subject: [PATCH] gh-130577: tarfile now validates archives to ensure member
5+
offsets are non-negative (GH-137027)
6+
7+
Co-authored-by: Gregory P. Smith <[email protected]>
8+
(cherry picked from commit 7040aa54f14676938970e10c5f74ea93cd56aa38)
9+
10+
Upstream Patch Reference: https://github.com/python/cpython/pull/137645
11+
Signed-off-by: Kanishk Bansal <[email protected]>
12+
13+
---
14+
Lib/tarfile.py | 3 +
15+
Lib/test/test_tarfile.py | 156 ++++++++++++++++++
16+
...-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3 +
17+
3 files changed, 162 insertions(+)
18+
create mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
19+
20+
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
21+
index 7c9027dde849fd..209c206231dc97 100755
22+
--- a/Lib/tarfile.py
23+
+++ b/Lib/tarfile.py
24+
@@ -1602,6 +1602,9 @@ def _block(self, count):
25+
"""Round up a byte count by BLOCKSIZE and return it,
26+
e.g. _block(834) => 1024.
27+
"""
28+
+ # Only non-negative offsets are allowed
29+
+ if count < 0:
30+
+ raise InvalidHeaderError("invalid offset")
31+
blocks, remainder = divmod(count, BLOCKSIZE)
32+
if remainder:
33+
blocks += 1
34+
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
35+
index 01d1e5e0ccf410..17d2239339ec1a 100644
36+
--- a/Lib/test/test_tarfile.py
37+
+++ b/Lib/test/test_tarfile.py
38+
@@ -48,6 +48,7 @@ def sha256sum(data):
39+
xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
40+
tmpname = os.path.join(TEMPDIR, "tmp.tar")
41+
dotlessname = os.path.join(TEMPDIR, "testtar")
42+
+SPACE = b" "
43+
44+
sha256_regtype = (
45+
"e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
46+
@@ -4234,6 +4235,161 @@ def valueerror_filter(tarinfo, path):
47+
self.expect_exception(TypeError) # errorlevel is not int
48+
49+
50+
+class OffsetValidationTests(unittest.TestCase):
51+
+ tarname = tmpname
52+
+ invalid_posix_header = (
53+
+ # name: 100 bytes
54+
+ tarfile.NUL * tarfile.LENGTH_NAME
55+
+ # mode, space, null terminator: 8 bytes
56+
+ + b"000755" + SPACE + tarfile.NUL
57+
+ # uid, space, null terminator: 8 bytes
58+
+ + b"000001" + SPACE + tarfile.NUL
59+
+ # gid, space, null terminator: 8 bytes
60+
+ + b"000001" + SPACE + tarfile.NUL
61+
+ # size, space: 12 bytes
62+
+ + b"\xff" * 11 + SPACE
63+
+ # mtime, space: 12 bytes
64+
+ + tarfile.NUL * 11 + SPACE
65+
+ # chksum: 8 bytes
66+
+ + b"0011407" + tarfile.NUL
67+
+ # type: 1 byte
68+
+ + tarfile.REGTYPE
69+
+ # linkname: 100 bytes
70+
+ + tarfile.NUL * tarfile.LENGTH_LINK
71+
+ # magic: 6 bytes, version: 2 bytes
72+
+ + tarfile.POSIX_MAGIC
73+
+ # uname: 32 bytes
74+
+ + tarfile.NUL * 32
75+
+ # gname: 32 bytes
76+
+ + tarfile.NUL * 32
77+
+ # devmajor, space, null terminator: 8 bytes
78+
+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
79+
+ # devminor, space, null terminator: 8 bytes
80+
+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
81+
+ # prefix: 155 bytes
82+
+ + tarfile.NUL * tarfile.LENGTH_PREFIX
83+
+ # padding: 12 bytes
84+
+ + tarfile.NUL * 12
85+
+ )
86+
+ invalid_gnu_header = (
87+
+ # name: 100 bytes
88+
+ tarfile.NUL * tarfile.LENGTH_NAME
89+
+ # mode, null terminator: 8 bytes
90+
+ + b"0000755" + tarfile.NUL
91+
+ # uid, null terminator: 8 bytes
92+
+ + b"0000001" + tarfile.NUL
93+
+ # gid, space, null terminator: 8 bytes
94+
+ + b"0000001" + tarfile.NUL
95+
+ # size, space: 12 bytes
96+
+ + b"\xff" * 11 + SPACE
97+
+ # mtime, space: 12 bytes
98+
+ + tarfile.NUL * 11 + SPACE
99+
+ # chksum: 8 bytes
100+
+ + b"0011327" + tarfile.NUL
101+
+ # type: 1 byte
102+
+ + tarfile.REGTYPE
103+
+ # linkname: 100 bytes
104+
+ + tarfile.NUL * tarfile.LENGTH_LINK
105+
+ # magic: 8 bytes
106+
+ + tarfile.GNU_MAGIC
107+
+ # uname: 32 bytes
108+
+ + tarfile.NUL * 32
109+
+ # gname: 32 bytes
110+
+ + tarfile.NUL * 32
111+
+ # devmajor, null terminator: 8 bytes
112+
+ + tarfile.NUL * 8
113+
+ # devminor, null terminator: 8 bytes
114+
+ + tarfile.NUL * 8
115+
+ # padding: 167 bytes
116+
+ + tarfile.NUL * 167
117+
+ )
118+
+ invalid_v7_header = (
119+
+ # name: 100 bytes
120+
+ tarfile.NUL * tarfile.LENGTH_NAME
121+
+ # mode, space, null terminator: 8 bytes
122+
+ + b"000755" + SPACE + tarfile.NUL
123+
+ # uid, space, null terminator: 8 bytes
124+
+ + b"000001" + SPACE + tarfile.NUL
125+
+ # gid, space, null terminator: 8 bytes
126+
+ + b"000001" + SPACE + tarfile.NUL
127+
+ # size, space: 12 bytes
128+
+ + b"\xff" * 11 + SPACE
129+
+ # mtime, space: 12 bytes
130+
+ + tarfile.NUL * 11 + SPACE
131+
+ # chksum: 8 bytes
132+
+ + b"0010070" + tarfile.NUL
133+
+ # type: 1 byte
134+
+ + tarfile.REGTYPE
135+
+ # linkname: 100 bytes
136+
+ + tarfile.NUL * tarfile.LENGTH_LINK
137+
+ # padding: 255 bytes
138+
+ + tarfile.NUL * 255
139+
+ )
140+
+ valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT)
141+
+ data_block = b"\xff" * tarfile.BLOCKSIZE
142+
+
143+
+ def _write_buffer(self, buffer):
144+
+ with open(self.tarname, "wb") as f:
145+
+ f.write(buffer)
146+
+
147+
+ def _get_members(self, ignore_zeros=None):
148+
+ with open(self.tarname, "rb") as f:
149+
+ with tarfile.open(
150+
+ mode="r", fileobj=f, ignore_zeros=ignore_zeros
151+
+ ) as tar:
152+
+ return tar.getmembers()
153+
+
154+
+ def _assert_raises_read_error_exception(self):
155+
+ with self.assertRaisesRegex(
156+
+ tarfile.ReadError, "file could not be opened successfully"
157+
+ ):
158+
+ self._get_members()
159+
+
160+
+ def test_invalid_offset_header_validations(self):
161+
+ for tar_format, invalid_header in (
162+
+ ("posix", self.invalid_posix_header),
163+
+ ("gnu", self.invalid_gnu_header),
164+
+ ("v7", self.invalid_v7_header),
165+
+ ):
166+
+ with self.subTest(format=tar_format):
167+
+ self._write_buffer(invalid_header)
168+
+ self._assert_raises_read_error_exception()
169+
+
170+
+ def test_early_stop_at_invalid_offset_header(self):
171+
+ buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header
172+
+ self._write_buffer(buffer)
173+
+ members = self._get_members()
174+
+ self.assertEqual(len(members), 1)
175+
+ self.assertEqual(members[0].name, "filename")
176+
+ self.assertEqual(members[0].offset, 0)
177+
+
178+
+ def test_ignore_invalid_archive(self):
179+
+ # 3 invalid headers with their respective data
180+
+ buffer = (self.invalid_gnu_header + self.data_block) * 3
181+
+ self._write_buffer(buffer)
182+
+ members = self._get_members(ignore_zeros=True)
183+
+ self.assertEqual(len(members), 0)
184+
+
185+
+ def test_ignore_invalid_offset_headers(self):
186+
+ for first_block, second_block, expected_offset in (
187+
+ (
188+
+ (self.valid_gnu_header),
189+
+ (self.invalid_gnu_header + self.data_block),
190+
+ 0,
191+
+ ),
192+
+ (
193+
+ (self.invalid_gnu_header + self.data_block),
194+
+ (self.valid_gnu_header),
195+
+ 1024,
196+
+ ),
197+
+ ):
198+
+ self._write_buffer(first_block + second_block)
199+
+ members = self._get_members(ignore_zeros=True)
200+
+ self.assertEqual(len(members), 1)
201+
+ self.assertEqual(members[0].name, "filename")
202+
+ self.assertEqual(members[0].offset, expected_offset)
203+
+
204+
+
205+
def setUpModule():
206+
support.unlink(TEMPDIR)
207+
os.makedirs(TEMPDIR)
208+
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
209+
new file mode 100644
210+
index 00000000000000..342cabbc865dc4
211+
--- /dev/null
212+
+++ b/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
213+
@@ -0,0 +1,3 @@
214+
+:mod:`tarfile` now validates archives to ensure member offsets are
215+
+non-negative. (Contributed by Alexander Enrique Urieles Nieto in
216+
+:gh:`130577`.)

SPECS/python3/python3.spec

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
Summary: A high-level scripting language
1313
Name: python3
1414
Version: 3.9.19
15-
Release: 14%{?dist}
15+
Release: 15%{?dist}
1616
License: PSF
1717
Vendor: Microsoft Corporation
1818
Distribution: Mariner
@@ -36,6 +36,7 @@ Patch12: CVE-2025-1795.patch
3636
Patch13: CVE-2025-6069.patch
3737
Patch14: CVE-2025-4516.patch
3838
Patch15: CVE-2025-4138.patch
39+
Patch16: CVE-2025-8194.patch
3940
# Patch for setuptools, resolved in 65.5.1
4041
Patch1000: CVE-2022-40897.patch
4142
Patch1001: CVE-2024-6345.patch
@@ -70,7 +71,8 @@ Provides: /bin/python3
7071
Provides: %{name}-docs = %{version}-%{release}
7172
Provides: python%{majmin} = %{version}-%{release}
7273
Provides: python%{majmin_nodots} = %{version}-%{release}
73-
%if %{with_check}
74+
75+
%if 0%{?with_check}
7476
BuildRequires: iana-etc
7577
BuildRequires: tzdata
7678
%endif
@@ -196,6 +198,7 @@ The test package contains all regression tests for Python as well as the modules
196198
%patch13 -p1
197199
%patch14 -p1
198200
%patch15 -p1
201+
%patch16 -p1
199202

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

373376
%changelog
377+
* Thu Sep 18 2025 Kanishk Bansal <[email protected]> - 3.9.19-15
378+
- Patch CVE-2025-8194
379+
374380
* Mon Jun 30 2025 Aninda Pradhan <[email protected]> - 3.9.19-14
375381
- Addresses CVE-2025-6069, CVE-2025-4516, CVE-2025-50181, CVE-2023-5752, CVE-2023-45803
376382
- Patch for CVE-2025-4138: Addresses CVE-2024-12718, CVE-2025-4138, CVE-2025-4330, CVE-2025-4517, CVE-2025-4435

toolkit/resources/manifests/package/pkggen_core_aarch64.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ ca-certificates-base-2.0.0-22.cm2.noarch.rpm
237237
ca-certificates-2.0.0-22.cm2.noarch.rpm
238238
dwz-0.14-2.cm2.aarch64.rpm
239239
unzip-6.0-22.cm2.aarch64.rpm
240-
python3-3.9.19-14.cm2.aarch64.rpm
241-
python3-devel-3.9.19-14.cm2.aarch64.rpm
242-
python3-libs-3.9.19-14.cm2.aarch64.rpm
243-
python3-setuptools-3.9.19-14.cm2.noarch.rpm
240+
python3-3.9.19-15.cm2.aarch64.rpm
241+
python3-devel-3.9.19-15.cm2.aarch64.rpm
242+
python3-libs-3.9.19-15.cm2.aarch64.rpm
243+
python3-setuptools-3.9.19-15.cm2.noarch.rpm
244244
python3-pygments-2.4.2-7.cm2.noarch.rpm
245245
which-2.21-8.cm2.aarch64.rpm
246246
libselinux-3.2-1.cm2.aarch64.rpm

toolkit/resources/manifests/package/pkggen_core_x86_64.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ ca-certificates-base-2.0.0-22.cm2.noarch.rpm
237237
ca-certificates-2.0.0-22.cm2.noarch.rpm
238238
dwz-0.14-2.cm2.x86_64.rpm
239239
unzip-6.0-22.cm2.x86_64.rpm
240-
python3-3.9.19-14.cm2.x86_64.rpm
241-
python3-devel-3.9.19-14.cm2.x86_64.rpm
242-
python3-libs-3.9.19-14.cm2.x86_64.rpm
243-
python3-setuptools-3.9.19-14.cm2.noarch.rpm
240+
python3-3.9.19-15.cm2.x86_64.rpm
241+
python3-devel-3.9.19-15.cm2.x86_64.rpm
242+
python3-libs-3.9.19-15.cm2.x86_64.rpm
243+
python3-setuptools-3.9.19-15.cm2.noarch.rpm
244244
python3-pygments-2.4.2-7.cm2.noarch.rpm
245245
which-2.21-8.cm2.x86_64.rpm
246246
libselinux-3.2-1.cm2.x86_64.rpm

toolkit/resources/manifests/package/toolchain_aarch64.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -510,28 +510,28 @@ procps-ng-devel-3.3.17-2.cm2.aarch64.rpm
510510
procps-ng-lang-3.3.17-2.cm2.aarch64.rpm
511511
pyproject-rpm-macros-1.0.0~rc1-4.cm2.noarch.rpm
512512
python-markupsafe-debuginfo-2.1.0-1.cm2.aarch64.rpm
513-
python3-3.9.19-14.cm2.aarch64.rpm
513+
python3-3.9.19-15.cm2.aarch64.rpm
514514
python3-audit-3.0.6-8.cm2.aarch64.rpm
515515
python3-cracklib-2.9.7-5.cm2.aarch64.rpm
516-
python3-curses-3.9.19-14.cm2.aarch64.rpm
516+
python3-curses-3.9.19-15.cm2.aarch64.rpm
517517
python3-Cython-0.29.33-2.cm2.aarch64.rpm
518-
python3-debuginfo-3.9.19-14.cm2.aarch64.rpm
519-
python3-devel-3.9.19-14.cm2.aarch64.rpm
518+
python3-debuginfo-3.9.19-15.cm2.aarch64.rpm
519+
python3-devel-3.9.19-15.cm2.aarch64.rpm
520520
python3-gpg-1.16.0-2.cm2.aarch64.rpm
521521
python3-jinja2-3.0.3-7.cm2.noarch.rpm
522522
python3-libcap-ng-0.8.2-2.cm2.aarch64.rpm
523-
python3-libs-3.9.19-14.cm2.aarch64.rpm
523+
python3-libs-3.9.19-15.cm2.aarch64.rpm
524524
python3-libxml2-2.10.4-8.cm2.aarch64.rpm
525525
python3-lxml-4.9.1-1.cm2.aarch64.rpm
526526
python3-magic-5.40-3.cm2.noarch.rpm
527527
python3-markupsafe-2.1.0-1.cm2.aarch64.rpm
528528
python3-newt-0.52.21-5.cm2.aarch64.rpm
529-
python3-pip-3.9.19-14.cm2.noarch.rpm
529+
python3-pip-3.9.19-15.cm2.noarch.rpm
530530
python3-pygments-2.4.2-7.cm2.noarch.rpm
531531
python3-rpm-4.18.0-4.cm2.aarch64.rpm
532-
python3-setuptools-3.9.19-14.cm2.noarch.rpm
533-
python3-test-3.9.19-14.cm2.aarch64.rpm
534-
python3-tools-3.9.19-14.cm2.aarch64.rpm
532+
python3-setuptools-3.9.19-15.cm2.noarch.rpm
533+
python3-test-3.9.19-15.cm2.aarch64.rpm
534+
python3-tools-3.9.19-15.cm2.aarch64.rpm
535535
readline-8.1-1.cm2.aarch64.rpm
536536
readline-debuginfo-8.1-1.cm2.aarch64.rpm
537537
readline-devel-8.1-1.cm2.aarch64.rpm

toolkit/resources/manifests/package/toolchain_x86_64.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -516,28 +516,28 @@ procps-ng-devel-3.3.17-2.cm2.x86_64.rpm
516516
procps-ng-lang-3.3.17-2.cm2.x86_64.rpm
517517
pyproject-rpm-macros-1.0.0~rc1-4.cm2.noarch.rpm
518518
python-markupsafe-debuginfo-2.1.0-1.cm2.x86_64.rpm
519-
python3-3.9.19-14.cm2.x86_64.rpm
519+
python3-3.9.19-15.cm2.x86_64.rpm
520520
python3-audit-3.0.6-8.cm2.x86_64.rpm
521521
python3-cracklib-2.9.7-5.cm2.x86_64.rpm
522-
python3-curses-3.9.19-14.cm2.x86_64.rpm
522+
python3-curses-3.9.19-15.cm2.x86_64.rpm
523523
python3-Cython-0.29.33-2.cm2.x86_64.rpm
524-
python3-debuginfo-3.9.19-14.cm2.x86_64.rpm
525-
python3-devel-3.9.19-14.cm2.x86_64.rpm
524+
python3-debuginfo-3.9.19-15.cm2.x86_64.rpm
525+
python3-devel-3.9.19-15.cm2.x86_64.rpm
526526
python3-gpg-1.16.0-2.cm2.x86_64.rpm
527527
python3-jinja2-3.0.3-7.cm2.noarch.rpm
528528
python3-libcap-ng-0.8.2-2.cm2.x86_64.rpm
529-
python3-libs-3.9.19-14.cm2.x86_64.rpm
529+
python3-libs-3.9.19-15.cm2.x86_64.rpm
530530
python3-libxml2-2.10.4-8.cm2.x86_64.rpm
531531
python3-lxml-4.9.1-1.cm2.x86_64.rpm
532532
python3-magic-5.40-3.cm2.noarch.rpm
533533
python3-markupsafe-2.1.0-1.cm2.x86_64.rpm
534534
python3-newt-0.52.21-5.cm2.x86_64.rpm
535-
python3-pip-3.9.19-14.cm2.noarch.rpm
535+
python3-pip-3.9.19-15.cm2.noarch.rpm
536536
python3-pygments-2.4.2-7.cm2.noarch.rpm
537537
python3-rpm-4.18.0-4.cm2.x86_64.rpm
538-
python3-setuptools-3.9.19-14.cm2.noarch.rpm
539-
python3-test-3.9.19-14.cm2.x86_64.rpm
540-
python3-tools-3.9.19-14.cm2.x86_64.rpm
538+
python3-setuptools-3.9.19-15.cm2.noarch.rpm
539+
python3-test-3.9.19-15.cm2.x86_64.rpm
540+
python3-tools-3.9.19-15.cm2.x86_64.rpm
541541
readline-8.1-1.cm2.x86_64.rpm
542542
readline-debuginfo-8.1-1.cm2.x86_64.rpm
543543
readline-devel-8.1-1.cm2.x86_64.rpm

0 commit comments

Comments
 (0)