From adb3507e8922e9e66a2cd4b3e00623aa56db2673 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 11 Mar 2025 15:18:44 -0600 Subject: [PATCH 1/4] Fix a bug. --- Modules/_interpretersmodule.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 74f1c02cfab4c9..71016f673822d4 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -104,14 +104,16 @@ xibufferview_dealloc(PyObject *op) { XIBufferViewObject *self = XIBufferViewObject_CAST(op); PyInterpreterState *interp = _PyInterpreterState_LookUpID(self->interpid); - /* If the interpreter is no longer alive then we have problems, - since other objects may be using the buffer still. */ - assert(interp != NULL); - - if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp, self->view) < 0) { - // XXX Emit a warning? + if (interp == NULL) { + /* That interpreter is alrady dead. */ PyErr_Clear(); } + else { + if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp, self->view) < 0) { + // XXX Emit a warning? + PyErr_Clear(); + } + } PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); From aee515b8d38a67689dfb3ea75e713fc14bd4a06e Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 11 Mar 2025 15:47:52 -0600 Subject: [PATCH 2/4] Handle errors in _memoryview_from_xid() correctly. --- Modules/_interpretersmodule.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 71016f673822d4..d0b8f53b27bad8 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -168,7 +168,12 @@ _memoryview_from_xid(_PyXIData_t *data) if (obj == NULL) { return NULL; } - return PyMemoryView_FromObject(obj); + PyObject *res = PyMemoryView_FromObject(obj); + if (res == NULL) { + Py_DECREF(obj); + return NULL; + } + return res; } static int From 54f829f206f432b61cca7ed416ce98dcf3a27e9c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 11 Mar 2025 16:06:45 -0600 Subject: [PATCH 3/4] Clean up the buffer if the xidata is never used. --- Modules/_interpretersmodule.c | 81 +++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index d0b8f53b27bad8..3d64b0ace16741 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -84,18 +84,27 @@ typedef struct { #define XIBufferViewObject_CAST(op) ((XIBufferViewObject *)(op)) static PyObject * -xibufferview_from_xid(PyTypeObject *cls, _PyXIData_t *data) +xibufferview_from_buffer(PyTypeObject *cls, Py_buffer *view, int64_t interpid) { - assert(_PyXIData_DATA(data) != NULL); - assert(_PyXIData_OBJ(data) == NULL); - assert(_PyXIData_INTERPID(data) >= 0); + assert(interpid >= 0); + + Py_buffer *copied = PyMem_RawMalloc(sizeof(Py_buffer)); + if (copied == NULL) { + return NULL; + } + /* This steals the view->obj reference */ + *copied = *view; + XIBufferViewObject *self = PyObject_Malloc(sizeof(XIBufferViewObject)); if (self == NULL) { + PyMem_RawFree(copied); return NULL; } PyObject_Init((PyObject *)self, cls); - self->view = (Py_buffer *)_PyXIData_DATA(data); - self->interpid = _PyXIData_INTERPID(data); + *self = (XIBufferViewObject){ + .view = copied, + .interpid = interpid, + }; return (PyObject *)self; } @@ -103,15 +112,22 @@ static void xibufferview_dealloc(PyObject *op) { XIBufferViewObject *self = XIBufferViewObject_CAST(op); - PyInterpreterState *interp = _PyInterpreterState_LookUpID(self->interpid); - if (interp == NULL) { - /* That interpreter is alrady dead. */ - PyErr_Clear(); - } - else { - if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp, self->view) < 0) { - // XXX Emit a warning? + + if (self->view != NULL) { + PyInterpreterState *interp = + _PyInterpreterState_LookUpID(self->interpid); + if (interp == NULL) { + /* The interpreter is no longer alive. */ PyErr_Clear(); + PyMem_RawFree(self->view); + } + else { + if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp, + self->view) < 0) + { + // XXX Emit a warning? + PyErr_Clear(); + } } } @@ -157,14 +173,28 @@ static PyType_Spec XIBufferViewType_spec = { static PyTypeObject * _get_current_xibufferview_type(void); + +struct xibuffer { + Py_buffer view; + int used; +}; + static PyObject * _memoryview_from_xid(_PyXIData_t *data) { + assert(_PyXIData_DATA(data) != NULL); + assert(_PyXIData_OBJ(data) == NULL); + assert(_PyXIData_INTERPID(data) >= 0); + struct xibuffer *view = (struct xibuffer *)_PyXIData_DATA(data); + assert(!view->used); + PyTypeObject *cls = _get_current_xibufferview_type(); if (cls == NULL) { return NULL; } - PyObject *obj = xibufferview_from_xid(cls, data); + + PyObject *obj = xibufferview_from_buffer( + cls, &view->view, _PyXIData_INTERPID(data)); if (obj == NULL) { return NULL; } @@ -173,21 +203,34 @@ _memoryview_from_xid(_PyXIData_t *data) Py_DECREF(obj); return NULL; } + view->used = 1; return res; } +static void +_pybuffer_shared_free(void* data) +{ + struct xibuffer *view = (struct xibuffer *)data; + if (!view->used) { + PyBuffer_Release(&view->view); + } + PyMem_RawFree(data); +} + static int -_memoryview_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_pybuffer_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) { - Py_buffer *view = PyMem_RawMalloc(sizeof(Py_buffer)); + struct xibuffer *view = PyMem_RawMalloc(sizeof(struct xibuffer)); if (view == NULL) { return -1; } - if (PyObject_GetBuffer(obj, view, PyBUF_FULL_RO) < 0) { + view->used = 0; + if (PyObject_GetBuffer(obj, &view->view, PyBUF_FULL_RO) < 0) { PyMem_RawFree(view); return -1; } _PyXIData_Init(data, tstate->interp, view, NULL, _memoryview_from_xid); + data->free = _pybuffer_shared_free; return 0; } @@ -208,7 +251,7 @@ register_memoryview_xid(PyObject *mod, PyTypeObject **p_state) *p_state = cls; // Register XID for the builtin memoryview type. - if (ensure_xid_class(&PyMemoryView_Type, _memoryview_shared) < 0) { + if (ensure_xid_class(&PyMemoryView_Type, _pybuffer_shared) < 0) { return -1; } // We don't ever bother un-registering memoryview. From ba7593d2828bea10880065325632d5f70bf58d95 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 21 Apr 2025 11:20:08 -0600 Subject: [PATCH 4/4] Do not undo what PyObject_Init() just did. --- Modules/_interpretersmodule.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 3d64b0ace16741..a70dce957d793e 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -76,7 +76,7 @@ is_running_main(PyInterpreterState *interp) // XXX Release when the original interpreter is destroyed. typedef struct { - PyObject_HEAD + PyObject base; Py_buffer *view; int64_t interpid; } XIBufferViewObject; @@ -100,8 +100,9 @@ xibufferview_from_buffer(PyTypeObject *cls, Py_buffer *view, int64_t interpid) PyMem_RawFree(copied); return NULL; } - PyObject_Init((PyObject *)self, cls); + PyObject_Init(&self->base, cls); *self = (XIBufferViewObject){ + .base = self->base, .view = copied, .interpid = interpid, };