From: Leonid Iziumtsev Date: Wed, 29 Jul 2026 10:12:57 +0000 (+0200) Subject: python3: fix CVE-2026-4360 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a28b6b43bbfed834a968eec14a329c230334bad4;p=thirdparty%2Fopenembedded%2Fopenembedded-core.git python3: fix CVE-2026-4360 Backport patch to fix CVE-2026-4360. Reference: https://nvd.nist.gov/vuln/detail/CVE-2026-4360 Upstream fix: https://github.com/python/cpython/commit/5e0ef3f1afe892e4f64eb83368db57ac4c40cba0 Signed-off-by: Leonid Iziumtsev Signed-off-by: Richard Purdie --- diff --git a/meta/recipes-devtools/python/python3/CVE-2026-4360.patch b/meta/recipes-devtools/python/python3/CVE-2026-4360.patch new file mode 100644 index 0000000000..d381508959 --- /dev/null +++ b/meta/recipes-devtools/python/python3/CVE-2026-4360.patch @@ -0,0 +1,148 @@ +From 66c8bc346c0c614edc05535145c0424a14fba213 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Mon, 29 Jun 2026 21:11:22 +0200 +Subject: [PATCH] gh-151987: Pass filter_function to `TarFile._extract_one()` + during `.extract()` (GH-151988) (#152609) + +(cherry picked from commit 7ccdbaba2c54250a70d7f25632152df7655a5e0a) + +Co-authored-by: Petr Viktorin +Co-authored-by: Seth Michael Larson + +CVE: CVE-2026-4360 +Upstream-Status: Backport [https://github.com/python/cpython/commit/5e0ef3f1afe892e4f64eb83368db57ac4c40cba0] + +Signed-off-by: Leonid Iziumtsev +--- + Lib/tarfile.py | 3 +- + Lib/test/test_tarfile.py | 92 +++++++++++++++++++ + ...-06-23-14-19-30.gh-issue-151987.8mNIMf.rst | 2 + + 3 files changed, 96 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst + +diff --git a/Lib/tarfile.py b/Lib/tarfile.py +index e6734db..2c46179 100644 +--- a/Lib/tarfile.py ++++ b/Lib/tarfile.py +@@ -2510,7 +2510,8 @@ class TarFile(object): + tarinfo, unfiltered = self._get_extract_tarinfo( + member, filter_function, path) + if tarinfo is not None: +- self._extract_one(tarinfo, path, set_attrs, numeric_owner) ++ self._extract_one(tarinfo, path, set_attrs, numeric_owner, ++ filter_function=filter_function) + + def _get_extract_tarinfo(self, member, filter_function, path): + """Get (filtered, unfiltered) TarInfos from *member* +diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py +index d974c7d..9a15585 100644 +--- a/Lib/test/test_tarfile.py ++++ b/Lib/test/test_tarfile.py +@@ -4399,6 +4399,98 @@ class TestExtractionFilters(unittest.TestCase): + st_mode = cc.outerdir.stat().st_mode + self.assertNotEqual(st_mode & 0o777, 0o777) + ++ @symlink_test ++ @unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown") ++ @unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown") ++ @unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid") ++ @support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE)) ++ def test_chown_links_on_extract(self, link_type): ++ with ArchiveMaker() as arc: ++ arc.add("test.txt", ++ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x') ++ arc.add("link", ++ type=link_type, ++ linkname='test.txt', ++ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x') ++ ++ with ( ++ os_helper.temp_dir() as tmpdir, ++ arc.open() as tar, ++ unittest.mock.patch("os.chown") as mock_chown, ++ unittest.mock.patch("os.lchown") as mock_lchown, ++ unittest.mock.patch("os.geteuid") as mock_geteuid, ++ ): ++ # Set UID to 0 so chown() is attempted. ++ mock_geteuid.return_value = 0 ++ tar.extract("link", path=tmpdir, filter='data') ++ extract_path = os.path.join(tmpdir, "link") ++ ++ if link_type == tarfile.SYMTYPE: ++ mock_chown.assert_not_called() ++ mock_lchown.assert_called_once_with(extract_path, -1, -1) ++ else: ++ mock_chown.assert_has_calls([ ++ unittest.mock.call(extract_path, -1, -1), ++ unittest.mock.call(extract_path, -1, -1) ++ ]) ++ mock_lchown.assert_not_called() ++ ++ @symlink_test ++ @unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown") ++ @unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown") ++ @unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid") ++ @support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE)) ++ def test_chown_links_on_extractall(self, link_type): ++ with ArchiveMaker() as arc: ++ arc.add("test.txt", ++ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x') ++ arc.add("link", ++ type=link_type, ++ linkname='test.txt', ++ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x') ++ ++ with ( ++ os_helper.temp_dir() as tmpdir, ++ arc.open() as tar, ++ unittest.mock.patch("os.chown") as mock_chown, ++ unittest.mock.patch("os.lchown") as mock_lchown, ++ unittest.mock.patch("os.geteuid") as mock_geteuid, ++ ): ++ # Set UID to 0 so chown() is attempted. ++ mock_geteuid.return_value = 0 ++ tar.extractall(path=tmpdir, filter='data') ++ extract_link_path = os.path.join(tmpdir, "link") ++ extract_file_path = os.path.join(tmpdir, "test.txt") ++ ++ if link_type == tarfile.SYMTYPE: ++ mock_chown.assert_called_once_with(extract_file_path, -1, -1) ++ mock_lchown.assert_called_once_with(extract_link_path, -1, -1) ++ else: ++ mock_chown.assert_has_calls([ ++ unittest.mock.call(extract_file_path, -1, -1), ++ unittest.mock.call(extract_link_path, -1, -1) ++ ]) ++ mock_lchown.assert_not_called() ++ ++ def test_extract_filters_target(self): ++ # Test that when extract() falls back to extracting (rather than ++ # linking) a hardlink target, it filters the target. ++ with ArchiveMaker() as arc: ++ arc.add("target") ++ arc.add("link", hardlink_to="target") ++ def testing_filter(member, path): ++ if member.name == 'target': ++ # target: set read-only ++ return member.replace(mode=stat.S_IRUSR) ++ # link: don't overwrite the mode ++ return member.replace(mode=None) ++ tempdir = pathlib.Path(TEMPDIR) / 'extract' ++ with os_helper.temp_dir(tempdir), arc.open() as tar: ++ tar.extract("link", path=tempdir, filter=testing_filter) ++ path = tempdir / 'link' ++ if os_helper.can_chmod(): ++ self.assertFalse(path.stat().st_mode & stat.S_IWUSR) ++ + def test_link_fallback_normalizes(self): + # Make sure hardlink fallbacks work for non-normalized paths for all + # filters +diff --git a/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst b/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst +new file mode 100644 +index 0000000..9eea7b3 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst +@@ -0,0 +1,2 @@ ++The :meth:`tarfile.TarFile.extract` method now applies the given filter when ++it extracts a link target from the archive as a fallback. diff --git a/meta/recipes-devtools/python/python3_3.14.6.bb b/meta/recipes-devtools/python/python3_3.14.6.bb index a45a456133..77333ce3e2 100644 --- a/meta/recipes-devtools/python/python3_3.14.6.bb +++ b/meta/recipes-devtools/python/python3_3.14.6.bb @@ -24,6 +24,7 @@ SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \ file://0001-prefer-valid-entrypoints.patch \ file://CVE-2026-11940.patch \ file://CVE-2026-11972.patch \ + file://CVE-2026-4360.patch \ " SRC_URI:append:class-native = " \ file://0001-Lib-sysconfig.py-use-prefix-value-from-build-configu.patch \