Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Assertion failure from functools.partial using a str subclass that raises on __eq__ #146075

Copy link
Copy link
@devdanzin

Description

@devdanzin
Issue body actions

Crash report

What happened?

It's possible to make the interpreter abort by passing an evil str subclass that raises on __eq__ to be used as a keyword by partial.

Automated diagnosis:

Bug: PyDict_Contains -1 treated as truthy in partial.__call__ vectorcall. When a keyword key's __eq__ or __hash__ raises during the PyDict_Contains check, the error return (-1) is treated as "found" (truthy), silently swallowing the exception and taking the wrong code path for keyword merging.

File: Modules/_functoolsmodule.c, line 455

MRE:

from functools import partial

class BadStr(str):
    def __eq__(self, other):
        raise RuntimeError
    def __hash__(self):
        return str.__hash__(self)

def f(**kwargs):
    return kwargs

p = partial(f, poison="")
result = p(**{BadStr("poison"): "new_value"})

Backtrace:

python: Python/generated_cases.c.h:12739: PyObject *_PyEval_EvalFrameDefault(PyThreadState *, _PyInterpreterFrame *, int): Assertion `!_PyErr_Occurred(tstate)' failed.

Program received signal SIGABRT, Aborted.

#0  __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:100
#3  0x00007ffff7c45e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c28888 in __GI_abort () at ./stdlib/abort.c:77
#5  0x00007ffff7c287f0 in __assert_fail_base (fmt=<optimized out>, assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:118
#6  0x00007ffff7c3c19f in __assert_fail (assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:127
#7  0x0000555555e5d037 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:12739
#8  0x0000555555e57778 in _PyEval_EvalFrame (tstate=0x5555568f7b18 <_PyRuntime+360664>, frame=0x7e8ff6fe52a8, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#9  _PyEval_Vector (tstate=<optimized out>, func=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0) at Python/ceval.c:2134
#10 0x0000555555ab9e00 in _PyObject_VectorcallTstate (tstate=tstate@entry=0x5555568f7b18 <_PyRuntime+360664>, callable=0x7cfff703cf60, args=args@entry=0x7bfff5b7e4a8,
    nargsf=nargsf@entry=9223372036854775809, kwnames=kwnames@entry=0x0) at ./Include/internal/pycore_call.h:136
#11 0x0000555555abcda5 in PyObject_CallOneArg (func=<optimized out>, arg=0x7d3ff7026870) at Objects/call.c:395
#12 0x0000555555c6fb74 in call_unbound_noarg (unbound=1, func=0x7cfff703cf60, self=0x7d3ff7026870) at Objects/typeobject.c:3033
#13 maybe_call_special_no_args (self=<optimized out>, attr=<optimized out>, attr_is_none=<optimized out>) at Objects/typeobject.c:3146
#14 0x0000555555caaeb6 in slot_tp_hash (self=<optimized out>) at Objects/typeobject.c:10705
#15 0x0000555555b97cd7 in _PyObject_HashFast (op=0x7d3ff7026870) at ./Include/internal/pycore_object.h:849
#16 setitem_take2_lock_held (mp=0x7c7ff70decc0, key=0x7d3ff7026870, value=0x7c6ff70eb5a0) at Objects/dictobject.c:2737
#17 0x000055555625d598 in partial_vectorcall (self=<optimized out>, args=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at ./Modules/_functoolsmodule.c:467
#18 0x0000555555abc520 in _PyVectorcall_Call (tstate=<optimized out>, func=<optimized out>, callable=<optimized out>, tuple=0x5555568c0b50 <_PyRuntime+135440>, kwargs=<optimized out>)
    at Objects/call.c:285
#19 0x0000555555e904e2 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:2937
#20 0x0000555555e57778 in _PyEval_EvalFrame (tstate=0x5555568f7b18 <_PyRuntime+360664>, frame=0x7e8ff6fe5220, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#21 _PyEval_Vector (tstate=<optimized out>, func=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0) at Python/ceval.c:2134
#22 0x0000555555e57195 in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=0x7c7ff70884c0) at Python/ceval.c:681
#23 0x0000555556061fb0 in run_eval_code_obj (tstate=tstate@entry=0x5555568f7b18 <_PyRuntime+360664>, co=co@entry=0x7d2ff6ffdfd0, globals=globals@entry=0x7c7ff70884c0,
    locals=locals@entry=0x7c7ff70884c0) at Python/pythonrun.c:1368
#24 0x000055555606117c in run_mod (mod=<optimized out>, filename=<optimized out>, globals=<optimized out>, locals=<optimized out>, flags=<optimized out>, arena=<optimized out>,
    interactive_src=<optimized out>, generate_new_source=<optimized out>) at Python/pythonrun.c:1471

Found using cpython-review-toolkit with Claude Opus 4.6, using the /cpython-review-toolkit:explore Modules/_functoolsmodule.c all deep command.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.15.0a7+ (heads/main:99e2c5eccd2, Mar 17 2026, 08:26:50) [Clang 21.1.2 (2ubuntu6)]

Linked PRs

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.15pre-release feature fixes, bugs and security fixespre-release feature fixes, bugs and security fixesextension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.