bpo-39382: Avoid dangling object use in abstract_issubclass() (GH-18530)
Hold reference of __bases__ tuple until tuple item is done with, because by dropping the reference the item may be destroyed.
This commit is contained in:
parent
a025d4ca99
commit
1c56f8ffad
|
@ -251,6 +251,27 @@ class TestIsInstanceIsSubclass(unittest.TestCase):
|
||||||
# blown
|
# blown
|
||||||
self.assertRaises(RecursionError, blowstack, isinstance, '', str)
|
self.assertRaises(RecursionError, blowstack, isinstance, '', str)
|
||||||
|
|
||||||
|
def test_issubclass_refcount_handling(self):
|
||||||
|
# bpo-39382: abstract_issubclass() didn't hold item reference while
|
||||||
|
# peeking in the bases tuple, in the single inheritance case.
|
||||||
|
class A:
|
||||||
|
@property
|
||||||
|
def __bases__(self):
|
||||||
|
return (int, )
|
||||||
|
|
||||||
|
class B:
|
||||||
|
def __init__(self):
|
||||||
|
# setting this here increases the chances of exhibiting the bug,
|
||||||
|
# probably due to memory layout changes.
|
||||||
|
self.x = 1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __bases__(self):
|
||||||
|
return (A(), )
|
||||||
|
|
||||||
|
self.assertEqual(True, issubclass(B(), int))
|
||||||
|
|
||||||
|
|
||||||
def blowstack(fxn, arg, compare_to):
|
def blowstack(fxn, arg, compare_to):
|
||||||
# Make sure that calling isinstance with a deeply nested tuple for its
|
# Make sure that calling isinstance with a deeply nested tuple for its
|
||||||
# argument will raise RecursionError eventually.
|
# argument will raise RecursionError eventually.
|
||||||
|
|
|
@ -594,6 +594,7 @@ Karan Goel
|
||||||
Jeroen Van Goey
|
Jeroen Van Goey
|
||||||
Christoph Gohlke
|
Christoph Gohlke
|
||||||
Tim Golden
|
Tim Golden
|
||||||
|
Yonatan Goldschmidt
|
||||||
Mark Gollahon
|
Mark Gollahon
|
||||||
Guilherme Gonçalves
|
Guilherme Gonçalves
|
||||||
Tiago Gonçalves
|
Tiago Gonçalves
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix a use-after-free in the single inheritance path of ``issubclass()``, when
|
||||||
|
the ``__bases__`` of an object has a single reference, and so does its first item.
|
||||||
|
Patch by Yonatan Goldschmidt.
|
|
@ -2379,9 +2379,16 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (derived == cls)
|
if (derived == cls) {
|
||||||
|
Py_XDECREF(bases); /* See below comment */
|
||||||
return 1;
|
return 1;
|
||||||
bases = abstract_get_bases(derived);
|
}
|
||||||
|
/* Use XSETREF to drop bases reference *after* finishing with
|
||||||
|
derived; bases might be the only reference to it.
|
||||||
|
XSETREF is used instead of SETREF, because bases is NULL on the
|
||||||
|
first iteration of the loop.
|
||||||
|
*/
|
||||||
|
Py_XSETREF(bases, abstract_get_bases(derived));
|
||||||
if (bases == NULL) {
|
if (bases == NULL) {
|
||||||
if (PyErr_Occurred())
|
if (PyErr_Occurred())
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2395,7 +2402,6 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
|
||||||
/* Avoid recursivity in the single inheritance case */
|
/* Avoid recursivity in the single inheritance case */
|
||||||
if (n == 1) {
|
if (n == 1) {
|
||||||
derived = PyTuple_GET_ITEM(bases, 0);
|
derived = PyTuple_GET_ITEM(bases, 0);
|
||||||
Py_DECREF(bases);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
|
|
Loading…
Reference in New Issue