Skip to content

Commit a0a98dd

Browse files
gh-104371: Fix calls to __release_buffer__ while an exception is active (#104378)
Co-authored-by: Kumar Aditya <[email protected]>
1 parent ac66cc1 commit a0a98dd

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

Lib/test/test_buffer.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4749,6 +4749,19 @@ def __buffer__(self, flags):
47494749
c.clear()
47504750
self.assertIs(c.buffer, None)
47514751

4752+
def test_release_buffer_with_exception_set(self):
4753+
class A:
4754+
def __buffer__(self, flags):
4755+
return memoryview(bytes(8))
4756+
def __release_buffer__(self, view):
4757+
pass
4758+
4759+
b = bytearray(8)
4760+
with memoryview(b):
4761+
# now b.extend will raise an exception due to exports
4762+
with self.assertRaises(BufferError):
4763+
b.extend(A())
4764+
47524765

47534766
if __name__ == "__main__":
47544767
unittest.main()

Objects/typeobject.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9117,14 +9117,20 @@ releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer)
91179117
static void
91189118
releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
91199119
{
9120+
// bf_releasebuffer may be called while an exception is already active.
9121+
// We have no way to report additional errors up the stack, because
9122+
// this slot returns void, so we simply stash away the active exception
9123+
// and restore it after the call to Python returns.
9124+
PyObject *exc = PyErr_GetRaisedException();
9125+
91209126
PyObject *mv;
91219127
bool is_buffer_wrapper = Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type;
91229128
if (is_buffer_wrapper) {
91239129
// Make sure we pass the same memoryview to
91249130
// __release_buffer__() that __buffer__() returned.
91259131
PyBufferWrapper *bw = (PyBufferWrapper *)buffer->obj;
91269132
if (bw->mv == NULL) {
9127-
return;
9133+
goto end;
91289134
}
91299135
mv = Py_NewRef(bw->mv);
91309136
}
@@ -9134,7 +9140,7 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
91349140
mv = PyMemoryView_FromBuffer(buffer);
91359141
if (mv == NULL) {
91369142
PyErr_WriteUnraisable(self);
9137-
return;
9143+
goto end;
91389144
}
91399145
// Set the memoryview to restricted mode, which forbids
91409146
// users from saving any reference to the underlying buffer
@@ -9155,6 +9161,10 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
91559161
PyObject_CallMethodNoArgs(mv, &_Py_ID(release));
91569162
}
91579163
Py_DECREF(mv);
9164+
end:
9165+
assert(!PyErr_Occurred());
9166+
9167+
PyErr_SetRaisedException(exc);
91589168
}
91599169

91609170
/*

0 commit comments

Comments
 (0)