Skip to content

Segfault from repr() of a corrupted set #141805

@devdanzin

Description

@devdanzin

Crash report

What happened?

It's possible to segfault the interpreter by corrupting a set, then calling repr() on it.

MRE:

tasks = set()

class Dummy(object):
    def __hash__(self):
        return 0

class CorrupTrigger():
    counter = 0
    def __hash__(self):
        return 0
    def __eq__(self,other):
        if self.counter < 1:
            self.counter += 1
            tasks.add(self)
        return False

tasks.add(Dummy())
tasks.add(Dummy())
tasks.pop()
tasks.add(CorrupTrigger())
repr(tasks)

Backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x0000555555b5b1e7 in Py_INCREF (op=0x0) at ./Include/refcount.h:281
281         PY_UINT32_T cur_refcnt = op->ob_refcnt;
(gdb) bt
#0  0x0000555555b5b1e7 in Py_INCREF (op=0x0) at ./Include/refcount.h:281
#1  _Py_NewRef (obj=0x0) at ./Include/refcount.h:529
#2  list_repr_impl (v=0x7c6ff6e72f60) at Objects/listobject.c:598
#3  list_repr (self=0x7c6ff6e72f60) at Objects/listobject.c:638
#4  0x0000555555bea219 in PyObject_Repr (v=v@entry=0x7c6ff6e72f60) at Objects/object.c:779
#5  0x0000555555c3c4b7 in set_repr_lock_held (so=0x7d0ff6eb17a0) at Objects/setobject.c:594
#6  set_repr (self=0x7d0ff6eb17a0) at Objects/setobject.c:622
#7  0x0000555555bea219 in PyObject_Repr (v=0x7d0ff6eb17a0) at Objects/object.c:779
#8  0x0000555555bd9b13 in cfunction_vectorcall_O (func=func@entry=0x7c7ff6e35d40, args=args@entry=0x7bfff5f70208, nargsf=nargsf@entry=9223372036854775809, kwnames=kwnames@entry=0x0)
    at Objects/methodobject.c:536
#9  0x0000555555aba6a0 in _PyObject_VectorcallTstate (tstate=0x555556aebf00 <_PyRuntime+358560>, callable=0x7c7ff6e35d40, args=0x7bfff5f70208, nargsf=9223372036854775809, kwnames=0x0)
    at ./Include/internal/pycore_call.h:169
#10 0x0000555555e8cf61 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:1620
#11 0x0000555555e4a67b in _PyEval_EvalFrame (tstate=0x555556aebf00 <_PyRuntime+358560>, frame=0x7e8ff6de5220, throwflag=0) at ./Include/internal/pycore_ceval.h:121

It's also possible to get set_pop_impl into an infinite loop by changing the last code block to:

tasks.add(Dummy())
tasks.add(Dummy())
tasks.pop()
tasks.add(CorrupTrigger())
tasks.pop()
tasks.pop()
tasks.pop()

Found when trying to simplify the MRE in #139071.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

Python 3.15.0a2+ (heads/main-dirty:41b9ad5b38e, Nov 20 2025, 11:03:41) [Clang 21.1.2 (2ubuntu6)]

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions