Skip to content

Ensure ntpath.realpath() result is a final, normalized pathname. #110495

@moonsikpark

Description

@moonsikpark

Bug report

Bug description:

Note that _readlink_deep() does nothing to ensure that the result is a final, normalized pathname. It just tries to keep reading symlinks until the target isn't a symlink or access/reading fails in a manner that's allowed. If it succeeds, the resulting pathname may include any number of symlinks, mount points, and short names. _getfinalpathname_nonstrict() should instead pass both the pathname and the seen set into _readlink_deep(). If it succeeds, then add the resulting pathname to the set, and continue the loop to try to get a final, normalized pathname.

Originally posted by @eryksun in #110298 (comment)

I was able to reproduce this issue. Run the following code block with Administrator privileges in Windows:

mkdir C:\testdir
mkdir C:\testdir\real
echo 1 > C:\testdir\real\file.txt
icacls C:\testdir\real\file.txt /deny *S-1-5-32-545:(S)
mklink /d C:\testdir\symlink1 C:\testdir\real
mklink /d C:\testdir\symlink2 C:\testdir\symlink1
mklink C:\testdir\symlink2\filelink.txt C:\testdir\symlink1\file.txt
python -c "import ntpath; print(ntpath.realpath('C:\\testdir\\symlink2\\filelink.txt'))"

REM Cleanup
icacls C:\testdir\real\file.txt /grant *S-1-5-32-545:(S)
rmdir C:\testdir /s /q

Expected output:

C:\testdir\real\file.txt

Actual output:

C:\testdir\symlink1\file.txt

This happens because _getfinalpathname_nonstrict() returns the result of _readlink_deep() if the return value is different from the input.

cpython/Lib/ntpath.py

Lines 680 to 686 in 92ca90b

try:
# The OS could not resolve this path fully, so we attempt
# to follow the link ourselves. If we succeed, join the tail
# and return.
new_path = _readlink_deep(path)
if new_path != path:
return join(new_path, tail) if tail else new_path

However, because _readlink_deep() does not ensure that it's result is a final, normalized pathname, ntpath.realpath() result is not ensured to be a final, normalized pathname.

I'll try to come up with a patch. @eryksun, if you have any further discussions or comments on how this should be fixed, please let me know in the comments.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Windows

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    OS-windowstype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions