Skip to content

Commit ccbbdd0

Browse files
benjaminporenmn
andauthored
[3.6] closes bpo-31608: Fix a crash in methods of a subclass of _collections.deque with a bad __new__(). (GH-9178)
(cherry picked from commit 24bd50b) Co-authored-by: Oren Milman <[email protected]>
1 parent cb51dd7 commit ccbbdd0

File tree

3 files changed

+30
-3
lines changed

3 files changed

+30
-3
lines changed

‎Lib/test/test_deque.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,21 @@ def __iter__(self):
893893
d1 == d2 # not clear if this is supposed to be True or False,
894894
# but it used to give a SystemError
895895

896+
@support.cpython_only
897+
def test_bug_31608(self):
898+
# The interpreter used to crash in specific cases where a deque
899+
# subclass returned a non-deque.
900+
class X(deque):
901+
pass
902+
d = X()
903+
def bad___new__(cls, *args, **kwargs):
904+
return [42]
905+
X.__new__ = bad___new__
906+
with self.assertRaises(TypeError):
907+
d * 42 # shouldn't crash
908+
with self.assertRaises(TypeError):
909+
d + deque([1, 2, 3]) # shouldn't crash
910+
896911

897912
class SubclassWithKwargs(deque):
898913
def __init__(self, newarg=1):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Raise a ``TypeError`` instead of crashing if a ``collections.deque`` subclass
2+
returns a non-deque from ``__new__``. Patch by Oren Milman.

‎Modules/_collectionsmodule.c‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ deque_inplace_concat(dequeobject *deque, PyObject *other)
514514
static PyObject *
515515
deque_copy(PyObject *deque)
516516
{
517+
PyObject *result;
517518
dequeobject *old_deque = (dequeobject *)deque;
518519
if (Py_TYPE(deque) == &deque_type) {
519520
dequeobject *new_deque;
@@ -538,10 +539,19 @@ deque_copy(PyObject *deque)
538539
return NULL;
539540
}
540541
if (old_deque->maxlen < 0)
541-
return PyObject_CallFunction((PyObject *)(Py_TYPE(deque)), "O", deque, NULL);
542+
result = PyObject_CallFunctionObjArgs((PyObject *)(Py_TYPE(deque)),
543+
deque, NULL);
542544
else
543-
return PyObject_CallFunction((PyObject *)(Py_TYPE(deque)), "Oi",
544-
deque, old_deque->maxlen, NULL);
545+
result = PyObject_CallFunction((PyObject *)(Py_TYPE(deque)), "Oi",
546+
deque, old_deque->maxlen, NULL);
547+
if (result != NULL && !PyObject_TypeCheck(result, &deque_type)) {
548+
PyErr_Format(PyExc_TypeError,
549+
"%.200s() must return a deque, not %.200s",
550+
Py_TYPE(deque)->tp_name, Py_TYPE(result)->tp_name);
551+
Py_DECREF(result);
552+
return NULL;
553+
}
554+
return result;
545555
}
546556

547557
PyDoc_STRVAR(copy_doc, "Return a shallow copy of a deque.");

0 commit comments

Comments
 (0)