Skip to content

Commit d8b72e4

Browse files
committed
bpo-28963: Fix out of bound iteration in asyncio.Future.remove_done_callback/C (#408)
1 parent 2ef08d3 commit d8b72e4

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

Lib/test/test_asyncio/test_futures.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,35 @@ def test_remove_done_callback(self):
569569
self.assertEqual(bag, [2])
570570
self.assertEqual(f.result(), 'foo')
571571

572+
def test_remove_done_callbacks_list_mutation(self):
573+
# see http://bugs.python.org/issue28963 for details
574+
575+
fut = self._new_future()
576+
fut.add_done_callback(str)
577+
578+
for _ in range(63):
579+
fut.add_done_callback(id)
580+
581+
class evil:
582+
def __eq__(self, other):
583+
fut.remove_done_callback(id)
584+
return False
585+
586+
fut.remove_done_callback(evil())
587+
588+
def test_schedule_callbacks_list_mutation(self):
589+
# see http://bugs.python.org/issue28963 for details
590+
591+
def mut(f):
592+
f.remove_done_callback(str)
593+
594+
fut = self._new_future()
595+
fut.add_done_callback(mut)
596+
fut.add_done_callback(str)
597+
fut.add_done_callback(str)
598+
fut.set_result(1)
599+
test_utils.run_briefly(self.loop)
600+
572601

573602
@unittest.skipUnless(hasattr(futures, '_CFuture'),
574603
'requires the C _asyncio module')

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ Extension Modules
261261
Library
262262
-------
263263

264+
- bpo-28963: Fix out of bound iteration in asyncio.Future.remove_done_callback
265+
implemented in C.
266+
264267
- bpo-29704: asyncio.subprocess.SubprocessStreamProtocol no longer closes before
265268
all pipes are closed.
266269

Modules/_asynciomodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
521521
return NULL;
522522
}
523523

524-
for (i = 0; i < len; i++) {
524+
for (i = 0; i < PyList_GET_SIZE(self->fut_callbacks); i++) {
525525
int ret;
526526
PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
527527

0 commit comments

Comments
 (0)