python3/00492-cve-2026-11940.patch
RHEL Packaging Agent 294a585edf Fix CVE-2026-11940: symlink escape via tarfile hardlink fallback
Security fix for CVE-2026-11940: a symlink escape vulnerability
via tarfile hardlink-extraction fallback. The new backport patch
(00491-cve-2026-11940.patch) adds a filter check on the
hardlink's target name before performing the fallback copy,
preventing extraction of symlinks that point outside the
destination directory.

CVE: CVE-2026-11940
Upstream patches:
 - 27dd970bf6.patch
Resolves: RHEL-227194

This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent.

Assisted-by: Ymir
2026-08-10 15:50:24 +02:00

60 lines
2.6 KiB
Diff

From a235238fe0884e85cdacbc986a4749c5c4717212 Mon Sep 17 00:00:00 2001
From: Stan Ulbrych <stan@python.org>
Date: Tue, 23 Jun 2026 14:31:38 +0100
Subject: [PATCH] gh-151558: Fix symlink escape via `tarfile`
hardlink-extraction fallback (GH-151559)
---
Lib/tarfile.py | 3 +++
Lib/test/test_tarfile.py | 23 +++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 3e03ebf..61d7e46 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2642,6 +2642,9 @@ class TarFile(object):
"makelink_with_filter: if filter_function is not None, "
+ "extraction_root must also not be None")
try:
+ filter_function(
+ unfiltered.replace(name=tarinfo.name, deep=False),
+ extraction_root)
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 0a7586a..d43559a 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -3543,6 +3543,29 @@ class TestExtractionFilters(unittest.TestCase):
self.expect_file("boom", symlink_to='../../link_here')
self.expect_file("c", symlink_to='b')
+ def test_sneaky_hardlink_fallback_deep(self):
+ # (CVE-2026-11940)
+ with ArchiveMaker() as arc:
+ arc.add("a/b/s", symlink_to=os.path.join("..", "escape"))
+ arc.add("s", hardlink_to=os.path.join("a", "b", "s"))
+
+ with self.check_context(arc.open(), 'data'):
+ e = self.expect_exception(
+ tarfile.LinkFallbackError,
+ "link 's' would be extracted as a copy of "
+ + "'a/b/s', which was rejected")
+ self.assertIsInstance(e.__cause__,
+ tarfile.LinkOutsideDestinationError)
+
+ for filter in 'tar', 'fully_trusted':
+ with self.subTest(filter), self.check_context(arc.open(), filter):
+ if not support.can_symlink():
+ self.expect_file("a/")
+ self.expect_file("a/b/")
+ else:
+ self.expect_file("a/b/s", symlink_to=os.path.join('..', 'escape'))
+ self.expect_file("s", symlink_to=os.path.join('..', 'escape'))
+
def test_exfiltration_via_symlink(self):
# (CVE-2025-4138)
# Test changing symlinks that result in a symlink pointing outside