GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-144475: Fix use-after-free in functools.partial.__repr__() by Nicolas0315 · Pull Request #145395 · python/cpython

Conversation

@Nicolas0315

@Nicolas0315 Nicolas0315 commented Mar 1, 2026 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Summary

Fix a heap-buffer-overflow (use-after-free) in functools.partial.__repr__() where a user-defined __repr__() on an argument could mutate the partial object via __setstate__(), freeing the args tuple while partial_repr() was still iterating over it.

Root Cause

partial_repr() captured the size of pto->args before the loop and accessed tuple items via borrowed references (PyTuple_GET_ITEM). If a __repr__() called during %R formatting invoked pto.__setstate__() with a new (smaller) args tuple, the original tuple was freed while iteration continued, causing an out-of-bounds read.

Fix

Hold strong references (Py_NewRef) to pto->args, pto->kw, and pto->fn before iterating/using them. This ensures the underlying objects remain alive even if user code mutates the partial via __setstate__() during formatting.

The same pattern is applied to:

  • pto->args: The positional arguments tuple iterated in the loop
  • pto->kw: The keyword arguments dict iterated via PyDict_Next
  • pto->fn: The callable whose __repr__ is invoked via %R

Reproducer (from the issue)

import gc
from functools import partial

g_partial = None

class EvilObject:
    def __init__(self, name, is_trigger=False):
        self.name = name
        self.is_trigger = is_trigger
        self.triggered = False

    def __repr__(self):
        global g_partial
        if self.is_trigger and not self.triggered and g_partial is not None:
            self.triggered = True
            new_state = (lambda x: x, ("replaced",), {}, None)
            g_partial.__setstate__(new_state)
            gc.collect()
        return f"EvilObject({self.name})"

evil1 = EvilObject("trigger", is_trigger=True)
evil2 = EvilObject("victim1")
evil3 = EvilObject("victim2")

p = partial(lambda: None, evil1, evil2, evil3)
g_partial = p
del evil1, evil2, evil3

repr(p)  # heap-buffer-overflow without fix

Fixes #144475.

  • Issue: heap-buffer-overflow in functools.partial.__repr__() #144475

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@Nicolas0315
gh-144475: Fix use-after-free in functools.partial.__repr__()
a445c16
Hold strong references to pto->args, pto->kw, and pto->fn during
partial_repr() to prevent them from being freed by a user-defined
__repr__() that mutates the partial object via __setstate__().

Previously, partial_repr() iterated over pto->args using a size 'n'
captured before the loop, and accessed tuple items via borrowed
references. If a __repr__() called during formatting invoked
pto.__setstate__() with a new (smaller) args tuple, the original
tuple could be freed while the loop was still iterating, leading to
a heap-buffer-overflow (out-of-bounds read).

The fix takes a new reference (Py_NewRef) to the args tuple, kw dict,
and fn callable before using them, ensuring they stay alive regardless
of any mutations to the partial object during formatting.
@Nicolas0315
Nicolas0315 requested a review from rhettinger as a code owner March 1, 2026 17:27
@python-cla-bot

python-cla-bot Bot commented Mar 1, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app

bedevere-app Bot commented Mar 1, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot mentioned this pull request Mar 1, 2026
heap-buffer-overflow in functools.partial.__repr__() #144475
Closed
@bedevere-app bedevere-app Bot added the awaiting review label Mar 1, 2026
Nicolas0315 added 2 commits March 2, 2026 03:31
@Nicolas0315
📜🤖 Added by blurb_it.
7e73c5d
gh-144475: Add NEWS entry for functools.partial.__repr__ fix
@Nicolas0315
Fix NEWS entry Sphinx references
6c18a9e
Use inline code markup instead of :func: and :meth: roles for
partial.__repr__ and __setstate__ to avoid Sphinx reference
resolution failures in the docs CI.
@aisk

aisk commented Mar 2, 2026

Copy link
Copy Markdown
Member

There is already an active PR #145362 for this issue, so please don’t open another PR for the same fix.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou encukou closed this Mar 3, 2026
@encukou

encukou commented Mar 3, 2026

Copy link
Copy Markdown
Member

Also, if this is LLM-generated, please review https://devguide.python.org/getting-started/generative-ai/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

@rhettinger rhettinger Awaiting requested review from rhettinger rhettinger is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

heap-buffer-overflow in functools.partial.__repr__()

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@Nicolas0315 @aisk @encukou
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.