Skip to content

Aborts from working with memoryviews and buffers across threads in free-threading build #132565

@devdanzin

Description

@devdanzin

Crash report

What happened?

It's possible to hit two different assertion failures in memoryobject.c from similar code, as shown below.

Found when looking for a repro of #110408, on clang ASAN builds.

First abort code:

from threading import Thread
from time import sleep

def getbuffers(obj: memoryview) -> None:
    obj1 = memoryview(obj[1:15].tobytes())
    obj1.__buffer__(0)
    sleep(0.006)
    obj2 = memoryview(obj[10:25].tobytes())
    obj2.__buffer__(0)
    sleep(0.006)
    obj3 = memoryview(obj[20:35].tobytes())
    obj3.__buffer__(0)

for x in range(100):
    alive = []

    obj = memoryview(bytearray(b"\x00" * 100))

    for x in range(20):
        alive.append(Thread(target=getbuffers, args=(obj,)))

    for t in alive:
        t.start()

First abort backtrace:

python: Objects/memoryobject.c:123: void mbuf_dealloc(PyObject *): Assertion `self->exports == 0' failed.

Thread 1 "python" received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140737350373440) at ./nptl/pthread_kill.c:44
44      ./nptl/pthread_kill.c: No such file or directory.

#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737350373440) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140737350373440) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=140737350373440, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7cac476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c927f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff7c9271b in __assert_fail_base (
    fmt=0x7ffff7e47130 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
    assertion=0x5555562191a0 <str> "self->exports == 0",
    file=0x555556218ca0 <str> "Objects/memoryobject.c", line=123, function=<optimized out>)
    at ./assert/assert.c:94
#6  0x00007ffff7ca3e96 in __GI___assert_fail (assertion=0x5555562191a0 <str> "self->exports == 0",
    file=0x555556218ca0 <str> "Objects/memoryobject.c", line=line@entry=123,
    function=0x5555562191e0 <__PRETTY_FUNCTION__.mbuf_dealloc> "void mbuf_dealloc(PyObject *)")
    at ./assert/assert.c:103
#7  0x0000555555b64141 in mbuf_dealloc (_self=<managedbuffer at remote 0x7fffb4338c10>)
    at Objects/memoryobject.c:123
#8  0x0000555555b86ba3 in _Py_Dealloc (op=<managedbuffer at remote 0x7fffb4338c10>)
    at Objects/object.c:3025
#9  0x0000555555b67e82 in Py_DECREF (lineno=1157, op=<managedbuffer at remote 0x7fffb4338c10>,
    filename=<optimized out>) at ./Include/refcount.h:365
#10 memory_dealloc (_self=<memoryview at remote 0x7fffb4a9c950>) at Objects/memoryobject.c:1157
#11 0x0000555555b86ba3 in _Py_Dealloc (op=<memoryview at remote 0x7fffb4a9c950>) at Objects/object.c:3025
#12 0x0000555555bfa8b9 in Py_DECREF (lineno=526, op=<memoryview at remote 0x7fffb4a9c950>,
    filename=<optimized out>) at ./Include/refcount.h:365
#13 Py_XDECREF (op=<memoryview at remote 0x7fffb4a9c950>) at ./Include/refcount.h:526
#14 tuple_dealloc (self=(<memoryview at remote 0x7fffb4a9c950>,)) at Objects/tupleobject.c:214
#15 0x0000555555b86ba3 in _Py_Dealloc (op=(<memoryview at remote 0x7fffb4a9c950>,))
    at Objects/object.c:3025
#16 0x0000555555dd7f38 in merge_queued_objects (to_merge=<optimized out>) at Python/brc.c:110
#17 _Py_brc_merge_refcounts (tstate=tstate@entry=0x5555567728d8 <_PyRuntime+361752>) at Python/brc.c:131
#18 0x0000555555edf328 in _Py_HandlePending (tstate=tstate@entry=0x5555567728d8 <_PyRuntime+361752>) at Python/ceval_gil.c:1351
#19 0x0000555555dfd62e in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:3887

Second abort code:

from threading import Thread
from time import sleep

def getbuffers(obj: memoryview) -> None:
    obj1 = obj[1:15]
    obj1.__buffer__(0)
    sleep(0.006)
    obj2 = obj[10:25]
    obj2.__buffer__(0)
    sleep(0.006)
    obj3 = obj[20:35]
    obj3.__buffer__(0)

for x in range(100):
    alive = []

    obj = memoryview(bytearray(b"\x00" * 100))

    for x in range(20):
        alive.append(Thread(target=getbuffers, args=(obj,)))

    for t in alive:
        t.start()

Second abort backtrace:

[Thread 0x7fff69810640 (LWP 23826) exited]
python: Objects/memoryobject.c:1116: void _memory_release(PyMemoryViewObject *): Assertion `self->mbuf->exports > 0' failed.

Thread 1 "python" received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140737350373440) at ./nptl/pthread_kill.c:44
44      ./nptl/pthread_kill.c: No such file or directory.
(gdb) bt
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737350373440) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140737350373440) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=140737350373440, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7cac476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c927f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff7c9271b in __assert_fail_base (
    fmt=0x7ffff7e47130 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
    assertion=0x555556219e40 <str> "self->mbuf->exports > 0",
    file=0x555556218ca0 <str> "Objects/memoryobject.c", line=1116, function=<optimized out>)
    at ./assert/assert.c:94
#6  0x00007ffff7ca3e96 in __GI___assert_fail (assertion=0x555556219e40 <str> "self->mbuf->exports > 0",
    file=0x555556218ca0 <str> "Objects/memoryobject.c", line=line@entry=1116,
    function=0x555556219de0 <__PRETTY_FUNCTION__._memory_release> "void _memory_release(PyMemoryViewObject *)") at ./assert/assert.c:103
#7  0x0000555555b6badb in _memory_release (self=self@entry=0x7fffb4a9ca30) at Objects/memoryobject.c:1116
#8  0x0000555555b67dc6 in memory_dealloc (_self=<memoryview at remote 0x7fffb4a9ca30>)
    at Objects/memoryobject.c:1156
#9  0x0000555555b86ba3 in _Py_Dealloc (op=<memoryview at remote 0x7fffb4a9ca30>) at Objects/object.c:3025
#10 0x0000555555b2c928 in Py_DECREF (lineno=526, op=<memoryview at remote 0x7fffb4a9ca30>,
    filename=<optimized out>) at ./Include/refcount.h:365
#11 Py_XDECREF (op=<memoryview at remote 0x7fffb4a9ca30>) at ./Include/refcount.h:526
#12 insertdict (interp=<optimized out>, mp=0x7fffb45f3790, key='obj', hash=<optimized out>,
    value=<optimized out>) at Objects/dictobject.c:1875
#13 0x0000555555b2af39 in _PyDict_SetItem_Take2 (mp=<optimized out>, key=<optimized out>,
    value=<optimized out>) at Objects/dictobject.c:2613
#14 PyDict_SetItem (
    op=op@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/mnt/c/Users/ddini/crashers/main/shelve-cpu_load-sigsegv/memoryview_from_zero.py') at remote 0x7fffb4770b20>, '__spec__': None, '__builtins__': <module at remote 0x7fffb425c090>, '__file__': '/mnt/c/Users/ddini/crashers/main/shelve-cpu_load-sigsegv/memoryview_from_zero.py', '__cached__': None, 'Thread': <type at remote 0x7fffb4d06410>, 'sleep': <built-in method sleep of module object at remote 0x7fffb4731580>, 'call_getbuffer': <function at remote 0x7fffb4a9c950>, 'x': 1, 'alive': [], 'obj': <memoryview at remote 0x7fffb4be2930>, 't': <Thread(_context=<_contextvars.Context at remote 0x7fffb44f44b0>, _daemonic=False, _handle=<_thread._ThreadHandle at remote 0x7fffb4bf3f80>, _ident=140734963451456, _initialized=True, _invoke_excepthook=<function at remote 0x7fffb4be2850>, _name='Thread-154 (call_getbuffer)', _native_id=23826, _started=<Event(_cond=<Condition(_lock=<_thread.lock at remote 0x7fffb...(truncated), key=key@entry='obj',
    value=<optimized out>) at Objects/dictobject.c:2633
#15 0x0000555555defbd2 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>,
    throwflag=<optimized out>) at Python/generated_cases.c.h:11083
#16 0x0000555555ddcb03 in _PyEval_EvalFrame (tstate=0x5555567728d8 <_PyRuntime+361752>,
    frame=0x529000005220, throwflag=0) at ./Include/internal/pycore_ceval.h:119

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

Python 3.14.0a7+ experimental free-threading build (heads/main:102f825c511, Apr 14 2025, 20:26:55) [Clang 19.1.7 (++20250114103320+cd708029e0b2-1exp120250114103432.75)]

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-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