Skip to content

Commit 9abea7e

Browse files
committed
pythongh-104371: Fix calls to __release_buffer__ while an exception is active
1 parent fcd5fb4 commit 9abea7e

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

Lib/test/test_buffer.py

+13
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

+18-1
Original file line numberDiff line numberDiff line change
@@ -9115,8 +9115,9 @@ releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer)
91159115
}
91169116

91179117
static void
9118-
releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
9118+
releasebuffer_call_python_inner(PyObject *self, Py_buffer *buffer)
91199119
{
9120+
assert(!PyErr_Occurred());
91209121
PyObject *mv;
91219122
bool is_buffer_wrapper = Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type;
91229123
if (is_buffer_wrapper) {
@@ -9157,6 +9158,22 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
91579158
Py_DECREF(mv);
91589159
}
91599160

9161+
static void
9162+
releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
9163+
{
9164+
// bf_releasebuffer may be called while an exception is already active.
9165+
// We have no way to report additional errors up the stack, because
9166+
// this slot returns void, so we simply stash away the active exception
9167+
// and restore it after the call to Python returns.
9168+
PyObject *type, *value, *traceback;
9169+
PyErr_Fetch(&type, &value, &traceback);
9170+
9171+
releasebuffer_call_python_inner(self, buffer);
9172+
assert(!PyErr_Occurred());
9173+
9174+
PyErr_Restore(type, value, traceback);
9175+
}
9176+
91609177
/*
91619178
* bf_releasebuffer is very delicate, because we need to ensure that
91629179
* C bf_releasebuffer slots are called correctly (or we'll leak memory),

0 commit comments

Comments
 (0)