Skip to content

Commit 1c56f8f

Browse files
authored
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.
1 parent a025d4c commit 1c56f8f

File tree

4 files changed

+34
-3
lines changed

4 files changed

+34
-3
lines changed

Lib/test/test_isinstance.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,27 @@ def test_isinstance_recursion_limit(self):
251251
# blown
252252
self.assertRaises(RecursionError, blowstack, isinstance, '', str)
253253

254+
def test_issubclass_refcount_handling(self):
255+
# bpo-39382: abstract_issubclass() didn't hold item reference while
256+
# peeking in the bases tuple, in the single inheritance case.
257+
class A:
258+
@property
259+
def __bases__(self):
260+
return (int, )
261+
262+
class B:
263+
def __init__(self):
264+
# setting this here increases the chances of exhibiting the bug,
265+
# probably due to memory layout changes.
266+
self.x = 1
267+
268+
@property
269+
def __bases__(self):
270+
return (A(), )
271+
272+
self.assertEqual(True, issubclass(B(), int))
273+
274+
254275
def blowstack(fxn, arg, compare_to):
255276
# Make sure that calling isinstance with a deeply nested tuple for its
256277
# argument will raise RecursionError eventually.

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ Karan Goel
594594
Jeroen Van Goey
595595
Christoph Gohlke
596596
Tim Golden
597+
Yonatan Goldschmidt
597598
Mark Gollahon
598599
Guilherme Gonçalves
599600
Tiago Gonçalves
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a use-after-free in the single inheritance path of ``issubclass()``, when
2+
the ``__bases__`` of an object has a single reference, and so does its first item.
3+
Patch by Yonatan Goldschmidt.

Objects/abstract.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,9 +2379,16 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
23792379
int r = 0;
23802380

23812381
while (1) {
2382-
if (derived == cls)
2382+
if (derived == cls) {
2383+
Py_XDECREF(bases); /* See below comment */
23832384
return 1;
2384-
bases = abstract_get_bases(derived);
2385+
}
2386+
/* Use XSETREF to drop bases reference *after* finishing with
2387+
derived; bases might be the only reference to it.
2388+
XSETREF is used instead of SETREF, because bases is NULL on the
2389+
first iteration of the loop.
2390+
*/
2391+
Py_XSETREF(bases, abstract_get_bases(derived));
23852392
if (bases == NULL) {
23862393
if (PyErr_Occurred())
23872394
return -1;
@@ -2395,7 +2402,6 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
23952402
/* Avoid recursivity in the single inheritance case */
23962403
if (n == 1) {
23972404
derived = PyTuple_GET_ITEM(bases, 0);
2398-
Py_DECREF(bases);
23992405
continue;
24002406
}
24012407
for (i = 0; i < n; i++) {

0 commit comments

Comments
 (0)