From 9abea7ed257f2be746425172e1fe04b43312a6e9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 10 May 2023 19:08:11 -0700 Subject: [PATCH 1/3] gh-104371: Fix calls to __release_buffer__ while an exception is active --- Lib/test/test_buffer.py | 13 +++++++++++++ Objects/typeobject.c | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 2c65ae8114818f..94fc9d4436b717 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -4749,6 +4749,19 @@ def __buffer__(self, flags): c.clear() self.assertIs(c.buffer, None) + def test_release_buffer_with_exception_set(self): + class A: + def __buffer__(self, flags): + return memoryview(bytes(8)) + def __release_buffer__(self, view): + pass + + b = bytearray(8) + with memoryview(b): + # now b.extend will raise an exception due to exports + with self.assertRaises(BufferError): + b.extend(A()) + if __name__ == "__main__": unittest.main() diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 98fac276a873e1..3d35325a7f396c 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9115,8 +9115,9 @@ releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer) } static void -releasebuffer_call_python(PyObject *self, Py_buffer *buffer) +releasebuffer_call_python_inner(PyObject *self, Py_buffer *buffer) { + assert(!PyErr_Occurred()); PyObject *mv; bool is_buffer_wrapper = Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type; if (is_buffer_wrapper) { @@ -9157,6 +9158,22 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer) Py_DECREF(mv); } +static void +releasebuffer_call_python(PyObject *self, Py_buffer *buffer) +{ + // bf_releasebuffer may be called while an exception is already active. + // We have no way to report additional errors up the stack, because + // this slot returns void, so we simply stash away the active exception + // and restore it after the call to Python returns. + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + + releasebuffer_call_python_inner(self, buffer); + assert(!PyErr_Occurred()); + + PyErr_Restore(type, value, traceback); +} + /* * bf_releasebuffer is very delicate, because we need to ensure that * C bf_releasebuffer slots are called correctly (or we'll leak memory), From 0576d2622323bd16d04211b240c0593dd5f50433 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 11 May 2023 18:58:40 -0700 Subject: [PATCH 2/3] Avoid extra function --- Objects/typeobject.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3d35325a7f396c..1ded2413a1a4db 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9115,9 +9115,15 @@ releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer) } static void -releasebuffer_call_python_inner(PyObject *self, Py_buffer *buffer) +releasebuffer_call_python(PyObject *self, Py_buffer *buffer) { - assert(!PyErr_Occurred()); + // bf_releasebuffer may be called while an exception is already active. + // We have no way to report additional errors up the stack, because + // this slot returns void, so we simply stash away the active exception + // and restore it after the call to Python returns. + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + PyObject *mv; bool is_buffer_wrapper = Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type; if (is_buffer_wrapper) { @@ -9125,7 +9131,7 @@ releasebuffer_call_python_inner(PyObject *self, Py_buffer *buffer) // __release_buffer__() that __buffer__() returned. PyBufferWrapper *bw = (PyBufferWrapper *)buffer->obj; if (bw->mv == NULL) { - return; + goto end; } mv = Py_NewRef(bw->mv); } @@ -9135,7 +9141,7 @@ releasebuffer_call_python_inner(PyObject *self, Py_buffer *buffer) mv = PyMemoryView_FromBuffer(buffer); if (mv == NULL) { PyErr_WriteUnraisable(self); - return; + goto end; } // Set the memoryview to restricted mode, which forbids // users from saving any reference to the underlying buffer @@ -9156,19 +9162,7 @@ releasebuffer_call_python_inner(PyObject *self, Py_buffer *buffer) PyObject_CallMethodNoArgs(mv, &_Py_ID(release)); } Py_DECREF(mv); -} - -static void -releasebuffer_call_python(PyObject *self, Py_buffer *buffer) -{ - // bf_releasebuffer may be called while an exception is already active. - // We have no way to report additional errors up the stack, because - // this slot returns void, so we simply stash away the active exception - // and restore it after the call to Python returns. - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - - releasebuffer_call_python_inner(self, buffer); +end: assert(!PyErr_Occurred()); PyErr_Restore(type, value, traceback); From a29d72b0f92d553f1900e21f384659a566bc208d Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Fri, 12 May 2023 10:25:46 +0530 Subject: [PATCH 3/3] use PyErr_GetRaisedException --- Objects/typeobject.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1ded2413a1a4db..6a50f4efd66f15 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9121,8 +9121,7 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer) // We have no way to report additional errors up the stack, because // this slot returns void, so we simply stash away the active exception // and restore it after the call to Python returns. - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); + PyObject *exc = PyErr_GetRaisedException(); PyObject *mv; bool is_buffer_wrapper = Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type; @@ -9165,7 +9164,7 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer) end: assert(!PyErr_Occurred()); - PyErr_Restore(type, value, traceback); + PyErr_SetRaisedException(exc); } /*