diff mbox series

[wrynose,3/3] python3: fix CVE-2026-87910

Message ID 20261016053944.3835603-4-dkelaiya@cisco.com
State New
Delegated to: Yoann Congal
Headers show
Series python3: upgrade to 3.14.8 and backport security fixes | expand

Commit Message

From: Darsh Kelaiya <dkelaiya@cisco.com>

This patch applies the upstream fix shown in [1]. The Windows test
correction is applied from [2]. The advisory identifying the fix is
referenced in [3].

[1] https://github.com/python/cpython/commit/fb2f0bbc3b35264f09cc2cb2934b7987527a6bc2
[2] https://github.com/python/cpython/commit/d9565e54b1fc6d63c5be9afd58114499128fa57b
[3] https://nvd.nist.gov/vuln/detail/cve-2026-87910

Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
 .../python/python3/CVE-2026-87910_p1.patch    | 98 +++++++++++++++++++
 .../python/python3/CVE-2026-87910_p2.patch    | 55 +++++++++++
 .../recipes-devtools/python/python3_3.14.8.bb |  2 +
 3 files changed, 155 insertions(+)
 create mode 100644 meta/recipes-devtools/python/python3/CVE-2026-87910_p1.patch
 create mode 100644 meta/recipes-devtools/python/python3/CVE-2026-87910_p2.patch
diff mbox series

Patch

diff --git a/meta/recipes-devtools/python/python3/CVE-2026-87910_p1.patch b/meta/recipes-devtools/python/python3/CVE-2026-87910_p1.patch
new file mode 100644
index 0000000000..9786e82619
--- /dev/null
+++ b/meta/recipes-devtools/python/python3/CVE-2026-87910_p1.patch
@@ -0,0 +1,98 @@ 
+From 19b8b2011fd1743841510e44b8e9e87f3a3f4ba0 Mon Sep 17 00:00:00 2001
+From: Petr Viktorin <encukou@gmail.com>
+Date: Fri, 11 Sep 2026 14:19:35 +0200
+Subject: [PATCH] gh-157265: tarfile: Honor None result of filter for link
+ fallbacks (GH-157266)
+
+Co-authored-by: Stan Ulbrych <stan@python.org>
+
+CVE: CVE-2026-87910
+Upstream-Status: Backport [https://github.com/python/cpython/commit/fb2f0bbc3b35264f09cc2cb2934b7987527a6bc2]
+
+(cherry picked from commit fb2f0bbc3b35264f09cc2cb2934b7987527a6bc2)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ Lib/tarfile.py                                |  4 ++-
+ Lib/test/test_tarfile.py                      | 31 +++++++++++++++++--
+ ...-09-10-13-38-11.gh-issue-157265.-vYuMp.rst |  3 ++
+ 3 files changed, 34 insertions(+), 4 deletions(-)
+ create mode 100644 Misc/NEWS.d/next/Security/2026-09-10-13-38-11.gh-issue-157265.-vYuMp.rst
+
+diff --git a/Lib/tarfile.py b/Lib/tarfile.py
+index b7b4af15b1..89e1e9b218 100644
+--- a/Lib/tarfile.py
++++ b/Lib/tarfile.py
+@@ -2818,9 +2818,11 @@ class TarFile(object):
+                     "makelink_with_filter: if filter_function is not None, "
+                     + "extraction_root must also not be None")
+             try:
+-                filter_function(
++                filtered = filter_function(
+                     unfiltered.replace(name=tarinfo.name, deep=False),
+                     extraction_root)
++                if filtered is None:
++                    return
+                 filtered = filter_function(unfiltered, extraction_root)
+             except _FILTER_ERRORS as cause:
+                 raise LinkFallbackError(tarinfo, unfiltered.name) from cause
+diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
+index ab13ab34fc..c09592075b 100644
+--- a/Lib/test/test_tarfile.py
++++ b/Lib/test/test_tarfile.py
+@@ -4395,9 +4395,15 @@ class TestExtractionFilters(unittest.TestCase):
+         for filter in 'tar', 'fully_trusted':
+             with self.subTest(filter), self.check_context(arc.open(), filter):
+                 if not os_helper.can_symlink():
+-                    self.expect_file("a/t/dummy")
+-                    self.expect_file("b/")
+-                    self.expect_file("c/")
++                    if filter == 'tar':
++                        self.expect_exception(
++                            tarfile.LinkFallbackError,
++                            "link 'boom' would be extracted as a copy of "
++                            + "'c/escape', which was rejected")
++                    else:
++                        self.expect_file("a/t/dummy")
++                        self.expect_file("b/")
++                        self.expect_file("c/")
+                 else:
+                     self.expect_file("a/t/dummy")
+                     self.expect_file("b/")
+@@ -4576,6 +4582,25 @@ class TestExtractionFilters(unittest.TestCase):
+             if os_helper.can_chmod():
+                 self.assertFalse(path.stat().st_mode & stat.S_IWUSR)
+ 
++    @symlink_test
++    def test_extract_filters_target_none(self):
++        # Test that when extract() falls back to extracting (rather than
++        # linking) a hardlink target, the member is skipped if the filter
++        # returns None.
++        with ArchiveMaker() as arc:
++            arc.add('a/b/s', symlink_to='../escape')
++            arc.add('q', hardlink_to='a/b/s')
++        def filter_unsafe_members(member, path):
++            try:
++                return tarfile.data_filter(member, path)
++            except tarfile.FilterError as error:
++                return None
++        with self.check_context(arc.open(), filter_unsafe_members):
++            if os_helper.can_symlink():
++                self.expect_file('a/b/s', symlink_to='../escape')
++            else:
++                self.expect_file('a/b/')  # symlink is not extracted
++
+     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-09-10-13-38-11.gh-issue-157265.-vYuMp.rst b/Misc/NEWS.d/next/Security/2026-09-10-13-38-11.gh-issue-157265.-vYuMp.rst
+new file mode 100644
+index 0000000000..ba27e47f73
+--- /dev/null
++++ b/Misc/NEWS.d/next/Security/2026-09-10-13-38-11.gh-issue-157265.-vYuMp.rst
+@@ -0,0 +1,3 @@
++In :mod:`tarfile`, when extracting a link falls back to extracting a member
++of the archive, skip the member when the filter function returns None when
++called with the extracted member's name replaced with the link's.
+-- 
+2.35.6
+
diff --git a/meta/recipes-devtools/python/python3/CVE-2026-87910_p2.patch b/meta/recipes-devtools/python/python3/CVE-2026-87910_p2.patch
new file mode 100644
index 0000000000..fc97378719
--- /dev/null
+++ b/meta/recipes-devtools/python/python3/CVE-2026-87910_p2.patch
@@ -0,0 +1,55 @@ 
+From 8dd3fcd26e7effd2de3b3ec7709d80632b6acfc7 Mon Sep 17 00:00:00 2001
+From: Petr Viktorin <encukou@gmail.com>
+Date: Fri, 11 Sep 2026 19:54:41 +0200
+Subject: [PATCH] gh-157265: Adjust test for Windows (GH-157334)
+
+gh-157266: Adjust test for Windows
+
+On Windows (no symlinks, no hardlinks), the behaviour is
+the same as without the fix in GH-157266:
+- a/t/dummy is extracted
+- b/ is extracted
+- c/ is *not* created (the target, a/t, is not in the archive)
+- c/escape: c/ is created; escape is skipped (target,
+   c/../../link_here, is not in archive)
+- c is not recreated as a directory
+- boom is not created (target is c/escape, which falls back to
+  ..\..\link_here, which does not exist in archive)
+
+CVE: CVE-2026-87910
+Upstream-Status: Backport [https://github.com/python/cpython/commit/d9565e54b1fc6d63c5be9afd58114499128fa57b]
+
+(cherry picked from commit d9565e54b1fc6d63c5be9afd58114499128fa57b)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ Lib/test/test_tarfile.py | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
+index c09592075b..7e072d9090 100644
+--- a/Lib/test/test_tarfile.py
++++ b/Lib/test/test_tarfile.py
+@@ -4395,15 +4395,15 @@ class TestExtractionFilters(unittest.TestCase):
+         for filter in 'tar', 'fully_trusted':
+             with self.subTest(filter), self.check_context(arc.open(), filter):
+                 if not os_helper.can_symlink():
+-                    if filter == 'tar':
++                    if filter == 'fully_trusted' or sys.platform == "win32":
++                        self.expect_file("a/t/dummy")
++                        self.expect_file("b/")
++                        self.expect_file("c/")
++                    else:
+                         self.expect_exception(
+                             tarfile.LinkFallbackError,
+                             "link 'boom' would be extracted as a copy of "
+                             + "'c/escape', which was rejected")
+-                    else:
+-                        self.expect_file("a/t/dummy")
+-                        self.expect_file("b/")
+-                        self.expect_file("c/")
+                 else:
+                     self.expect_file("a/t/dummy")
+                     self.expect_file("b/")
+-- 
+2.35.6
+
diff --git a/meta/recipes-devtools/python/python3_3.14.8.bb b/meta/recipes-devtools/python/python3_3.14.8.bb
index 6aa5514246..2bf2806ee8 100644
--- a/meta/recipes-devtools/python/python3_3.14.8.bb
+++ b/meta/recipes-devtools/python/python3_3.14.8.bb
@@ -36,6 +36,8 @@  SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \
            file://0001-test_only_active_thread-skip-problematic-test.patch \
            file://0001-prefer-valid-entrypoints.patch \
            file://CVE-2025-15367.patch \
+           file://CVE-2026-87910_p1.patch \
+           file://CVE-2026-87910_p2.patch \
            "
 SRC_URI:append:class-native = " \
            file://0001-Lib-sysconfig.py-use-prefix-value-from-build-configu.patch \