mirror of https://github.com/python/cpython.git
Fix SF bug 574207 (chained __slots__ dealloc segfault).
This is inspired by SF patch 581742 (by Jonathan Hogg, who also submitted the bug report, and two other suggested patches), but separates the non-GC case from the GC case to avoid testing for GC several times. Had to fix an assert() from call_finalizer() that asserted that the object wasn't untracked, because it's possible that the object isn't GC'ed!
This commit is contained in:
parent
31f3db39f3
commit
22b1387c51
|
@ -401,7 +401,8 @@ call_finalizer(PyObject *self)
|
|||
_Py_NewReference(self);
|
||||
self->ob_refcnt = refcnt;
|
||||
}
|
||||
assert(_Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
|
||||
assert(!PyType_IS_GC(self->ob_type) ||
|
||||
_Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
|
||||
/* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal, but
|
||||
* _Py_NewReference bumped it again, so that's a wash.
|
||||
* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
|
||||
|
@ -423,14 +424,55 @@ subtype_dealloc(PyObject *self)
|
|||
PyTypeObject *type, *base;
|
||||
destructor basedealloc;
|
||||
|
||||
/* This exists so we can DECREF self->ob_type */
|
||||
/* Extract the type; we expect it to be a heap type */
|
||||
type = self->ob_type;
|
||||
assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
|
||||
|
||||
/* Test whether the type has GC exactly once */
|
||||
|
||||
if (!PyType_IS_GC(type)) {
|
||||
/* It's really rare to find a dynamic type that doesn't have
|
||||
GC; it can only happen when deriving from 'object' and not
|
||||
adding any slots or instance variables. This allows
|
||||
certain simplifications: there's no need to call
|
||||
clear_slots(), or DECREF the dict, or clear weakrefs. */
|
||||
|
||||
/* Maybe call finalizer; exit early if resurrected */
|
||||
if (call_finalizer(self) < 0)
|
||||
return;
|
||||
|
||||
/* Find the nearest base with a different tp_dealloc */
|
||||
base = type;
|
||||
while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
|
||||
assert(base->ob_size == 0);
|
||||
base = base->tp_base;
|
||||
assert(base);
|
||||
}
|
||||
|
||||
/* Call the base tp_dealloc() */
|
||||
assert(basedealloc);
|
||||
basedealloc(self);
|
||||
|
||||
/* Can't reference self beyond this point */
|
||||
Py_DECREF(type);
|
||||
|
||||
/* Done */
|
||||
return;
|
||||
}
|
||||
|
||||
/* We get here only if the type has GC */
|
||||
|
||||
/* UnTrack and re-Track around the trashcan macro, alas */
|
||||
_PyObject_GC_UNTRACK(self);
|
||||
Py_TRASHCAN_SAFE_BEGIN(self);
|
||||
_PyObject_GC_TRACK(self); /* We'll untrack for real later */
|
||||
|
||||
/* Maybe call finalizer; exit early if resurrected */
|
||||
if (call_finalizer(self) < 0)
|
||||
return;
|
||||
|
||||
/* Find the nearest base with a different tp_dealloc
|
||||
and clear slots while we're at it */
|
||||
type = self->ob_type;
|
||||
base = type;
|
||||
while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
|
||||
if (base->ob_size)
|
||||
|
@ -456,7 +498,7 @@ subtype_dealloc(PyObject *self)
|
|||
PyObject_ClearWeakRefs(self);
|
||||
|
||||
/* Finalize GC if the base doesn't do GC and we do */
|
||||
if (PyType_IS_GC(type) && !PyType_IS_GC(base))
|
||||
if (!PyType_IS_GC(base))
|
||||
_PyObject_GC_UNTRACK(self);
|
||||
|
||||
/* Call the base tp_dealloc() */
|
||||
|
@ -464,9 +506,9 @@ subtype_dealloc(PyObject *self)
|
|||
basedealloc(self);
|
||||
|
||||
/* Can't reference self beyond this point */
|
||||
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
|
||||
Py_DECREF(type);
|
||||
}
|
||||
|
||||
Py_TRASHCAN_SAFE_END(self);
|
||||
}
|
||||
|
||||
static PyTypeObject *solid_base(PyTypeObject *type);
|
||||
|
|
Loading…
Reference in New Issue