You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
memoryview can set an exception in tp_clear #77894
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2018-05-31.12:15:10.896>labels= ['interpreter-core', '3.10', '3.9', 'type-crash', '3.11']
title='memoryview can set an exception in tp_clear'updated_at=<Date2021-10-18.20:19:58.191>user='https://github.com/serhiy-storchaka'
The tp_clear handler of memoryview can set an exception when fail to release the buffer. An exception in tp_clear is not expected and caused a crash in the garbage collector. In the master branch it will cause just writing a traceback to stderr (see bpo-33622), but in any case it would be better to handle the failure locally in memoryview. I don't know what is the best solution: silencing an error, writing a traceback with more detailed information, or resurrecting the buffer object.
I don't know how to reproduce a failure in tp_clear(). I just can't prove that it never fails. Maybe it is needed a bug in the implementation of the buffer protocol in third-party extension.
If it should not happen then we can just add
assert(!PyErr_Occurred());
or
if (PyErr_Occurred()) {
PyErr_WriteUnraisable(NULL);
}
It is better to crash in memoryview.c than in the garbage collector if this crash is caused by incorrect buffer protocol implementation.
skrah commented on May 31, 2018
skrahmannequin
Mannequin
More actions
This looks the same as bpo-25525. I think it cannot happen, and no one has ever reported an actual issue for 6 years now.
You *really* need to show a reproducer if you assert that something can crash.
See the delete_garbage() function line 770 in Modules/gcmodule.c for changes in the master branch relevant to this issue. See Py_FatalError() in the collect() function at line 974 for a crash.
skrah commented on May 31, 2018
skrahmannequin
Mannequin
More actions
Yes, but who calls tp_clear() if the memoryview is not being deallocated?
Serhiy is right about the theoretical concern here. However, it's probably quite difficult to find a concrete situation where this occurs, because we're talking about mbuf_clear and the managerbuffer object can't really get involved in a reference cycle by itself (not in normal use anyway): the memoryview object does, but it's a different thing.
By the way: PyBuffer_Release() returns void, so IMO it's a bug if it can return with an exception set. We should fix that rather than focus on mbuf_clear().
skrah commented on May 31, 2018
skrahmannequin
Mannequin
More actions
Well, the example would need exports:
>>> a = [bytes()]
>>> a.append(memoryview(a[0]))
>>> a.append(memoryview(a[1]))
>>> a.append(a)
>>> a
[b'', <memory at 0x1ad21f8>, <memory at 0x1b52348>, [...]]
The first memoryview has one export, so its refcount > 0.
Do I fundamentally misunderstand tp_clear() and tp_clear() can be called on objects with refcount > 0?
@serhiy-storchaka If you are still looking for reproducible test cases, check out https://youtrack.jetbrains.com/issue/PY-54447. PyCharm users running under the debugger with code using multiprocessing and concurrent.futures have been seeing this since Python 3.10. Example code to reproduce is included.
@serhiy-storchaka I can confirm that the change seems to have fixed the issue. I ran the test code in the linked isue in Python 3.10 and reproduced the bug, then again in 3.12.7 with your fix and was not able to reproduce it. Thanks for chasing this down!
static void
bytearray_dealloc(PyByteArrayObject *self)
{
if (self->ob_exports > 0) {
PyErr_SetString(PyExc_SystemError,
"deallocated bytearray object has exported buffers");
PyErr_Print();
}
if (self->ob_bytes != 0) {
PyObject_Free(self->ob_bytes);
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
I have never seen this message before all getbuffer() methods were added, which I believe started in 3.10. This would be consistent with the Jetbrains issue (linked above in this issue), where people say 3.9 worked. I'm not sure why a bytearray with exports is allowed to be deallocated.
Activity
serhiy-storchaka commentedon May 31, 2018
The tp_clear handler of memoryview can set an exception when fail to release the buffer. An exception in tp_clear is not expected and caused a crash in the garbage collector. In the master branch it will cause just writing a traceback to stderr (see bpo-33622), but in any case it would be better to handle the failure locally in memoryview. I don't know what is the best solution: silencing an error, writing a traceback with more detailed information, or resurrecting the buffer object.
skrah commentedon May 31, 2018
Could you please show how tp_clear() can be called when self->exports > 0? It should not happen.
bpo-33622 is a big issue with many commits. Would it be possible to extract the relevant part?
serhiy-storchaka commentedon May 31, 2018
I don't know how to reproduce a failure in tp_clear(). I just can't prove that it never fails. Maybe it is needed a bug in the implementation of the buffer protocol in third-party extension.
If it should not happen then we can just add
or
It is better to crash in memoryview.c than in the garbage collector if this crash is caused by incorrect buffer protocol implementation.
skrah commentedon May 31, 2018
This looks the same as bpo-25525. I think it cannot happen, and no one has ever reported an actual issue for 6 years now.
You *really* need to show a reproducer if you assert that something can crash.
serhiy-storchaka commentedon May 31, 2018
See the delete_garbage() function line 770 in Modules/gcmodule.c for changes in the master branch relevant to this issue. See Py_FatalError() in the collect() function at line 974 for a crash.
skrah commentedon May 31, 2018
Yes, but who calls tp_clear() if the memoryview is not being deallocated?
serhiy-storchaka commentedon May 31, 2018
The GC calls tp_clear() if the memoryview is a part of the reference loop.
The GC will call tp_clear() of the list or the memoryview. What will be called first is not specified.
skrah commentedon May 31, 2018
The point is that *no garbage collection is triggered* if self->exports > 0.
It would be a major bug if it were and I suspect it would be reported within a week. Fortunately, no such bug has been reported in 6 years.
pitrou commentedon May 31, 2018
Serhiy is right about the theoretical concern here. However, it's probably quite difficult to find a concrete situation where this occurs, because we're talking about mbuf_clear and the managerbuffer object can't really get involved in a reference cycle by itself (not in normal use anyway): the memoryview object does, but it's a different thing.
By the way: PyBuffer_Release() returns void, so IMO it's a bug if it can return with an exception set. We should fix that rather than focus on mbuf_clear().
skrah commentedon May 31, 2018
Well, the example would need exports:
The first memoryview has one export, so its refcount > 0.
Do I fundamentally misunderstand tp_clear() and tp_clear() can be called on objects with refcount > 0?
vstinner commentedon May 31, 2018
If the bug cannot occur, just add "assert(!PyErr_Occurred());" no?
17 remaining items
gh-77894: Fix a crash when the GC breaks a loop containing a memoryviâĶ
pythongh-77894: Fix a crash when the GC breaks a loop containing a meâĶ
[3.12] pythongh-77894: Fix a crash when the GC breaks a loop containiâĶ
[3.12] gh-77894: Fix a crash when the GC breaks a loop containing a mâĶ
[3.13] gh-77894: Fix a crash when the GC breaks a loop containing a mâĶ
tjnd89 commentedon Jan 14, 2025
@serhiy-storchaka If you are still looking for reproducible test cases, check out https://youtrack.jetbrains.com/issue/PY-54447. PyCharm users running under the debugger with code using
multiprocessingandconcurrent.futureshave been seeing this since Python 3.10. Example code to reproduce is included.serhiy-storchaka commentedon Jan 14, 2025
I forgot to close this issue after merging #123898.
@tjnd89, thank you for reference. Did this change fix that issue? If no, it is perhaps not related.
tjnd89 commentedon Jan 14, 2025
@serhiy-storchaka I can confirm that the change seems to have fixed the issue. I ran the test code in the linked isue in Python 3.10 and reproduced the bug, then again in 3.12.7 with your fix and was not able to reproduce it. Thanks for chasing this down!
lisa-gll commentedon May 11, 2025
It looks like all reproducers involve some kind of
getbuffer()methods that expose amemoryviewon the Python level.The previous collection scheme apparently relied on not adding these methods, which is why #58006 has not been implemented for a long time.
The new scheme apparently swallows an exception:
lisa-gll commentedon May 11, 2025
The ignored exception comes from:
I have never seen this message before all
getbuffer()methods were added, which I believe started in 3.10. This would be consistent with the Jetbrains issue (linked above in this issue), where people say 3.9 worked. I'm not sure why a bytearray with exports is allowed to be deallocated.Release cffi buffer exports explicitly to avoid a GC segfault with meâĶ