Skip to content

[3.12] gh-157579: Fix race condition in the cleanup of tempfile.TemporaryDirectory (GH-157580) (GH-158430) - #159074

Merged
Yhg1s merged 2 commits into
python:3.12from
encukou:backport-58ed025-3.12
Oct 25, 2026
Merged

Yhg1s merged 2 commits into
python:3.12from
encukou:backport-58ed025-3.12

Conversation

@encukou

@encukou encukou commented Oct 9, 2026

Copy link
Copy Markdown
Member

(cherry picked from commit 5c20517)

  • Add root user checks to test.support

This partially backports commit 86b8617 (GH-146195); making existing tests use the helper is omitted.

(cherry picked from commit 86b8617)

(cherry picked from commit e004cf8)

miss-islington and others added 2 commits October 9, 2026 17:30
…e.TemporaryDirectory` (pythonGH-157580) (pythonGH-158430)

* pythongh-157579: Fix race condition in the cleanup of `tempfile.TemporaryDirectory` (pythonGH-157580)

(cherry picked from commit 5c20517)

* Add root user checks to test.support

This partially backports commit 86b8617
(pythonGH-146195); making existing tests use the helper is omitted.

---------
(cherry picked from commit 58ed025)

Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Co-authored-by: Stan Ulbrych <stan@python.org>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
@Yhg1s
Yhg1s merged commit c39b52d into python:3.12 Oct 25, 2026
30 checks passed
@encukou
encukou deleted the backport-58ed025-3.12 branch October 27, 2026 09:41
icanhasmath added a commit to ActiveState/cpython that referenced this pull request Oct 28, 2026
….TemporaryDirectory (CVE-2026-12345)

When TemporaryDirectory cleanup recovers from a PermissionError it resets
permissions and retries removal. It did that by resolving paths again,
so an attacker who could modify the tree during cleanup could replace a
directory with a symlink and make cleanup delete files, or reset their
permissions and flags, outside of the temporary directory. Files are now
handled relative to the open directory file descriptors that the
fd-based shutil.rmtree() walk already holds.

Port of the upstream fix (pythonGH-157580, pythonGH-158430), based on the 3.12
backport (python#159074), plus pythongh-134993 (os.lstat in os.supports_dir_fd).

3.10.22.x: the 3.12 fix relies on 3.12's shutil.rmtree() API
(onexc, a public dir_fd parameter, and the _onexc_kwargs switch that
passes direntry/dir_fd to the callback). 3.10 only has onerror and a
recursive _rmtree_safe_fd(), so this adds the private keyword arguments
_dir_fd and _onerror_kwargs to rmtree() instead and threads the parent
directory fd and os.DirEntry through _rmtree_safe_fd(). With
_onerror_kwargs left false, existing onerror callbacks get the same
three positional arguments as before. TemporaryDirectory._rmtree() is
ported onto the 3.11 shape of the code (bpo-43153 and pythongh-79325 are
picked first), using os.lstat(base, dir_fd=...) where 3.12 uses
os.path.isdir()/isjunction() on the full path. Tests are adapted:
onerror instead of onexc, local root and platform skips instead of
support.requires_non_root_user and PY_SUPPORT_TIER.

(cherry picked from commit c39b52d)
(cherry picked from commit 5c20517)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
icanhasmath added a commit to ActiveState/cpython that referenced this pull request Oct 28, 2026
…TemporaryDirectory (CVE-2026-12345)

When TemporaryDirectory cleanup recovers from a PermissionError it resets
permissions and retries removal. It did that by resolving paths again,
so an attacker who could modify the tree during cleanup could replace a
directory with a symlink and make cleanup delete files, or reset their
permissions and flags, outside of the temporary directory. Files are now
handled relative to the open directory file descriptors that the
fd-based shutil.rmtree() walk already holds.

Port of the upstream fix (pythonGH-157580, pythonGH-158430), based on the 3.12
backport (python#159074), plus pythongh-134993 (os.lstat in os.supports_dir_fd).

3.10.22.x: the 3.12 fix relies on 3.12's shutil.rmtree() API
(onexc, a public dir_fd parameter, and the _onexc_kwargs switch that
passes direntry/dir_fd to the callback). 3.10 only has onerror and a
recursive _rmtree_safe_fd(), so this adds the private keyword arguments
_dir_fd and _onerror_kwargs to rmtree() instead and threads the parent
directory fd and os.DirEntry through _rmtree_safe_fd(). With
_onerror_kwargs left false, existing onerror callbacks get the same
three positional arguments as before. TemporaryDirectory._rmtree() is
ported onto the 3.11 shape of the code (bpo-43153 and pythongh-79325 are
picked first), using os.lstat(base, dir_fd=...) where 3.12 uses
os.path.isdir()/isjunction() on the full path. Tests are adapted:
onerror instead of onexc, local root and platform skips instead of
support.requires_non_root_user and PY_SUPPORT_TIER.

(cherry picked from commit c39b52d)
(cherry picked from commit 5c20517)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 776b466)

3.9.25.x: tests use test.support instead of test.support.os_helper, and
test_parent_mode_preserved only covers the default (no ignore_errors)
case because 3.9's TemporaryDirectory has no ignore_cleanup_errors.
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.

5 participants