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 — heap-buffer-overflow in functools.partial.__repr__() · Issue #144475 · python/cpython

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

New issue
Copy link
New issue
Copy link
Closed
Closed
heap-buffer-overflow in functools.partial.__repr__()#144475
Copy link
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@Qanux
Qanux
opened on Feb 4, 2026
Issue body actions

Bug report

Bug description:

A heap-buffer-overflow vulnerability exists in CPython's functools.partial.__repr__() method due to improper handling of borrowed references during iteration over pto->args.

Running with a Python interpreter compiled with ASAN (./configure --with-address-sanitizer):

Details
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 
            # Replace args via __setstate__, this frees the old tuple 
            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") 
evil4 = EvilObject("victim3") 
evil5 = EvilObject("victim4") 

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

del evil1, evil2, evil3, evil4, evil5 

repr(p) 

Asan output:

Details
=================================================================
==430879==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5060000d4878 at pc 0x5c4743c6d881 bp 0x7ffc1076d330 sp 0x7ffc1076d320
READ of size 8 at 0x5060000d4878 thread T0
    #0 0x5c4743c6d880 in partial_repr Modules/_functoolsmodule.c:712
    #1 0x5c47436f8158 in PyObject_Repr Objects/object.c:779
    #2 0x5c47436f8158 in PyObject_Repr Objects/object.c:754
    #3 0x5c47435da5ef in _PyObject_VectorcallTstate Include/internal/pycore_call.h:136
    #4 0x5c47435da5ef in PyObject_Vectorcall Objects/call.c:327
    #5 0x5c474394258e in _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:762
    #6 0x5c4743498fc3 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1809
    #7 0x5c474394f069 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:118
    #8 0x5c474394f069 in _PyEval_Vector Python/ceval.c:2097
    #9 0x5c474394f069 in PyEval_EvalCode Python/ceval.c:673
    #10 0x5c4743a8f6ed in run_eval_code_obj Python/pythonrun.c:1366
    #11 0x5c4743a8f6ed in run_mod Python/pythonrun.c:1469
    #12 0x5c4743a9456e in pyrun_file Python/pythonrun.c:1294
    #13 0x5c4743a9456e in _PyRun_SimpleFileObject Python/pythonrun.c:518
    #14 0x5c4743a94fbb in _PyRun_AnyFileObject Python/pythonrun.c:81
    #15 0x5c4743b0a0e9 in pymain_run_file_obj Modules/main.c:410
    #16 0x5c4743b0a0e9 in pymain_run_file Modules/main.c:429
    #17 0x5c4743b0a0e9 in pymain_run_python Modules/main.c:691
    #18 0x5c4743b0b7de in Py_RunMain Modules/main.c:772
    #19 0x5c4743b0b7de in pymain_main Modules/main.c:802
    #20 0x5c4743b0b7de in Py_BytesMain Modules/main.c:826
    #21 0x7005d3e29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #22 0x7005d3e29e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #23 0x5c47434b8d34 in _start (/home/or4nge/cpython/python+0x20cd34)

0x5060000d4878 is located 0 bytes to the right of 56-byte region [0x5060000d4840,0x5060000d4878)
allocated by thread T0 here:
    #0 0x7005d42b4887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x5c47439e5079 in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46
    #2 0x5c47439e5079 in gc_alloc Python/gc.c:2352
    #3 0x5c47439e5079 in _PyObject_GC_NewVar Python/gc.c:2394
    #4 0x5c4743762d6a in tuple_alloc Objects/tupleobject.c:57
    #5 0x5c47437657b7 in PyTuple_New Objects/tupleobject.c:81
    #6 0x5c47437657b7 in PyTuple_New Objects/tupleobject.c:75
    #7 0x5c47439cad7f in fold_tuple_of_constants Python/flowgraph.c:1470
    #8 0x5c47439cad7f in optimize_basic_block Python/flowgraph.c:2337
    #9 0x5c47439cf342 in optimize_cfg Python/flowgraph.c:2546
    #10 0x5c47439cf342 in _PyCfg_OptimizeCodeUnit Python/flowgraph.c:3658
    #11 0x5c4743992c7a in optimize_and_assemble_code_unit Python/compile.c:1439
    #12 0x5c4743992c7a in _PyCompile_OptimizeAndAssemble Python/compile.c:1481
    #13 0x5c4743988122 in codegen_function_body Python/codegen.c:1411
    #14 0x5c4743988122 in codegen_function Python/codegen.c:1503
    #15 0x5c4743979224 in codegen_visit_stmt Python/codegen.c:3029
    #16 0x5c474397fa1a in codegen_body Python/codegen.c:929
    #17 0x5c474398142f in codegen_class_body Python/codegen.c:1592
    #18 0x5c474398142f in codegen_class Python/codegen.c:1685
    #19 0x5c474397b86a in codegen_visit_stmt Python/codegen.c:3031
    #20 0x5c474397fa1a in codegen_body Python/codegen.c:929
    #21 0x5c474398ade2 in _PyCodegen_Module Python/codegen.c:892
    #22 0x5c474398d148 in compiler_codegen Python/compile.c:841
    #23 0x5c47439930f6 in compiler_mod Python/compile.c:862
    #24 0x5c47439930f6 in _PyAST_Compile Python/compile.c:1494
    #25 0x5c4743a8f4d0 in run_mod Python/pythonrun.c:1419
    #26 0x5c4743a9456e in pyrun_file Python/pythonrun.c:1294
    #27 0x5c4743a9456e in _PyRun_SimpleFileObject Python/pythonrun.c:518
    #28 0x5c4743a94fbb in _PyRun_AnyFileObject Python/pythonrun.c:81
    #29 0x5c4743b0a0e9 in pymain_run_file_obj Modules/main.c:410
    #30 0x5c4743b0a0e9 in pymain_run_file Modules/main.c:429
    #31 0x5c4743b0a0e9 in pymain_run_python Modules/main.c:691
    #32 0x5c4743b0b7de in Py_RunMain Modules/main.c:772
    #33 0x5c4743b0b7de in pymain_main Modules/main.c:802
    #34 0x5c4743b0b7de in Py_BytesMain Modules/main.c:826
    #35 0x7005d3e29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-buffer-overflow Modules/_functoolsmodule.c:712 in partial_repr
Shadow bytes around the buggy address:
  0x0a0c800128b0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
  0x0a0c800128c0: 00 00 00 00 00 00 00 00 fa fa fa fa fd fd fd fd
  0x0a0c800128d0: fd fd fd fd fa fa fa fa 00 00 00 00 00 00 00 00
  0x0a0c800128e0: fa fa fa fa 00 00 00 00 00 00 00 00 fa fa fa fa
  0x0a0c800128f0: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 00
=>0x0a0c80012900: 00 00 00 00 fa fa fa fa 00 00 00 00 00 00 00[fa]
  0x0a0c80012910: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
  0x0a0c80012920: 00 00 00 00 00 00 00 00 fa fa fa fa fd fd fd fd
  0x0a0c80012930: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
  0x0a0c80012940: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
  0x0a0c80012950: 00 00 00 00 00 00 00 00 fa fa fa fa 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==430879==ABORTING

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

  • gh-144475: Fix a heap buffer overflow in partial_repr #144571
  • gh-144475: Fix a heap buffer overflow in partial_repr #145362
  • gh-144475: Fix use-after-free in functools.partial.__repr__() #145395
  • [3.14] gh-144475: Fix reference management in partial_repr (GH-145362) #145470
  • [3.13] gh-144475: Fix reference management in partial_repr (GH-145362) #145882

Activity

  1. Qanux
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Feb 4, 2026
  2. sergey-miryanov
    added
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    on Feb 4, 2026
  3. dr-carlos commented on Feb 4, 2026

    @dr-carlos
    dr-carlos
    on Feb 4, 2026
    Contributor
    More actions

    This seems to be solveable (thought I might have missed something!) by:

    1. Putting pto->args into a variable so it doesn't change every loop iteration if a malicious argument modifies the pto->args reference.
    2. And taking a strong reference to pto->args for that new variable.

    If both of those are implemented, repr(p) returns the repr before __setstate__ takes effect:

    functools.partial(<function EvilObject.__repr__.<locals>.<lambda> at 0x7c96c8232a30>, EvilObject(trigger), EvilObject(victim1), EvilObject(victim2), EvilObject(victim3), EvilObject(victim4))

    Future calls to repr(p) return as expected post-__setstate__:

    functools.partial(<function EvilObject.__repr__.<locals>.<lambda> at 0x7ce5ce632a30>, 'replaced')

    This also passes test_functools.

    Happy to open a PR if this is an acceptable fix.

    Reacted by Junxi Qian
  4. picnixz
    added
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    and removed
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    on Feb 5, 2026
  5. serhiy-storchaka commented on Feb 11, 2026

    @serhiy-storchaka
    serhiy-storchaka
    on Feb 11, 2026
    Member
    More actions

    Thank you for your report, @Qanux. Please use <details> to collapse long listings.

  6. brijkapadia
    added 2 commits that reference this issue on Feb 28, 2026

    pythongh-144475: Fix a heap buffer overflow in partial_repr (v2)

    e869237

    pythongh-144475: Fix a heap buffer overflow in partial_repr (v2)

    7a5456c
  7. encukou
    added a commit that references this issue on Mar 3, 2026

    gh-144475: Fix reference management in partial_repr (GH-145362)

    671a953
  8. miss-islington
    added a commit that references this issue on Mar 3, 2026

    pythongh-144475: Fix reference management in partial_repr (pythonGH-1…

    cac1a91
  9. encukou
    added a commit that references this issue on Mar 3, 2026

    [3.14] gh-144475: Fix reference management in partial_repr (GH-145362) (

    3c99c16
  10. brijkapadia
    added a commit that references this issue on Mar 12, 2026

    [3.13] pythongh-144475: Fix reference management in partial_repr (pyt…

    bcce928
  11. Yhg1s
    added a commit that references this issue on Mar 24, 2026

    [3.13] gh-144475: Fix reference management in partial_repr (GH-145362) (

    4e37207
  12. encukou
    closed this as completedon Apr 7, 2026
  13. ljfp
    added a commit that references this issue on Apr 25, 2026

    pythongh-144475: Fix reference management in partial_repr (pythonGH-1…

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirC modules in the Modules dirtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      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.