Skip to content

Conversation

@gaborbernat
Copy link
Member

@gaborbernat gaborbernat commented Dec 15, 2025

A race condition existed between checking if the lock file exists and opening it with O_TRUNC, allowing local attackers to create a symlink pointing to victim files. When the lock was acquired, os.open() would follow the symlink and truncate the target file, causing data loss or corruption.

The vulnerability affected both Unix and Windows platforms and cascaded through dependent libraries:

  • virtualenv: Could overwrite user configs with virtualenv metadata, leaking file contents
  • PyTorch: Could truncate CPU ISA cache causing crashes, or corrupt compiled model checkpoints preventing
    model loading (DoS for ML pipelines)

Unix/Linux/macOS fix:

  • Add O_NOFOLLOW flag to os.open() call in UnixFileLock._acquire()
  • System returns ELOOP error if lock path is a symlink, preventing the attack

Windows fix:

  • Use GetFileAttributesW API via ctypes to detect reparse points (symlinks/junctions)
  • Refuse to open lock file if FILE_ATTRIBUTE_REPARSE_POINT flag is set
  • Raises OSError before attempting to open, closing the race window

This addresses CWE-362 (Race Condition), CWE-367 (TOCTOU), and CWE-59 (Link Following).

Reported-by: @tsigouris007

@gaborbernat gaborbernat changed the title Fix TOCTOU symlink vulnerability in lock file creation CVE-2025-68146: Fix TOCTOU symlink vulnerability in lock file creation Dec 15, 2025
A race condition existed between checking if the lock file exists and opening it with O_TRUNC, allowing
local attackers to create a symlink pointing to victim files. When the lock was acquired, os.open() would
follow the symlink and truncate the target file, causing data loss or corruption.

The vulnerability affected both Unix and Windows platforms and cascaded through dependent libraries:
- virtualenv: Could overwrite user configs with virtualenv metadata, leaking file contents
- PyTorch: Could truncate CPU ISA cache causing crashes, or corrupt compiled model checkpoints preventing
  model loading (DoS for ML pipelines)

Unix/Linux/macOS fix:
- Add O_NOFOLLOW flag to os.open() call in UnixFileLock._acquire()
- System returns ELOOP error if lock path is a symlink, preventing the attack

Windows fix:
- Use GetFileAttributesW API via ctypes to detect reparse points (symlinks/junctions)
- Refuse to open lock file if FILE_ATTRIBUTE_REPARSE_POINT flag is set
- Raises OSError before attempting to open, closing the race window

This addresses CWE-362 (Race Condition), CWE-367 (TOCTOU), and CWE-59 (Link Following).

Reported-by: @tsigouris007
Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
@gaborbernat gaborbernat merged commit 4724d7f into tox-dev:main Dec 15, 2025
30 checks passed
ansibuddy pushed a commit to ansible/molecule that referenced this pull request Dec 22, 2025
This PR contains the following updates:

| Package | Type | Update | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.14.9` -> `v0.14.10` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/astral-sh%2fruff-pre-commit/v0.14.10?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/astral-sh%2fruff-pre-commit/v0.14.9/v0.14.10?slim=true)
|
|
[astral-sh/uv-pre-commit](https://redirect.github.com/astral-sh/uv-pre-commit)
| repository | patch | `0.9.17` -> `0.9.18` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/astral-sh%2fuv-pre-commit/0.9.18?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/astral-sh%2fuv-pre-commit/0.9.17/0.9.18?slim=true)
|
| [biomejs/pre-commit](https://redirect.github.com/biomejs/pre-commit) |
repository | patch | `v2.3.8` -> `v2.3.10` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/biomejs%2fpre-commit/v2.3.10?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/biomejs%2fpre-commit/v2.3.8/v2.3.10?slim=true)
|
| [filelock](https://redirect.github.com/tox-dev/py-filelock) |
dependency-groups | patch | `3.20.0` -> `3.20.1` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/filelock/3.20.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/filelock/3.20.0/3.20.1?slim=true)
|
| [mypy](https://redirect.github.com/python/mypy)
([changelog](https://mypy.readthedocs.io/en/latest/changelog.html)) |
dependency-groups | patch | `1.19.0` -> `1.19.1` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/mypy/1.19.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mypy/1.19.0/1.19.1?slim=true)
|
| [pre-commit](https://redirect.github.com/pre-commit/pre-commit) |
dependency-groups | patch | `4.5.0` -> `4.5.1` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/pre-commit/4.5.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pre-commit/4.5.0/4.5.1?slim=true)
|
|
[pre-commit/mirrors-mypy](https://redirect.github.com/pre-commit/mirrors-mypy)
| repository | patch | `v1.19.0` -> `v1.19.1` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/pre-commit%2fmirrors-mypy/v1.19.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/pre-commit%2fmirrors-mypy/v1.19.0/v1.19.1?slim=true)
|
| [ruff](https://docs.astral.sh/ruff)
([source](https://redirect.github.com/astral-sh/ruff),
[changelog](https://redirect.github.com/astral-sh/ruff/blob/main/CHANGELOG.md))
| dependency-groups | patch | `0.14.9` -> `0.14.10` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.14.10?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.14.9/0.14.10?slim=true)
|
|
[tombi-toml/tombi-pre-commit](https://redirect.github.com/tombi-toml/tombi-pre-commit)
| repository | patch | `v0.7.7` -> `v0.7.8` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/tombi-toml%2ftombi-pre-commit/v0.7.8?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/tombi-toml%2ftombi-pre-commit/v0.7.7/v0.7.8?slim=true)
|
|  |  | lockFileMaintenance | All locks refreshed |  |  |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit)</summary>

###
[`v0.14.10`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.14.10)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.14.9...v0.14.10)

See: <https://github.com/astral-sh/ruff/releases/tag/0.14.10>

</details>

<details>
<summary>astral-sh/uv-pre-commit (astral-sh/uv-pre-commit)</summary>

###
[`v0.9.18`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.9.18)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.9.17...0.9.18)

See: <https://github.com/astral-sh/uv/releases/tag/0.9.18>

</details>

<details>
<summary>biomejs/pre-commit (biomejs/pre-commit)</summary>

###
[`v2.3.10`](https://redirect.github.com/biomejs/pre-commit/compare/v2.3.9...v2.3.10)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.3.9...v2.3.10)

###
[`v2.3.9`](https://redirect.github.com/biomejs/pre-commit/compare/v2.3.8...v2.3.9)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.3.8...v2.3.9)

</details>

<details>
<summary>tox-dev/py-filelock (filelock)</summary>

###
[`v3.20.1`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.20.1)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.20.0...3.20.1)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

- CVE-2025-68146: Fix TOCTOU symlink vulnerability in lock file creation
by [@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#461](https://redirect.github.com/tox-dev/filelock/pull/461)

**Full Changelog**:
<tox-dev/filelock@3.20.0...3.20.1>

</details>

<details>
<summary>python/mypy (mypy)</summary>

###
[`v1.19.1`](https://redirect.github.com/python/mypy/blob/HEAD/CHANGELOG.md#Mypy-1191)

[Compare
Source](https://redirect.github.com/python/mypy/compare/v1.19.0...v1.19.1)

- Fix noncommutative joins with bounded TypeVars (Shantanu, PR
[20345](https://redirect.github.com/python/mypy/pull/20345))
- Respect output format for cached runs by serializing raw errors in
cache metas (Ivan Levkivskyi, PR
[20372](https://redirect.github.com/python/mypy/pull/20372))
- Allow `types.NoneType` in match cases (A5rocks, PR
[20383](https://redirect.github.com/python/mypy/pull/20383))
- Fix mypyc generator regression with empty tuple (BobTheBuidler, PR
[20371](https://redirect.github.com/python/mypy/pull/20371))
- Fix crash involving Unpack-ed TypeVarTuple (Shantanu, PR
[20323](https://redirect.github.com/python/mypy/pull/20323))
- Fix crash on star import of redefinition (Ivan Levkivskyi, PR
[20333](https://redirect.github.com/python/mypy/pull/20333))
- Fix crash on typevar with forward ref used in other module (Ivan
Levkivskyi, PR
[20334](https://redirect.github.com/python/mypy/pull/20334))
- Fail with an explicit error on PyPy (Ivan Levkivskyi, PR
[20389](https://redirect.github.com/python/mypy/pull/20389))

</details>

<details>
<summary>pre-commit/pre-commit (pre-commit)</summary>

###
[`v4.5.1`](https://redirect.github.com/pre-commit/pre-commit/blob/HEAD/CHANGELOG.md#451---2025-12-16)

[Compare
Source](https://redirect.github.com/pre-commit/pre-commit/compare/v4.5.0...v4.5.1)

\==================

##### Fixes

- Fix `language: python` with `repo: local` without
`additional_dependencies`.
-
[#&#8203;3597](https://redirect.github.com/pre-commit/pre-commit/issues/3597)
PR by [@&#8203;asottile](https://redirect.github.com/asottile).

</details>

<details>
<summary>pre-commit/mirrors-mypy (pre-commit/mirrors-mypy)</summary>

###
[`v1.19.1`](https://redirect.github.com/pre-commit/mirrors-mypy/compare/v1.19.0...v1.19.1)

[Compare
Source](https://redirect.github.com/pre-commit/mirrors-mypy/compare/v1.19.0...v1.19.1)

</details>

<details>
<summary>astral-sh/ruff (ruff)</summary>

###
[`v0.14.10`](https://redirect.github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#01410)

[Compare
Source](https://redirect.github.com/astral-sh/ruff/compare/0.14.9...0.14.10)

Released on 2025-12-18.

##### Preview features

- \[formatter] Fluent formatting of method chains
([#&#8203;21369](https://redirect.github.com/astral-sh/ruff/pull/21369))
- \[formatter] Keep lambda parameters on one line and parenthesize the
body if it expands
([#&#8203;21385](https://redirect.github.com/astral-sh/ruff/pull/21385))
- \[`flake8-implicit-str-concat`] New rule to prevent implicit string
concatenation in collections (`ISC004`)
([#&#8203;21972](https://redirect.github.com/astral-sh/ruff/pull/21972))
- \[`flake8-use-pathlib`] Make fixes unsafe when types change in
compound statements (`PTH104`, `PTH105`, `PTH109`, `PTH115`)
([#&#8203;22009](https://redirect.github.com/astral-sh/ruff/pull/22009))
- \[`refurb`] Extend support for `Path.open` (`FURB101`, `FURB103`)
([#&#8203;21080](https://redirect.github.com/astral-sh/ruff/pull/21080))

##### Bug fixes

- \[`pyupgrade`] Fix parsing named Unicode escape sequences (`UP032`)
([#&#8203;21901](https://redirect.github.com/astral-sh/ruff/pull/21901))

##### Rule changes

- \[`eradicate`] Ignore `ruff:disable` and `ruff:enable` comments in
`ERA001`
([#&#8203;22038](https://redirect.github.com/astral-sh/ruff/pull/22038))
- \[`flake8-pytest-style`] Allow `match` and `check` keyword arguments
without an expected exception type (`PT010`)
([#&#8203;21964](https://redirect.github.com/astral-sh/ruff/pull/21964))
- \[syntax-errors] Annotated name cannot be global
([#&#8203;20868](https://redirect.github.com/astral-sh/ruff/pull/20868))

##### Documentation

- Add `uv` and `ty` to the Ruff README
([#&#8203;21996](https://redirect.github.com/astral-sh/ruff/pull/21996))
- Document known lambda formatting deviations from Black
([#&#8203;21954](https://redirect.github.com/astral-sh/ruff/pull/21954))
- Update `setup.md`
([#&#8203;22024](https://redirect.github.com/astral-sh/ruff/pull/22024))
- \[`flake8-bandit`] Fix broken link (`S704`)
([#&#8203;22039](https://redirect.github.com/astral-sh/ruff/pull/22039))

##### Other changes

- Fix playground Share button showing "Copied!" before clipboard copy
completes
([#&#8203;21942](https://redirect.github.com/astral-sh/ruff/pull/21942))

##### Contributors

- [@&#8203;dylwil3](https://redirect.github.com/dylwil3)
-
[@&#8203;charliecloudberry](https://redirect.github.com/charliecloudberry)
- [@&#8203;charliermarsh](https://redirect.github.com/charliermarsh)
- [@&#8203;chirizxc](https://redirect.github.com/chirizxc)
- [@&#8203;ntBre](https://redirect.github.com/ntBre)
- [@&#8203;zanieb](https://redirect.github.com/zanieb)
- [@&#8203;amyreese](https://redirect.github.com/amyreese)
- [@&#8203;hauntsaninja](https://redirect.github.com/hauntsaninja)
- [@&#8203;11happy](https://redirect.github.com/11happy)
- [@&#8203;mahiro72](https://redirect.github.com/mahiro72)
- [@&#8203;MichaReiser](https://redirect.github.com/MichaReiser)
- [@&#8203;phongddo](https://redirect.github.com/phongddo)
- [@&#8203;PeterJCLaw](https://redirect.github.com/PeterJCLaw)

</details>

<details>
<summary>tombi-toml/tombi-pre-commit
(tombi-toml/tombi-pre-commit)</summary>

###
[`v0.7.8`](https://redirect.github.com/tombi-toml/tombi-pre-commit/releases/tag/v0.7.8)

[Compare
Source](https://redirect.github.com/tombi-toml/tombi-pre-commit/compare/v0.7.7...v0.7.8)

See: <https://github.com/tombi-toml/tombi/releases/tag/v0.7.8>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on monday" in timezone
UTC, Automerge - Between 12:00 AM and 03:59 AM, only on Monday ( * 0-3 *
* 1 ) in timezone UTC.

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ansible/molecule).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi41OS4wIiwidXBkYXRlZEluVmVyIjoiNDIuNTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiY2hvcmUiLCJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant