mirror of
https://github.com/python/cpython.git
synced 2025-07-15 23:35:23 +00:00
bpo-31095: fix potential crash during GC (GH-2974)
This commit is contained in:
parent
bf9075a0c5
commit
a6296d34a4
14 changed files with 60 additions and 13 deletions
|
@ -728,8 +728,9 @@ functions. With :c:func:`Py_VISIT`, :c:func:`Noddy_traverse` can be simplified:
|
||||||
uniformity across these boring implementations.
|
uniformity across these boring implementations.
|
||||||
|
|
||||||
We also need to provide a method for clearing any subobjects that can
|
We also need to provide a method for clearing any subobjects that can
|
||||||
participate in cycles. We implement the method and reimplement the deallocator
|
participate in cycles.
|
||||||
to use it::
|
|
||||||
|
::
|
||||||
|
|
||||||
static int
|
static int
|
||||||
Noddy_clear(Noddy *self)
|
Noddy_clear(Noddy *self)
|
||||||
|
@ -747,13 +748,6 @@ to use it::
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
Noddy_dealloc(Noddy* self)
|
|
||||||
{
|
|
||||||
Noddy_clear(self);
|
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
|
||||||
}
|
|
||||||
|
|
||||||
Notice the use of a temporary variable in :c:func:`Noddy_clear`. We use the
|
Notice the use of a temporary variable in :c:func:`Noddy_clear`. We use the
|
||||||
temporary variable so that we can set each member to *NULL* before decrementing
|
temporary variable so that we can set each member to *NULL* before decrementing
|
||||||
its reference count. We do this because, as was discussed earlier, if the
|
its reference count. We do this because, as was discussed earlier, if the
|
||||||
|
@ -776,6 +770,23 @@ be simplified::
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Note that :c:func:`Noddy_dealloc` may call arbitrary functions through
|
||||||
|
``__del__`` method or weakref callback. It means circular GC can be
|
||||||
|
triggered inside the function. Since GC assumes reference count is not zero,
|
||||||
|
we need to untrack the object from GC by calling :c:func:`PyObject_GC_UnTrack`
|
||||||
|
before clearing members. Here is reimplemented deallocator which uses
|
||||||
|
:c:func:`PyObject_GC_UnTrack` and :c:func:`Noddy_clear`.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
static void
|
||||||
|
Noddy_dealloc(Noddy* self)
|
||||||
|
{
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
|
Noddy_clear(self);
|
||||||
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
|
}
|
||||||
|
|
||||||
Finally, we add the :const:`Py_TPFLAGS_HAVE_GC` flag to the class flags::
|
Finally, we add the :const:`Py_TPFLAGS_HAVE_GC` flag to the class flags::
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
||||||
|
|
|
@ -46,6 +46,7 @@ Noddy_clear(Noddy *self)
|
||||||
static void
|
static void
|
||||||
Noddy_dealloc(Noddy* self)
|
Noddy_dealloc(Noddy* self)
|
||||||
{
|
{
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
Noddy_clear(self);
|
Noddy_clear(self);
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix potential crash during GC caused by ``tp_dealloc`` which doesn't call
|
||||||
|
``PyObject_GC_UnTrack()``.
|
|
@ -1717,6 +1717,8 @@ dequeiter_traverse(dequeiterobject *dio, visitproc visit, void *arg)
|
||||||
static void
|
static void
|
||||||
dequeiter_dealloc(dequeiterobject *dio)
|
dequeiter_dealloc(dequeiterobject *dio)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(dio);
|
||||||
Py_XDECREF(dio->deque);
|
Py_XDECREF(dio->deque);
|
||||||
PyObject_GC_Del(dio);
|
PyObject_GC_Del(dio);
|
||||||
}
|
}
|
||||||
|
@ -2097,6 +2099,8 @@ static PyMemberDef defdict_members[] = {
|
||||||
static void
|
static void
|
||||||
defdict_dealloc(defdictobject *dd)
|
defdict_dealloc(defdictobject *dd)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(dd);
|
||||||
Py_CLEAR(dd->default_factory);
|
Py_CLEAR(dd->default_factory);
|
||||||
PyDict_Type.tp_dealloc((PyObject *)dd);
|
PyDict_Type.tp_dealloc((PyObject *)dd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -627,6 +627,7 @@ element_gc_clear(ElementObject *self)
|
||||||
static void
|
static void
|
||||||
element_dealloc(ElementObject* self)
|
element_dealloc(ElementObject* self)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
PyObject_GC_UnTrack(self);
|
PyObject_GC_UnTrack(self);
|
||||||
Py_TRASHCAN_SAFE_BEGIN(self)
|
Py_TRASHCAN_SAFE_BEGIN(self)
|
||||||
|
|
||||||
|
@ -2076,6 +2077,8 @@ elementiter_dealloc(ElementIterObject *it)
|
||||||
{
|
{
|
||||||
Py_ssize_t i = it->parent_stack_used;
|
Py_ssize_t i = it->parent_stack_used;
|
||||||
it->parent_stack_used = 0;
|
it->parent_stack_used = 0;
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(it);
|
||||||
while (i--)
|
while (i--)
|
||||||
Py_XDECREF(it->parent_stack[i].parent);
|
Py_XDECREF(it->parent_stack[i].parent);
|
||||||
PyMem_Free(it->parent_stack);
|
PyMem_Free(it->parent_stack);
|
||||||
|
@ -2083,7 +2086,6 @@ elementiter_dealloc(ElementIterObject *it)
|
||||||
Py_XDECREF(it->sought_tag);
|
Py_XDECREF(it->sought_tag);
|
||||||
Py_XDECREF(it->root_element);
|
Py_XDECREF(it->root_element);
|
||||||
|
|
||||||
PyObject_GC_UnTrack(it);
|
|
||||||
PyObject_GC_Del(it);
|
PyObject_GC_Del(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
||||||
static void
|
static void
|
||||||
partial_dealloc(partialobject *pto)
|
partial_dealloc(partialobject *pto)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
PyObject_GC_UnTrack(pto);
|
PyObject_GC_UnTrack(pto);
|
||||||
if (pto->weakreflist != NULL)
|
if (pto->weakreflist != NULL)
|
||||||
PyObject_ClearWeakRefs((PyObject *) pto);
|
PyObject_ClearWeakRefs((PyObject *) pto);
|
||||||
|
@ -1073,7 +1074,11 @@ lru_cache_clear_list(lru_list_elem *link)
|
||||||
static void
|
static void
|
||||||
lru_cache_dealloc(lru_cache_object *obj)
|
lru_cache_dealloc(lru_cache_object *obj)
|
||||||
{
|
{
|
||||||
lru_list_elem *list = lru_cache_unlink_list(obj);
|
lru_list_elem *list;
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(obj);
|
||||||
|
|
||||||
|
list = lru_cache_unlink_list(obj);
|
||||||
Py_XDECREF(obj->maxsize_O);
|
Py_XDECREF(obj->maxsize_O);
|
||||||
Py_XDECREF(obj->func);
|
Py_XDECREF(obj->func);
|
||||||
Py_XDECREF(obj->cache);
|
Py_XDECREF(obj->cache);
|
||||||
|
|
|
@ -1084,6 +1084,8 @@ bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg)
|
||||||
static void
|
static void
|
||||||
bytesiobuf_dealloc(bytesiobuf *self)
|
bytesiobuf_dealloc(bytesiobuf *self)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
Py_CLEAR(self->source);
|
Py_CLEAR(self->source);
|
||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
|
|
@ -655,7 +655,8 @@ py_encode_basestring(PyObject* self UNUSED, PyObject *pystr)
|
||||||
static void
|
static void
|
||||||
scanner_dealloc(PyObject *self)
|
scanner_dealloc(PyObject *self)
|
||||||
{
|
{
|
||||||
/* Deallocate scanner object */
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
scanner_clear(self);
|
scanner_clear(self);
|
||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
@ -1778,7 +1779,8 @@ bail:
|
||||||
static void
|
static void
|
||||||
encoder_dealloc(PyObject *self)
|
encoder_dealloc(PyObject *self)
|
||||||
{
|
{
|
||||||
/* Deallocate Encoder */
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
encoder_clear(self);
|
encoder_clear(self);
|
||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2778,6 +2778,8 @@ context_clear(PySSLContext *self)
|
||||||
static void
|
static void
|
||||||
context_dealloc(PySSLContext *self)
|
context_dealloc(PySSLContext *self)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
context_clear(self);
|
context_clear(self);
|
||||||
SSL_CTX_free(self->ctx);
|
SSL_CTX_free(self->ctx);
|
||||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
|
@ -4292,6 +4294,7 @@ static PyTypeObject PySSLMemoryBIO_Type = {
|
||||||
static void
|
static void
|
||||||
PySSLSession_dealloc(PySSLSession *self)
|
PySSLSession_dealloc(PySSLSession *self)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
PyObject_GC_UnTrack(self);
|
PyObject_GC_UnTrack(self);
|
||||||
Py_XDECREF(self->ctx);
|
Py_XDECREF(self->ctx);
|
||||||
if (self->session != NULL) {
|
if (self->session != NULL) {
|
||||||
|
|
|
@ -1589,6 +1589,8 @@ typedef struct {
|
||||||
static void
|
static void
|
||||||
unpackiter_dealloc(unpackiterobject *self)
|
unpackiter_dealloc(unpackiterobject *self)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
Py_XDECREF(self->so);
|
Py_XDECREF(self->so);
|
||||||
PyBuffer_Release(&self->buf);
|
PyBuffer_Release(&self->buf);
|
||||||
PyObject_GC_Del(self);
|
PyObject_GC_Del(self);
|
||||||
|
|
|
@ -1847,6 +1847,8 @@ dict_dealloc(PyDictObject *mp)
|
||||||
PyObject **values = mp->ma_values;
|
PyObject **values = mp->ma_values;
|
||||||
PyDictKeysObject *keys = mp->ma_keys;
|
PyDictKeysObject *keys = mp->ma_keys;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i, n;
|
||||||
|
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
PyObject_GC_UnTrack(mp);
|
PyObject_GC_UnTrack(mp);
|
||||||
Py_TRASHCAN_SAFE_BEGIN(mp)
|
Py_TRASHCAN_SAFE_BEGIN(mp)
|
||||||
if (values != NULL) {
|
if (values != NULL) {
|
||||||
|
@ -3270,6 +3272,8 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
|
||||||
static void
|
static void
|
||||||
dictiter_dealloc(dictiterobject *di)
|
dictiter_dealloc(dictiterobject *di)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
_PyObject_GC_UNTRACK(di);
|
||||||
Py_XDECREF(di->di_dict);
|
Py_XDECREF(di->di_dict);
|
||||||
Py_XDECREF(di->di_result);
|
Py_XDECREF(di->di_result);
|
||||||
PyObject_GC_Del(di);
|
PyObject_GC_Del(di);
|
||||||
|
@ -3629,6 +3633,8 @@ dictiter_reduce(dictiterobject *di)
|
||||||
static void
|
static void
|
||||||
dictview_dealloc(_PyDictViewObject *dv)
|
dictview_dealloc(_PyDictViewObject *dv)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
_PyObject_GC_UNTRACK(dv);
|
||||||
Py_XDECREF(dv->dv_dict);
|
Py_XDECREF(dv->dv_dict);
|
||||||
PyObject_GC_Del(dv);
|
PyObject_GC_Del(dv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -553,6 +553,7 @@ set_dealloc(PySetObject *so)
|
||||||
setentry *entry;
|
setentry *entry;
|
||||||
Py_ssize_t used = so->used;
|
Py_ssize_t used = so->used;
|
||||||
|
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
PyObject_GC_UnTrack(so);
|
PyObject_GC_UnTrack(so);
|
||||||
Py_TRASHCAN_SAFE_BEGIN(so)
|
Py_TRASHCAN_SAFE_BEGIN(so)
|
||||||
if (so->weakreflist != NULL)
|
if (so->weakreflist != NULL)
|
||||||
|
@ -809,6 +810,8 @@ typedef struct {
|
||||||
static void
|
static void
|
||||||
setiter_dealloc(setiterobject *si)
|
setiter_dealloc(setiterobject *si)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
_PyObject_GC_UNTRACK(si);
|
||||||
Py_XDECREF(si->si_set);
|
Py_XDECREF(si->si_set);
|
||||||
PyObject_GC_Del(si);
|
PyObject_GC_Del(si);
|
||||||
}
|
}
|
||||||
|
|
|
@ -633,6 +633,8 @@ typedef struct {
|
||||||
static void
|
static void
|
||||||
ast_dealloc(AST_object *self)
|
ast_dealloc(AST_object *self)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
Py_CLEAR(self->dict);
|
Py_CLEAR(self->dict);
|
||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
|
|
@ -520,6 +520,8 @@ typedef struct {
|
||||||
static void
|
static void
|
||||||
ast_dealloc(AST_object *self)
|
ast_dealloc(AST_object *self)
|
||||||
{
|
{
|
||||||
|
/* bpo-31095: UnTrack is needed before calling any callbacks */
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
Py_CLEAR(self->dict);
|
Py_CLEAR(self->dict);
|
||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue