https://github.com/python/cpython/commit/b38be2e6cf9d989075ab73412c63e003ebad4ff3
commit: b38be2e6cf9d989075ab73412c63e003ebad4ff3
branch: main
author: Stan Ulbrych <[email protected]>
committer: encukou <[email protected]>
date: 2026-09-10T12:00:28+02:00
summary:
gh-157190: Follow symlinks when extracting `tarfile` hard links (GH-157191)
files:
M Lib/tarfile.py
M Lib/test/test_tarfile.py
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index c4cbbdb980857d..6e092f1dcee5e8 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2815,7 +2815,11 @@ def makelink_with_filter(self, tarinfo, targetpath,
if os.path.lexists(targetpath):
# Avoid FileExistsError on following os.link.
os.unlink(targetpath)
- os.link(tarinfo._link_target, targetpath)
+ # Resolve the target so the hard link points to the file
+ # itself. Otherwise os.link() may duplicate a symlink to a
+ # shallower location, where its relative target escapes the
+ # destination directory. (CVE-2026-82049)
+ os.link(os.path.realpath(tarinfo._link_target), targetpath)
return
except symlink_exception:
keyerror_to_extracterror = True
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 62edb7115ed682..be5abfe211fb92 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -4655,6 +4655,24 @@ def test_sneaky_hardlink_fallback_deep(self):
self.expect_file("a/b/s", symlink_to=os.path.join('..',
'escape'))
self.expect_file("s", symlink_to=os.path.join('..',
'escape'))
+ @symlink_test
+ @os_helper.skip_unless_hardlink
+ def test_sneaky_hardlink_relocation(self):
+ with ArchiveMaker() as arc:
+ arc.add("a/escape", content="decoy")
+ arc.add("a/b/s", symlink_to=os.path.join("..", "escape"))
+ arc.add("s", hardlink_to=os.path.join("a", "b", "s"))
+
+ for filter in 'data', 'tar':
+ with self.subTest(filter), self.check_context(arc.open(), filter):
+ self.expect_file("a/escape", content="decoy")
+ if os_helper.can_symlink():
+ self.expect_file("a/b/s", symlink_to=os.path.join('..',
'escape'))
+ else:
+ self.expect_file("a/b/s", content="decoy")
+ self.expect_file("s", content="decoy")
+ self.assertFalse((self.destdir / "s").is_symlink())
+
@symlink_test
def test_exfiltration_via_symlink(self):
# (CVE-2025-4138)
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]