Skip to content

Commit 43a0137

Browse files
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. (cherry picked from commit 1c56f8f) Co-authored-by: Yonatan Goldschmidt <[email protected]>
1 parent 00e4587 commit 43a0137

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
@@ -568,6 +568,7 @@ Karan Goel
568568
Jeroen Van Goey
569569
Christoph Gohlke
570570
Tim Golden
571+
Yonatan Goldschmidt
571572
Mark Gollahon
572573
Guilherme Gonçalves
573574
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
@@ -2287,9 +2287,16 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
22872287
int r = 0;
22882288

22892289
while (1) {
2290-
if (derived == cls)
2290+
if (derived == cls) {
2291+
Py_XDECREF(bases); /* See below comment */
22912292
return 1;
2292-
bases = abstract_get_bases(derived);
2293+
}
2294+
/* Use XSETREF to drop bases reference *after* finishing with
2295+
derived; bases might be the only reference to it.
2296+
XSETREF is used instead of SETREF, because bases is NULL on the
2297+
first iteration of the loop.
2298+
*/
2299+
Py_XSETREF(bases, abstract_get_bases(derived));
22932300
if (bases == NULL) {
22942301
if (PyErr_Occurred())
22952302
return -1;
@@ -2303,7 +2310,6 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
23032310
/* Avoid recursivity in the single inheritance case */
23042311
if (n == 1) {
23052312
derived = PyTuple_GET_ITEM(bases, 0);
2306-
Py_DECREF(bases);
23072313
continue;
23082314
}
23092315
for (i = 0; i < n; i++) {

0 commit comments

Comments
 (0)