gh-142155: Fix infinite recursion in shutil.copytree on Windows junctions#142156
gh-142155: Fix infinite recursion in shutil.copytree on Windows junctions#142156ChuheLin wants to merge 7 commits into
Conversation
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
picnixz
left a comment
There was a problem hiding this comment.
If you want to have a recursive guard, use a frozenset() as a default and update the frozenset every time. We do this in typing.py I think or maybe elsewhere (search for recursive_guard in Lib)
Sorry, something went wrong.
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
Sorry, something went wrong.
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
picnixz
left a comment
There was a problem hiding this comment.
Address all changes before requesting a new review please, either by adding a comment if I was not clear or by changing the code.
Sorry, something went wrong.
Thanks for the detailed review! It's getting late in my time zone, so I'm going to get some rest now. I will go through all your comments and update the code first thing tomorrow. |
Sorry, something went wrong.
|
This PR is stale because it has been open for 30 days with no activity. |
Sorry, something went wrong.
Summary
This PR fixes a critical crash (WinError 206 / WinError 1921) in
shutil.copytreeon Windows when encountering directory junctions that point to a parent directory (recursive cycles).The Problem
On Windows,
shutil.copytreecontains specific logic to force traversal into Directory Junctions by treating them as standard directories (is_symlinkis forced toFalse).However, unlike
os.walkorshutil.rmtree(after recent fixes),copytreelacked a cycle detection mechanism for these traversed junctions. This caused infinite recursion until the path exceeded the OS limit, resulting in an unhandledOSError(Crash) rather than a PythonRecursionError.The Fix
I implemented cycle detection using file system identity (
st_dev,st_ino), similar to how deepcopy handles recursion:_seenset parameter tocopytreeand its helper_copytree.(st.st_dev, st.st_ino)is already in_seen.shutil.Error("Infinite recursion detected")immediately, preventing the crash.Verification
test_copytree_recursive_junctiontoLib/test/test_shutil.py.WinError 206crash.shutil.Error: Infinite recursion detected.