Skip to content

tmpdir: prevent symlink attacks and TOCTOU races (CVE-2025-71176)#14279

Open
laurac8r wants to merge 12 commits intopytest-dev:mainfrom
laurac8r:hotfix/cve
Open

tmpdir: prevent symlink attacks and TOCTOU races (CVE-2025-71176)#14279
laurac8r wants to merge 12 commits intopytest-dev:mainfrom
laurac8r:hotfix/cve

Conversation

@laurac8r
Copy link

@laurac8r laurac8r commented Mar 9, 2026

Open the base temporary directory using os.open with O_NOFOLLOW and O_DIRECTORY flags to prevent symlink attacks. Use the resulting file descriptor for fstat and fchmod operations to eliminate Time-of-Check Time-of-Use (TOCTOU) races.

closes #13669

  • Include documentation when adding new features.
  • Include new tests or update existing tests when applicable.
  • Allow maintainers to push and squash when merging my commits. Please uncheck this if you prefer to squash the commits yourself.

If this change fixes an issue, please:

  • Add text like closes #XYZW to the PR description and/or commits (where XYZW is the issue number). See the github docs for more information.

Important

Unsupervised agentic contributions are not accepted. See our AI/LLM-Assisted Contributions Policy.

  • If AI agents were used, they are credited in Co-authored-by commit trailers.

Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please:

  • Create a new changelog file in the changelog directory, with a name like <ISSUE NUMBER>.<TYPE>.rst. See changelog/README.rst for details.

    Write sentences in the past or present tense, examples:

    • Improved verbose diff output with sequences.
    • Terminal summary statistics now use multiple colors.

    Also make sure to end the sentence with a ..

  • Add yourself to AUTHORS in alphabetical order.

Laura Kaminskiy added 3 commits March 9, 2026 00:49
Open the base temporary directory using `os.open` with `O_NOFOLLOW` and `O_DIRECTORY` flags to prevent symlink attacks.
Use the resulting file descriptor for `fstat` and `fchmod` operations to eliminate Time-of-Check Time-of-Use (TOCTOU) races.

Co-authored-by: Windsurf, Gemini
Co-authored-by: Windsurf, Gemini
@psf-chronographer psf-chronographer bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Mar 9, 2026
@@ -0,0 +1,3 @@
Fixed a symlink attack vulnerability (CVE-2025-71176) in the :fixture:`tmp_path` fixture's base directory handling.

The ``pytest-of-<user>`` directory under the system temp root is now opened with ``O_NOFOLLOW`` and verified using file-descriptor-based ``fstat``/``fchmod``, preventing symlink attacks and TOCTOU races.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind linking this abbrebiation to the definition?


# Figure out what rootdir name pytest would use, then replace it
# with a symlink pointing to the attacker-controlled directory.
import getpass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the import happening inside a function?

) -> None:
"""Cover the branch where basetemp is set but the directory no longer
exists when pytest_sessionfinish runs (314->320 partial branch)."""
from _pytest.tmpdir import pytest_sessionfinish
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misplaced imports are normally discouraged. Any justification?

@@ -0,0 +1,3 @@
Fixed a symlink attack vulnerability (CVE-2025-71176) in the :fixture:`tmp_path` fixture's base directory handling.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a :cve: role that the sphinx-issues extension provides. Plus, feel free to take the credit:

Suggested change
Fixed a symlink attack vulnerability (CVE-2025-71176) in the :fixture:`tmp_path` fixture's base directory handling.
Fixed a symlink attack vulnerability (:cve:`2025-71176`) in the :fixture:`tmp_path` fixture's base directory handling -- by :user:`laurac8r`.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be useful to symlink 14279.bugfix.rst to 13669.bugfix.rst so that both are linked @ https://pytest--14279.org.readthedocs.build/en/14279/changelog.html#bug-fixes (you can also use this link to preview the change note render after pushing updates to this PR).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of this should probably go under a @contextlib.contextmanager for maintainability.

assert (basetemp.parent.stat().st_mode & 0o077) == 0


@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can assign this to a variable and then reuse it in all these tests like so:

skip_if_no_getuid = pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions")
Suggested change
@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions")
@skip_if_no_getuid

tmp_path: Path,
) -> None:
"""Cover the branch where basetemp is set but the directory no longer
exists when pytest_sessionfinish runs (314->320 partial branch)."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These line numbers will become wrong over time.


# exitstatus=0 + policy="failed" + _given_basetemp=None enters the
# cleanup block; basetemp.is_dir() is False so rmtree is skipped.
pytest_sessionfinish(FakeSession, exitstatus=0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this should be isolated, no?

@orlitzky
Copy link

orlitzky commented Mar 9, 2026

This certainly looks like an improvement, but it's still possible for any user to DoS the machine until a reboot with for x in $(cat /etc/passwd | cut -f1 -d:); do touch /tmp/pytest-of-$x; done;.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vulnerable tmpdir handling (CVE-2025-71176)

3 participants