Skip to content

Commit 5485085

Browse files
authored
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.
1 parent fd7d539 commit 5485085

File tree

2 files changed

+56
-24
lines changed

2 files changed

+56
-24
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix memory leaks in asyncio ProactorEventLoop on overlapped operation
2+
failure.

Modules/overlapped.c

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,28 @@ Overlapped_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
561561
return (PyObject *)self;
562562
}
563563

564+
565+
/* Note (bpo-32710): OverlappedType.tp_clear is not defined to not release
566+
buffers while overlapped are still running, to prevent a crash. */
567+
static int
568+
Overlapped_clear(OverlappedObject *self)
569+
{
570+
switch (self->type) {
571+
case TYPE_READ:
572+
case TYPE_ACCEPT:
573+
Py_CLEAR(self->allocated_buffer);
574+
break;
575+
case TYPE_WRITE:
576+
case TYPE_READINTO:
577+
if (self->user_buffer.obj) {
578+
PyBuffer_Release(&self->user_buffer);
579+
}
580+
break;
581+
}
582+
self->type = TYPE_NOT_STARTED;
583+
return 0;
584+
}
585+
564586
static void
565587
Overlapped_dealloc(OverlappedObject *self)
566588
{
@@ -594,20 +616,11 @@ Overlapped_dealloc(OverlappedObject *self)
594616
}
595617
}
596618

597-
if (self->overlapped.hEvent != NULL)
619+
if (self->overlapped.hEvent != NULL) {
598620
CloseHandle(self->overlapped.hEvent);
599-
600-
switch (self->type) {
601-
case TYPE_READ:
602-
case TYPE_ACCEPT:
603-
Py_CLEAR(self->allocated_buffer);
604-
break;
605-
case TYPE_WRITE:
606-
case TYPE_READINTO:
607-
if (self->user_buffer.obj)
608-
PyBuffer_Release(&self->user_buffer);
609-
break;
610621
}
622+
623+
Overlapped_clear(self);
611624
PyObject_Del(self);
612625
SetLastError(olderr);
613626
}
@@ -723,8 +736,7 @@ do_ReadFile(OverlappedObject *self, HANDLE handle,
723736
case ERROR_IO_PENDING:
724737
Py_RETURN_NONE;
725738
default:
726-
PyBuffer_Release(&self->user_buffer);
727-
self->type = TYPE_NOT_STARTED;
739+
Overlapped_clear(self);
728740
return SetFromWindowsErr(err);
729741
}
730742
}
@@ -827,7 +839,7 @@ do_WSARecv(OverlappedObject *self, HANDLE handle,
827839
case ERROR_IO_PENDING:
828840
Py_RETURN_NONE;
829841
default:
830-
self->type = TYPE_NOT_STARTED;
842+
Overlapped_clear(self);
831843
return SetFromWindowsErr(err);
832844
}
833845
}
@@ -955,7 +967,7 @@ Overlapped_WriteFile(OverlappedObject *self, PyObject *args)
955967
case ERROR_IO_PENDING:
956968
Py_RETURN_NONE;
957969
default:
958-
self->type = TYPE_NOT_STARTED;
970+
Overlapped_clear(self);
959971
return SetFromWindowsErr(err);
960972
}
961973
}
@@ -1012,8 +1024,7 @@ Overlapped_WSASend(OverlappedObject *self, PyObject *args)
10121024
case ERROR_IO_PENDING:
10131025
Py_RETURN_NONE;
10141026
default:
1015-
PyBuffer_Release(&self->user_buffer);
1016-
self->type = TYPE_NOT_STARTED;
1027+
Overlapped_clear(self);
10171028
return SetFromWindowsErr(err);
10181029
}
10191030
}
@@ -1063,7 +1074,7 @@ Overlapped_AcceptEx(OverlappedObject *self, PyObject *args)
10631074
case ERROR_IO_PENDING:
10641075
Py_RETURN_NONE;
10651076
default:
1066-
self->type = TYPE_NOT_STARTED;
1077+
Overlapped_clear(self);
10671078
return SetFromWindowsErr(err);
10681079
}
10691080
}
@@ -1155,7 +1166,7 @@ Overlapped_ConnectEx(OverlappedObject *self, PyObject *args)
11551166
case ERROR_IO_PENDING:
11561167
Py_RETURN_NONE;
11571168
default:
1158-
self->type = TYPE_NOT_STARTED;
1169+
Overlapped_clear(self);
11591170
return SetFromWindowsErr(err);
11601171
}
11611172
}
@@ -1194,7 +1205,7 @@ Overlapped_DisconnectEx(OverlappedObject *self, PyObject *args)
11941205
case ERROR_IO_PENDING:
11951206
Py_RETURN_NONE;
11961207
default:
1197-
self->type = TYPE_NOT_STARTED;
1208+
Overlapped_clear(self);
11981209
return SetFromWindowsErr(err);
11991210
}
12001211
}
@@ -1249,7 +1260,7 @@ Overlapped_TransmitFile(OverlappedObject *self, PyObject *args)
12491260
case ERROR_IO_PENDING:
12501261
Py_RETURN_NONE;
12511262
default:
1252-
self->type = TYPE_NOT_STARTED;
1263+
Overlapped_clear(self);
12531264
return SetFromWindowsErr(err);
12541265
}
12551266
}
@@ -1290,7 +1301,7 @@ Overlapped_ConnectNamedPipe(OverlappedObject *self, PyObject *args)
12901301
case ERROR_IO_PENDING:
12911302
Py_RETURN_FALSE;
12921303
default:
1293-
self->type = TYPE_NOT_STARTED;
1304+
Overlapped_clear(self);
12941305
return SetFromWindowsErr(err);
12951306
}
12961307
}
@@ -1340,6 +1351,25 @@ Overlapped_getpending(OverlappedObject *self)
13401351
self->type != TYPE_NOT_STARTED);
13411352
}
13421353

1354+
static int
1355+
Overlapped_traverse(OverlappedObject *self, visitproc visit, void *arg)
1356+
{
1357+
switch (self->type) {
1358+
case TYPE_READ:
1359+
case TYPE_ACCEPT:
1360+
Py_VISIT(self->allocated_buffer);
1361+
break;
1362+
case TYPE_WRITE:
1363+
case TYPE_READINTO:
1364+
if (self->user_buffer.obj) {
1365+
Py_VISIT(&self->user_buffer.obj);
1366+
}
1367+
break;
1368+
}
1369+
return 0;
1370+
}
1371+
1372+
13431373
static PyMethodDef Overlapped_methods[] = {
13441374
{"getresult", (PyCFunction) Overlapped_getresult,
13451375
METH_VARARGS, Overlapped_getresult_doc},
@@ -1410,7 +1440,7 @@ PyTypeObject OverlappedType = {
14101440
/* tp_as_buffer */ 0,
14111441
/* tp_flags */ Py_TPFLAGS_DEFAULT,
14121442
/* tp_doc */ "OVERLAPPED structure wrapper",
1413-
/* tp_traverse */ 0,
1443+
/* tp_traverse */ (traverseproc)Overlapped_traverse,
14141444
/* tp_clear */ 0,
14151445
/* tp_richcompare */ 0,
14161446
/* tp_weaklistoffset */ 0,

0 commit comments

Comments
 (0)