diff options
author | Victor Stinner <vstinner@redhat.com> | 2019-01-11 13:35:14 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-11 13:35:14 (GMT) |
commit | 5485085b324a45307c1ff4ec7d85b5998d7d5e0d (patch) | |
tree | a1183c6a4bec97b23d427ed6425603e3db5ff4c2 /Modules | |
parent | fd7d539be3ce1cc098a4f104b7a7816ca00add16 (diff) | |
download | cpython-5485085b324a45307c1ff4ec7d85b5998d7d5e0d.zip cpython-5485085b324a45307c1ff4ec7d85b5998d7d5e0d.tar.gz cpython-5485085b324a45307c1ff4ec7d85b5998d7d5e0d.tar.bz2 |
bpo-32710: Fix _overlapped.Overlapped memory leaks (GH-11489)
Fix memory leaks in asyncio ProactorEventLoop on overlapped operation
failures.
Changes:
* Implement the tp_traverse slot in the _overlapped.Overlapped type
to help to break reference cycles and identify referrers in the
garbage collector.
* Always clear overlapped on failure: not only set type to
TYPE_NOT_STARTED, but release also resources.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/overlapped.c | 78 |
1 files changed, 54 insertions, 24 deletions
diff --git a/Modules/overlapped.c b/Modules/overlapped.c index bbaa4fb..ef4390b 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -561,6 +561,28 @@ Overlapped_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)self; } + +/* Note (bpo-32710): OverlappedType.tp_clear is not defined to not release + buffers while overlapped are still running, to prevent a crash. */ +static int +Overlapped_clear(OverlappedObject *self) +{ + switch (self->type) { + case TYPE_READ: + case TYPE_ACCEPT: + Py_CLEAR(self->allocated_buffer); + break; + case TYPE_WRITE: + case TYPE_READINTO: + if (self->user_buffer.obj) { + PyBuffer_Release(&self->user_buffer); + } + break; + } + self->type = TYPE_NOT_STARTED; + return 0; +} + static void Overlapped_dealloc(OverlappedObject *self) { @@ -594,20 +616,11 @@ Overlapped_dealloc(OverlappedObject *self) } } - if (self->overlapped.hEvent != NULL) + if (self->overlapped.hEvent != NULL) { CloseHandle(self->overlapped.hEvent); - - switch (self->type) { - case TYPE_READ: - case TYPE_ACCEPT: - Py_CLEAR(self->allocated_buffer); - break; - case TYPE_WRITE: - case TYPE_READINTO: - if (self->user_buffer.obj) - PyBuffer_Release(&self->user_buffer); - break; } + + Overlapped_clear(self); PyObject_Del(self); SetLastError(olderr); } @@ -723,8 +736,7 @@ do_ReadFile(OverlappedObject *self, HANDLE handle, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - PyBuffer_Release(&self->user_buffer); - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -827,7 +839,7 @@ do_WSARecv(OverlappedObject *self, HANDLE handle, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -955,7 +967,7 @@ Overlapped_WriteFile(OverlappedObject *self, PyObject *args) case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1012,8 +1024,7 @@ Overlapped_WSASend(OverlappedObject *self, PyObject *args) case ERROR_IO_PENDING: Py_RETURN_NONE; default: - PyBuffer_Release(&self->user_buffer); - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1063,7 +1074,7 @@ Overlapped_AcceptEx(OverlappedObject *self, PyObject *args) case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1155,7 +1166,7 @@ Overlapped_ConnectEx(OverlappedObject *self, PyObject *args) case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1194,7 +1205,7 @@ Overlapped_DisconnectEx(OverlappedObject *self, PyObject *args) case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1249,7 +1260,7 @@ Overlapped_TransmitFile(OverlappedObject *self, PyObject *args) case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1290,7 +1301,7 @@ Overlapped_ConnectNamedPipe(OverlappedObject *self, PyObject *args) case ERROR_IO_PENDING: Py_RETURN_FALSE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1340,6 +1351,25 @@ Overlapped_getpending(OverlappedObject *self) self->type != TYPE_NOT_STARTED); } +static int +Overlapped_traverse(OverlappedObject *self, visitproc visit, void *arg) +{ + switch (self->type) { + case TYPE_READ: + case TYPE_ACCEPT: + Py_VISIT(self->allocated_buffer); + break; + case TYPE_WRITE: + case TYPE_READINTO: + if (self->user_buffer.obj) { + Py_VISIT(&self->user_buffer.obj); + } + break; + } + return 0; +} + + static PyMethodDef Overlapped_methods[] = { {"getresult", (PyCFunction) Overlapped_getresult, METH_VARARGS, Overlapped_getresult_doc}, @@ -1410,7 +1440,7 @@ PyTypeObject OverlappedType = { /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT, /* tp_doc */ "OVERLAPPED structure wrapper", - /* tp_traverse */ 0, + /* tp_traverse */ (traverseproc)Overlapped_traverse, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, |