Skip to content

os.path.realpath() on Windows misresolves a broken symlink reached through a directory symlink #160506

Closed

Description

@gpshead

Bug report

Bug description:

On Windows, os.path.realpath() returns the wrong path for a broken relative symlink when the path used to reach it goes through a symlink to a directory. ntpath._readlink_deep() joins the link target to dirname() of the path as given, so a .. in the target removes the directory link's name instead of going up from the directory that contains the link. The join dates from bpo-38453 (GH-16967).

tarfile's data and tar extraction filters use this result to decide where a member will be written. Whoever supplies the archive can make extractall() create a new file outside the destination directory, in a directory that already exists, when the extracting process is able to create symlinks.

# Windows, with permission to create symlinks
import os
os.makedirs(r"C:\work\out")
os.symlink(".", r"C:\work\out\alias", target_is_directory=True)
os.symlink(r"..\outside\new.txt", r"C:\work\out\link")
print(os.path.realpath(r"C:\work\out\link"))
print(os.path.realpath(r"C:\work\out\alias\link"))

Both names are the same link, so both lines should be C:\work\outside\new.txt; the second is C:\work\out\outside\new.txt. Opening either name for writing creates C:\work\outside\new.txt.

An archive with the members alias -> ., alias/link -> ../outside/new.txt and a regular file alias/link is extracted by extractall(r"C:\work\out", filter="data") without error and the file's content ends up in C:\work\outside\new.txt. The same archive with the regular file named link raises OutsideDestinationError.

I have not run this on Windows. The output above comes from running the Windows branch of Lib/ntpath.py and Lib/tarfile.py from main on Linux over stand-ins for the nt functions, and from reading the code. test_realpath_broken_symlinks in test_ntpath already asserts the textual join (broken2, broken3). gh-88102, gh-85035 and gh-110495 are related realpath() issues but do not cover this case.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Other

Linked PRs

Activity

  1. self-assigned this
    on Oct 23, 2026
  2. added a commit that references this issue on Oct 31, 2026
  3. added a commit that references this issue on Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

OS-windowsstdlibStandard Library Python modules in the Lib/ directorytype-securityA security issue

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions