summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Marko <peter.marko@siemens.com>2025-08-09 22:37:21 +0200
committerSteve Sakoman <steve@sakoman.com>2025-08-20 07:37:19 -0700
commit6d83b872af7421e1e4f18c999f356855116dc238 (patch)
tree7e1d96591659e800508c185dbc19b4606d8fb883
parent0263c3dfa4fe1b26619f028a6a1c242e7e221dbf (diff)
downloadpoky-6d83b872af7421e1e4f18c999f356855116dc238.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: 34f1b4877a0601d2057453c159c76a54754f229a) 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.12.11.bb9
2 files changed, 224 insertions, 4 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..b8243a67f6
--- /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@@ -1615,6 +1615,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@@ -50,6 +50,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@@ -4488,6 +4489,161 @@ class OverwriteTests(archiver_tests.OverwriteTests, unittest.TestCase):
50 ar.extractall(self.testdir, filter='fully_trusted')
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.12.11.bb b/meta/recipes-devtools/python/python3_3.12.11.bb
index 84c4f74158..1c31077320 100644
--- a/meta/recipes-devtools/python/python3_3.12.11.bb
+++ b/meta/recipes-devtools/python/python3_3.12.11.bb
@@ -34,6 +34,7 @@ SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \
34 file://0001-test_deadlock-skip-problematic-test.patch \ 34 file://0001-test_deadlock-skip-problematic-test.patch \
35 file://0001-test_active_children-skip-problematic-test.patch \ 35 file://0001-test_active_children-skip-problematic-test.patch \
36 file://0001-test_readline-skip-limited-history-test.patch \ 36 file://0001-test_readline-skip-limited-history-test.patch \
37 file://CVE-2025-8194.patch \
37 " 38 "
38 39
39SRC_URI:append:class-native = " \ 40SRC_URI:append:class-native = " \
@@ -184,14 +185,14 @@ do_install:append:class-native() {
184 # when they're only used for python called with -O or -OO. 185 # when they're only used for python called with -O or -OO.
185 #find ${D} -name *opt-*.pyc -delete 186 #find ${D} -name *opt-*.pyc -delete
186 # Remove all pyc files. There are a ton of them and it is probably faster to let 187 # Remove all pyc files. There are a ton of them and it is probably faster to let
187 # python create the ones it wants at runtime rather than manage in the sstate 188 # python create the ones it wants at runtime rather than manage in the sstate
188 # tarballs and sysroot creation. 189 # tarballs and sysroot creation.
189 find ${D} -name *.pyc -delete 190 find ${D} -name *.pyc -delete
190 191
191 # Nothing should be looking into ${B} for python3-native 192 # Nothing should be looking into ${B} for python3-native
192 sed -i -e 's:${B}:/build/path/unavailable/:g' \ 193 sed -i -e 's:${B}:/build/path/unavailable/:g' \
193 ${D}/${libdir}/python${PYTHON_MAJMIN}/config-${PYTHON_MAJMIN}${PYTHON_ABI}*/Makefile 194 ${D}/${libdir}/python${PYTHON_MAJMIN}/config-${PYTHON_MAJMIN}${PYTHON_ABI}*/Makefile
194 195
195 # disable the lookup in user's site-packages globally 196 # disable the lookup in user's site-packages globally
196 sed -i 's#ENABLE_USER_SITE = None#ENABLE_USER_SITE = False#' ${D}${libdir}/python${PYTHON_MAJMIN}/site.py 197 sed -i 's#ENABLE_USER_SITE = None#ENABLE_USER_SITE = False#' ${D}${libdir}/python${PYTHON_MAJMIN}/site.py
197 198
@@ -226,7 +227,7 @@ do_install:append() {
226 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_range.cpython* 227 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_range.cpython*
227 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_xml_etree.cpython* 228 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/test/__pycache__/test_xml_etree.cpython*
228 229
229 # Similar to the above, we're getting reproducibility issues with 230 # Similar to the above, we're getting reproducibility issues with
230 # /usr/lib/python3.10/__pycache__/traceback.cpython-310.pyc 231 # /usr/lib/python3.10/__pycache__/traceback.cpython-310.pyc
231 # so remove it too 232 # so remove it too
232 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/__pycache__/traceback.cpython* 233 rm -f ${D}${libdir}/python${PYTHON_MAJMIN}/__pycache__/traceback.cpython*
@@ -303,7 +304,7 @@ py_package_preprocess () {
303 cd - 304 cd -
304 305
305 mv ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config-${MULTILIB_SUFFIX} 306 mv ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config ${PKGD}/${bindir}/python${PYTHON_MAJMIN}-config-${MULTILIB_SUFFIX}
306 307
307 #Remove the unneeded copy of target sysconfig data 308 #Remove the unneeded copy of target sysconfig data
308 rm -rf ${PKGD}/${libdir}/python-sysconfigdata 309 rm -rf ${PKGD}/${libdir}/python-sysconfigdata
309} 310}