summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Marko <peter.marko@siemens.com>2025-08-09 22:37:35 +0200
committerSteve Sakoman <steve@sakoman.com>2025-08-18 13:18:01 -0700
commite371e6b3a76bfe1d875ca19798e5152af7596927 (patch)
treeef71d4cba3e480202f2b91ee6f6a6b0f820b746c
parentdc468377e8843b52b1a55d4b0ba30bbc4b40a7a5 (diff)
downloadpoky-e371e6b3a76bfe1d875ca19798e5152af7596927.tar.gz
python3: patch CVE-2025-8194
Pick commit from 3.12 branch mentioned in NVD report. https://nvd.nist.gov/vuln/detail/CVE-2025-8194 (From OE-Core rev: 4ae9daf3d05530952a8b002257dd9afda2e077e4) Signed-off-by: Peter Marko <peter.marko@siemens.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/python/python3/CVE-2025-8194.patch219
-rw-r--r--meta/recipes-devtools/python/python3_3.10.18.bb7
2 files changed, 223 insertions, 3 deletions
diff --git a/meta/recipes-devtools/python/python3/CVE-2025-8194.patch b/meta/recipes-devtools/python/python3/CVE-2025-8194.patch
new file mode 100644
index 0000000000..44ada01133
--- /dev/null
+++ b/meta/recipes-devtools/python/python3/CVE-2025-8194.patch
@@ -0,0 +1,219 @@
1From c9d9f78feb1467e73fd29356c040bde1c104f29f Mon Sep 17 00:00:00 2001
2From: "Miss Islington (bot)"
3 <31488909+miss-islington@users.noreply.github.com>
4Date: Mon, 4 Aug 2025 13:45:06 +0200
5Subject: [PATCH] [3.12] gh-130577: tarfile now validates archives to ensure
6 member offsets are non-negative (GH-137027) (#137171)
7
8(cherry picked from commit 7040aa54f14676938970e10c5f74ea93cd56aa38)
9
10Co-authored-by: Alexander Urieles <aeurielesn@users.noreply.github.com>
11Co-authored-by: Gregory P. Smith <greg@krypto.org>
12
13CVE: CVE-2025-8194
14Upstream-Status: Backport [https://github.com/python/cpython/commit/c9d9f78feb1467e73fd29356c040bde1c104f29f]
15Signed-off-by: Peter Marko <peter.marko@siemens.com>
16---
17 Lib/tarfile.py | 3 +
18 Lib/test/test_tarfile.py | 156 ++++++++++++++++++
19 ...-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3 +
20 3 files changed, 162 insertions(+)
21 create mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
22
23diff --git a/Lib/tarfile.py b/Lib/tarfile.py
24index 9999a99d54..59d3f6e5cc 100755
25--- a/Lib/tarfile.py
26+++ b/Lib/tarfile.py
27@@ -1613,6 +1613,9 @@ class TarInfo(object):
28 """Round up a byte count by BLOCKSIZE and return it,
29 e.g. _block(834) => 1024.
30 """
31+ # Only non-negative offsets are allowed
32+ if count < 0:
33+ raise InvalidHeaderError("invalid offset")
34 blocks, remainder = divmod(count, BLOCKSIZE)
35 if remainder:
36 blocks += 1
37diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
38index a184ba75a8..759fa03ead 100644
39--- a/Lib/test/test_tarfile.py
40+++ b/Lib/test/test_tarfile.py
41@@ -49,6 +49,7 @@ bz2name = os.path.join(TEMPDIR, "testtar.tar.bz2")
42 xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
43 tmpname = os.path.join(TEMPDIR, "tmp.tar")
44 dotlessname = os.path.join(TEMPDIR, "testtar")
45+SPACE = b" "
46
47 sha256_regtype = (
48 "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
49@@ -4273,6 +4274,161 @@ class TestExtractionFilters(unittest.TestCase):
50 self.expect_exception(TypeError) # errorlevel is not int
51
52
53+class OffsetValidationTests(unittest.TestCase):
54+ tarname = tmpname
55+ invalid_posix_header = (
56+ # name: 100 bytes
57+ tarfile.NUL * tarfile.LENGTH_NAME
58+ # mode, space, null terminator: 8 bytes
59+ + b"000755" + SPACE + tarfile.NUL
60+ # uid, space, null terminator: 8 bytes
61+ + b"000001" + SPACE + tarfile.NUL
62+ # gid, space, null terminator: 8 bytes
63+ + b"000001" + SPACE + tarfile.NUL
64+ # size, space: 12 bytes
65+ + b"\xff" * 11 + SPACE
66+ # mtime, space: 12 bytes
67+ + tarfile.NUL * 11 + SPACE
68+ # chksum: 8 bytes
69+ + b"0011407" + tarfile.NUL
70+ # type: 1 byte
71+ + tarfile.REGTYPE
72+ # linkname: 100 bytes
73+ + tarfile.NUL * tarfile.LENGTH_LINK
74+ # magic: 6 bytes, version: 2 bytes
75+ + tarfile.POSIX_MAGIC
76+ # uname: 32 bytes
77+ + tarfile.NUL * 32
78+ # gname: 32 bytes
79+ + tarfile.NUL * 32
80+ # devmajor, space, null terminator: 8 bytes
81+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
82+ # devminor, space, null terminator: 8 bytes
83+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
84+ # prefix: 155 bytes
85+ + tarfile.NUL * tarfile.LENGTH_PREFIX
86+ # padding: 12 bytes
87+ + tarfile.NUL * 12
88+ )
89+ invalid_gnu_header = (
90+ # name: 100 bytes
91+ tarfile.NUL * tarfile.LENGTH_NAME
92+ # mode, null terminator: 8 bytes
93+ + b"0000755" + tarfile.NUL
94+ # uid, null terminator: 8 bytes
95+ + b"0000001" + tarfile.NUL
96+ # gid, space, null terminator: 8 bytes
97+ + b"0000001" + tarfile.NUL
98+ # size, space: 12 bytes
99+ + b"\xff" * 11 + SPACE
100+ # mtime, space: 12 bytes
101+ + tarfile.NUL * 11 + SPACE
102+ # chksum: 8 bytes
103+ + b"0011327" + tarfile.NUL
104+ # type: 1 byte
105+ + tarfile.REGTYPE
106+ # linkname: 100 bytes
107+ + tarfile.NUL * tarfile.LENGTH_LINK
108+ # magic: 8 bytes
109+ + tarfile.GNU_MAGIC
110+ # uname: 32 bytes
111+ + tarfile.NUL * 32
112+ # gname: 32 bytes
113+ + tarfile.NUL * 32
114+ # devmajor, null terminator: 8 bytes
115+ + tarfile.NUL * 8
116+ # devminor, null terminator: 8 bytes
117+ + tarfile.NUL * 8
118+ # padding: 167 bytes
119+ + tarfile.NUL * 167
120+ )
121+ invalid_v7_header = (
122+ # name: 100 bytes
123+ tarfile.NUL * tarfile.LENGTH_NAME
124+ # mode, space, null terminator: 8 bytes
125+ + b"000755" + SPACE + tarfile.NUL
126+ # uid, space, null terminator: 8 bytes
127+ + b"000001" + SPACE + tarfile.NUL
128+ # gid, space, null terminator: 8 bytes
129+ + b"000001" + SPACE + tarfile.NUL
130+ # size, space: 12 bytes
131+ + b"\xff" * 11 + SPACE
132+ # mtime, space: 12 bytes
133+ + tarfile.NUL * 11 + SPACE
134+ # chksum: 8 bytes
135+ + b"0010070" + tarfile.NUL
136+ # type: 1 byte
137+ + tarfile.REGTYPE
138+ # linkname: 100 bytes
139+ + tarfile.NUL * tarfile.LENGTH_LINK
140+ # padding: 255 bytes
141+ + tarfile.NUL * 255
142+ )
143+ valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT)
144+ data_block = b"\xff" * tarfile.BLOCKSIZE
145+
146+ def _write_buffer(self, buffer):
147+ with open(self.tarname, "wb") as f:
148+ f.write(buffer)
149+
150+ def _get_members(self, ignore_zeros=None):
151+ with open(self.tarname, "rb") as f:
152+ with tarfile.open(
153+ mode="r", fileobj=f, ignore_zeros=ignore_zeros
154+ ) as tar:
155+ return tar.getmembers()
156+
157+ def _assert_raises_read_error_exception(self):
158+ with self.assertRaisesRegex(
159+ tarfile.ReadError, "file could not be opened successfully"
160+ ):
161+ self._get_members()
162+
163+ def test_invalid_offset_header_validations(self):
164+ for tar_format, invalid_header in (
165+ ("posix", self.invalid_posix_header),
166+ ("gnu", self.invalid_gnu_header),
167+ ("v7", self.invalid_v7_header),
168+ ):
169+ with self.subTest(format=tar_format):
170+ self._write_buffer(invalid_header)
171+ self._assert_raises_read_error_exception()
172+
173+ def test_early_stop_at_invalid_offset_header(self):
174+ buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header
175+ self._write_buffer(buffer)
176+ members = self._get_members()
177+ self.assertEqual(len(members), 1)
178+ self.assertEqual(members[0].name, "filename")
179+ self.assertEqual(members[0].offset, 0)
180+
181+ def test_ignore_invalid_archive(self):
182+ # 3 invalid headers with their respective data
183+ buffer = (self.invalid_gnu_header + self.data_block) * 3
184+ self._write_buffer(buffer)
185+ members = self._get_members(ignore_zeros=True)
186+ self.assertEqual(len(members), 0)
187+
188+ def test_ignore_invalid_offset_headers(self):
189+ for first_block, second_block, expected_offset in (
190+ (
191+ (self.valid_gnu_header),
192+ (self.invalid_gnu_header + self.data_block),
193+ 0,
194+ ),
195+ (
196+ (self.invalid_gnu_header + self.data_block),
197+ (self.valid_gnu_header),
198+ 1024,
199+ ),
200+ ):
201+ self._write_buffer(first_block + second_block)
202+ members = self._get_members(ignore_zeros=True)
203+ self.assertEqual(len(members), 1)
204+ self.assertEqual(members[0].name, "filename")
205+ self.assertEqual(members[0].offset, expected_offset)
206+
207+
208 def setUpModule():
209 os_helper.unlink(TEMPDIR)
210 os.makedirs(TEMPDIR)
211diff --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
212new file mode 100644
213index 0000000000..342cabbc86
214--- /dev/null
215+++ b/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
216@@ -0,0 +1,3 @@
217+:mod:`tarfile` now validates archives to ensure member offsets are
218+non-negative. (Contributed by Alexander Enrique Urieles Nieto in
219+:gh:`130577`.)
diff --git a/meta/recipes-devtools/python/python3_3.10.18.bb b/meta/recipes-devtools/python/python3_3.10.18.bb
index 875b52cde9..89036ff3b8 100644
--- a/meta/recipes-devtools/python/python3_3.10.18.bb
+++ b/meta/recipes-devtools/python/python3_3.10.18.bb
@@ -37,6 +37,7 @@ SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \
37 file://0001-Avoid-shebang-overflow-on-python-config.py.patch \ 37 file://0001-Avoid-shebang-overflow-on-python-config.py.patch \
38 file://0001-test_storlines-skip-due-to-load-variability.patch \ 38 file://0001-test_storlines-skip-due-to-load-variability.patch \
39 file://0001-gh-107811-tarfile-treat-overflow-in-UID-GID-as-failu.patch \ 39 file://0001-gh-107811-tarfile-treat-overflow-in-UID-GID-as-failu.patch \
40 file://CVE-2025-8194.patch \
40 " 41 "
41 42
42SRC_URI:append:class-native = " \ 43SRC_URI:append:class-native = " \
@@ -170,7 +171,7 @@ do_install:append:class-native() {
170 # when they're only used for python called with -O or -OO. 171 # when they're only used for python called with -O or -OO.
171 #find ${D} -name *opt-*.pyc -delete 172 #find ${D} -name *opt-*.pyc -delete
172 # Remove all pyc files. There are a ton of them and it is probably faster to let 173 # Remove all pyc files. There are a ton of them and it is probably faster to let
173 # python create the ones it wants at runtime rather than manage in the sstate 174 # python create the ones it wants at runtime rather than manage in the sstate
174 # tarballs and sysroot creation. 175 # tarballs and sysroot creation.
175 find ${D} -name *.pyc -delete 176 find ${D} -name *.pyc -delete
176 177
@@ -206,7 +207,7 @@ do_install:append() {
206 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_range.cpython* 207 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_range.cpython*
207 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_xml_etree.cpython* 208 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_xml_etree.cpython*
208 209
209 # Similar to the above, we're getting reproducibility issues with 210 # Similar to the above, we're getting reproducibility issues with
210 # /usr/lib/python3.10/__pycache__/traceback.cpython-310.pyc 211 # /usr/lib/python3.10/__pycache__/traceback.cpython-310.pyc
211 # so remove it too 212 # so remove it too
212 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/__pycache__/traceback.cpython* 213 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/__pycache__/traceback.cpython*
@@ -266,7 +267,7 @@ py_package_preprocess () {
266 cd - 267 cd -
267 268
268 mv ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config-${MULTILIB_SUFFIX} 269 mv ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config-${MULTILIB_SUFFIX}
269 270
270 #Remove the unneeded copy of target sysconfig data 271 #Remove the unneeded copy of target sysconfig data
271 rm -rf ${PKGD}/${libdir}/python-sysconfigdata 272 rm -rf ${PKGD}/${libdir}/python-sysconfigdata
272} 273}