Visitar URL original
lang/python313: fix CVE-2026-12345 (tempfile.TemporaryDirectory race) by laffer1 · Pull Request #1654 · MidnightBSD/mports · GitHub
Skip to content

lang/python313: fix CVE-2026-12345 (tempfile.TemporaryDirectory race) - #1654

Merged
laffer1 merged 1 commit into
masterfrom
lang/python313
Sep 30, 2026
Merged

laffer1 merged 1 commit into
masterfrom
lang/python313

Conversation

@laffer1

@laffer1 laffer1 commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Backports the Lib/shutil.py and Lib/tempfile.py changes for CVE-2026-12345 from python/cpython#158429, the 3.13 backport of python/cpython#157580. PORTREVISION is set to 1. lang/python313t shares the same MASTERDIR, so it gets the fix too (python313t-3.13.11_1).

One tempfile.py hunk was adjusted by hand. The 3.13 branch includes an unrelated DragonFly BSD change (IsADirectoryError) that 3.13.11 doesn't have, and I left it out. The resulting added and removed lines match the upstream backport exactly.

Note: the upstream 3.13 backport PR was still open when this was written. If it changes before merging, this patch should be refreshed, or dropped once 3.13.12 ships.

The upstream test changes are not included, because they rely on test.support.requires_non_root_user, which exists only on main.

Testing (MidnightBSD 4.0.6 amd64):

  • bmake patch build fake package succeeds (python313-3.13.11_1)
  • The upstream test_tempfile and test_shutil changes were applied locally only, with an equivalent skipIf. Run as nobody, both pass (329 run, 70 skipped).
  • The new test_cleanup_with_symlink_race fails against unpatched 3.13.11 and passes with this patch
  • python313t was not built separately

🤖 Generated with Claude Code

Summary by Sourcery

Backport the Python 3.13 tempfile cleanup security fix and publish it as a revised port.

Bug Fixes:

  • Fix a race condition in TemporaryDirectory cleanup that could be exploited through symlink replacement during recursive deletion.

Enhancements:

  • Harden shutil.rmtree and tempfile cleanup to operate relative to directory file descriptors where supported, while preserving existing cleanup behavior and compatibility.

Build:

  • Increment the lang/python313 port revision so the security fix is packaged for python313 and the shared python313t variant.

Backport the Lib/shutil.py and Lib/tempfile.py changes from
python/cpython#158429 (3.13 backport of gh-157580). TemporaryDirectory
cleanup could be redirected through a symlink swapped in during
removal, deleting files or resetting permissions/flags outside the
temporary directory. Also covers lang/python313t (same MASTERDIR).

One tempfile.py hunk was adjusted by hand: the 3.13 branch has an
unrelated DragonFly BSD IsADirectoryError change that 3.13.11 lacks;
that change is not included. The upstream test changes are not
included: they depend on test.support.requires_non_root_user, which is
only on main.

AI-Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Holt <luke@foolishgames.com>
@sourcery-ai

sourcery-ai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Backports the CVE-2026-12345 fix by making TemporaryDirectory cleanup resistant to symlink races through descriptor-relative traversal and cleanup, while preserving existing shutil.rmtree callback compatibility; PORTREVISION 1 publishes the fix for both Python package variants.

Sequence diagram for race-resistant TemporaryDirectory cleanup

sequenceDiagram
    participant TD as TemporaryDirectory
    participant SH as shutil.rmtree
    participant FS as Filesystem

    TD->>SH: _rmtree(name, dir_fd=dir_fd)
    SH->>FS: Open and traverse directory entries
    FS-->>SH: direntry and parent dir_fd
    SH->>TD: onexc(func, path, exc, direntry, dir_fd)
    TD->>FS: _resetperms_at(base, dir_fd, fullpath)
    TD->>FS: os.unlink(base, dir_fd=dir_fd)
    alt Entry is a directory
        FS-->>TD: IsADirectoryError
        TD->>TD: _rmtree(base, dir_fd=dir_fd, fullname=fullpath)
    else Entry is removed
        FS-->>TD: Success
    end
Loading

File-Level Changes

Change Details Files
Backport the symlink-race-resistant directory removal implementation used by TemporaryDirectory cleanup.
  • Extend shutil.rmtree’s internal error callback path with directory-entry and directory-file-descriptor context.
  • Add an internal compatibility switch so existing public onexc callbacks retain their three-argument behavior.
  • Implement descriptor-relative permission resets and unlink/recurse operations, with platform fallbacks for symlink-safe chmod.
  • Update TemporaryDirectory cleanup recursion to use the descriptor-relative rmtree path and preserve full paths for diagnostics and platform checks.
lang/python313/files/patch-Lib/shutil.py
lang/python313/files/patch-Lib_tempfile.py
Bump the package revision so the security fix is included in the 3.13 package and its threaded variant.
  • Set PORTREVISION to 1.
  • Rely on the shared MASTERDIR so python313t receives the same patch.
lang/python313/Makefile

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Claude finished @laffer1's task in 32s —— View job


Review of PR #1654 (lang/python313 CVE backport)

Verdict: looks good. Approve-worthy, with a few non-blocking notes. I could not build or run tests here, so this is a read-through only.

What I checked

  • PORTREVISION= 1 is placed correctly, after DISTVERSION.
  • python313t sets MASTERDIR to ../python313 and includes its Makefile. It picks up both the patches and the bump.
  • Makefile.version has PYTHON_DISTVERSION=3.13.11, which matches the PR description.
  • Patch names follow the patch-Lib_shutil.py and patch-Lib_tempfile.py convention.
  • The hunk line offsets are consistent in both patches. In tempfile.py they run +1, then +54, then +73, and the hunk headers match. The shutil.py hunks also chain correctly.
  • The shutil.py logic holds together. The internal _onexc_kwargs=True call passes direntry and dir_fd. A user-supplied onexc is wrapped so it still gets the 3-argument form. The onerror delegate and the ignore_errors handlers accept **kwargs. _rmtree_unsafe still calls onexc with 3 positional arguments, which the new tempfile handler accepts because its extras default to None.

Notes (non-blocking)

  1. Unverifiable CVE ID. I can't confirm CVE-2026-12345 or the cited upstream PRs ([3.13] gh-157579: Fix race condition in the cleanup of tempfile.TemporaryDirectory (GH-157580) python/cpython#158429 and #157580) from here. Please confirm them, and keep the reference in the commit message.
  2. Public signature change. shutil.rmtree gains a _onexc_kwargs keyword argument, which is visible in inspect.signature. This matches the upstream design as described, so it is fine. It is one more reason to drop the patch when 3.13.12 ships.
  3. Reminder in Makefile.version. Its comment says to remove PORTREVISION on a version bump. Add a note or a follow-up so the patches are also removed at 3.13.12. This is especially worth doing because the upstream backport was still open and may change.
  4. Untested platforms. python313t was not built separately. It shares the sources and patches, so the risk is low. A bmake patch run there would confirm that both patches apply cleanly.
  5. onexc fallback paths. The _resetperms_at fallbacks (O_NOFOLLOW open, then lstat+chmod by name) are only exercised on platforms without chmod(dir_fd=, follow_symlinks=False). Check whether MidnightBSD takes the first branch. It probably does, because it is FreeBSD-13-based.

There are no license, plist, security or architecture-compatibility concerns. The change touches no arch-specific code.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@@ -0,0 +1,71 @@
--- Lib/shutil.py.orig 2026-09-29 20:52:56 UTC
@@ -0,0 +1,162 @@
--- Lib/tempfile.py.orig 2026-09-29 20:52:57 UTC
@laffer1
laffer1 merged commit 11e803d into master Sep 30, 2026
9 of 10 checks passed
@laffer1
laffer1 deleted the lang/python313 branch September 30, 2026 01:29
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.

2 participants