bpo-31095: fix potential crash during GC (GH-2974) (#3196) · python/cpython@0fcc033 (original) (raw)

14 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -728,8 +728,9 @@ functions. With :c:func:`Py_VISIT`, :c:func:`Noddy_traverse` can be simplified:
728 728 uniformity across these boring implementations.
729 729
730 730 We also need to provide a method for clearing any subobjects that can
731 -participate in cycles. We implement the method and reimplement the deallocator
732 -to use it::
731 +participate in cycles.
732 +
733 +::
733 734
734 735 static int
735 736 Noddy_clear(Noddy *self)
@@ -747,13 +748,6 @@ to use it::
747 748 return 0;
748 749 }
749 750
750 - static void
751 - Noddy_dealloc(Noddy* self)
752 - {
753 - Noddy_clear(self);
754 - Py_TYPE(self)->tp_free((PyObject*)self);
755 - }
756 -
757 751 Notice the use of a temporary variable in :c:func:`Noddy_clear`. We use the
758 752 temporary variable so that we can set each member to *NULL* before decrementing
759 753 its reference count. We do this because, as was discussed earlier, if the
@@ -776,6 +770,23 @@ be simplified::
776 770 return 0;
777 771 }
778 772
773 +Note that :c:func:`Noddy_dealloc` may call arbitrary functions through
774 +``__del__`` method or weakref callback. It means circular GC can be
775 +triggered inside the function. Since GC assumes reference count is not zero,
776 +we need to untrack the object from GC by calling :c:func:`PyObject_GC_UnTrack`
777 +before clearing members. Here is reimplemented deallocator which uses
778 +:c:func:`PyObject_GC_UnTrack` and :c:func:`Noddy_clear`.
779 +
780 +::
781 +
782 + static void
783 + Noddy_dealloc(Noddy* self)
784 + {
785 + PyObject_GC_UnTrack(self);
786 + Noddy_clear(self);
787 + Py_TYPE(self)->tp_free((PyObject*)self);
788 + }
789 +
779 790 Finally, we add the :const:`Py_TPFLAGS_HAVE_GC` flag to the class flags::
780 791
781 792 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE Py_TPFLAGS_HAVE_GC, /* tp_flags */
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@ Noddy_clear(Noddy *self)
46 46 static void
47 47 Noddy_dealloc(Noddy* self)
48 48 {
49 +PyObject_GC_UnTrack(self);
49 50 Noddy_clear(self);
50 51 Py_TYPE(self)->tp_free((PyObject*)self);
51 52 }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1 +Fix potential crash during GC caused by ``tp_dealloc`` which doesn't call
2 +``PyObject_GC_UnTrack()``.
Original file line number Diff line number Diff line change
@@ -1641,6 +1641,8 @@ dequeiter_traverse(dequeiterobject *dio, visitproc visit, void *arg)
1641 1641 static void
1642 1642 dequeiter_dealloc(dequeiterobject *dio)
1643 1643 {
1644 +/* bpo-31095: UnTrack is needed before calling any callbacks */
1645 +PyObject_GC_UnTrack(dio);
1644 1646 Py_XDECREF(dio->deque);
1645 1647 PyObject_GC_Del(dio);
1646 1648 }
@@ -2021,6 +2023,8 @@ static PyMemberDef defdict_members[] = {
2021 2023 static void
2022 2024 defdict_dealloc(defdictobject *dd)
2023 2025 {
2026 +/* bpo-31095: UnTrack is needed before calling any callbacks */
2027 +PyObject_GC_UnTrack(dd);
2024 2028 Py_CLEAR(dd->default_factory);
2025 2029 PyDict_Type.tp_dealloc((PyObject *)dd);
2026 2030 }
Original file line number Diff line number Diff line change
@@ -653,6 +653,7 @@ element_gc_clear(ElementObject *self)
653 653 static void
654 654 element_dealloc(ElementObject* self)
655 655 {
656 +/* bpo-31095: UnTrack is needed before calling any callbacks */
656 657 PyObject_GC_UnTrack(self);
657 658 Py_TRASHCAN_SAFE_BEGIN(self)
658 659
@@ -2025,6 +2026,9 @@ static void
2025 2026 elementiter_dealloc(ElementIterObject *it)
2026 2027 {
2027 2028 ParentLocator *p = it->parent_stack;
2029 +/* bpo-31095: UnTrack is needed before calling any callbacks */
2030 +PyObject_GC_UnTrack(it);
2031 +
2028 2032 while (p) {
2029 2033 ParentLocator *temp = p;
2030 2034 Py_XDECREF(p->parent);
@@ -2034,8 +2038,6 @@ elementiter_dealloc(ElementIterObject *it)
2034 2038
2035 2039 Py_XDECREF(it->sought_tag);
2036 2040 Py_XDECREF(it->root_element);
2037 -
2038 -PyObject_GC_UnTrack(it);
2039 2041 PyObject_GC_Del(it);
2040 2042 }
2041 2043
Original file line number Diff line number Diff line change
@@ -116,6 +116,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
116 116 static void
117 117 partial_dealloc(partialobject *pto)
118 118 {
119 +/* bpo-31095: UnTrack is needed before calling any callbacks */
119 120 PyObject_GC_UnTrack(pto);
120 121 if (pto->weakreflist != NULL)
121 122 PyObject_ClearWeakRefs((PyObject *) pto);
@@ -1039,7 +1040,11 @@ lru_cache_clear_list(lru_list_elem *link)
1039 1040 static void
1040 1041 lru_cache_dealloc(lru_cache_object *obj)
1041 1042 {
1042 -lru_list_elem *list = lru_cache_unlink_list(obj);
1043 +lru_list_elem *list;
1044 +/* bpo-31095: UnTrack is needed before calling any callbacks */
1045 +PyObject_GC_UnTrack(obj);
1046 +
1047 +list = lru_cache_unlink_list(obj);
1043 1048 Py_XDECREF(obj->maxsize_O);
1044 1049 Py_XDECREF(obj->func);
1045 1050 Py_XDECREF(obj->cache);
Original file line number Diff line number Diff line change
@@ -1133,6 +1133,8 @@ bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg)
1133 1133 static void
1134 1134 bytesiobuf_dealloc(bytesiobuf *self)
1135 1135 {
1136 +/* bpo-31095: UnTrack is needed before calling any callbacks */
1137 +PyObject_GC_UnTrack(self);
1136 1138 Py_CLEAR(self->source);
1137 1139 Py_TYPE(self)->tp_free(self);
1138 1140 }
Original file line number Diff line number Diff line change
@@ -655,7 +655,8 @@ py_encode_basestring(PyObject* self UNUSED, PyObject *pystr)
655 655 static void
656 656 scanner_dealloc(PyObject *self)
657 657 {
658 -/* Deallocate scanner object */
658 +/* bpo-31095: UnTrack is needed before calling any callbacks */
659 +PyObject_GC_UnTrack(self);
659 660 scanner_clear(self);
660 661 Py_TYPE(self)->tp_free(self);
661 662 }
@@ -1793,7 +1794,8 @@ encoder_listencode_list(PyEncoderObject *s, _PyAccu *acc,
1793 1794 static void
1794 1795 encoder_dealloc(PyObject *self)
1795 1796 {
1796 -/* Deallocate Encoder */
1797 +/* bpo-31095: UnTrack is needed before calling any callbacks */
1798 +PyObject_GC_UnTrack(self);
1797 1799 encoder_clear(self);
1798 1800 Py_TYPE(self)->tp_free(self);
1799 1801 }
Original file line number Diff line number Diff line change
@@ -2465,6 +2465,8 @@ context_clear(PySSLContext *self)
2465 2465 static void
2466 2466 context_dealloc(PySSLContext *self)
2467 2467 {
2468 +/* bpo-31095: UnTrack is needed before calling any callbacks */
2469 +PyObject_GC_UnTrack(self);
2468 2470 context_clear(self);
2469 2471 SSL_CTX_free(self->ctx);
2470 2472 #ifdef OPENSSL_NPN_NEGOTIATED
Original file line number Diff line number Diff line change
@@ -1581,6 +1581,8 @@ typedef struct {
1581 1581 static void
1582 1582 unpackiter_dealloc(unpackiterobject *self)
1583 1583 {
1584 +/* bpo-31095: UnTrack is needed before calling any callbacks */
1585 +PyObject_GC_UnTrack(self);
1584 1586 Py_XDECREF(self->so);
1585 1587 PyBuffer_Release(&self->buf);
1586 1588 PyObject_GC_Del(self);
Original file line number Diff line number Diff line change
@@ -1636,6 +1636,8 @@ dict_dealloc(PyDictObject *mp)
1636 1636 PyObject **values = mp->ma_values;
1637 1637 PyDictKeysObject *keys = mp->ma_keys;
1638 1638 Py_ssize_t i, n;
1639 +
1640 +/* bpo-31095: UnTrack is needed before calling any callbacks */
1639 1641 PyObject_GC_UnTrack(mp);
1640 1642 Py_TRASHCAN_SAFE_BEGIN(mp)
1641 1643 if (values != NULL) {
@@ -2961,6 +2963,8 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
2961 2963 static void
2962 2964 dictiter_dealloc(dictiterobject *di)
2963 2965 {
2966 +/* bpo-31095: UnTrack is needed before calling any callbacks */
2967 +_PyObject_GC_UNTRACK(di);
2964 2968 Py_XDECREF(di->di_dict);
2965 2969 Py_XDECREF(di->di_result);
2966 2970 PyObject_GC_Del(di);
@@ -3319,6 +3323,8 @@ dictiter_reduce(dictiterobject *di)
3319 3323 static void
3320 3324 dictview_dealloc(_PyDictViewObject *dv)
3321 3325 {
3326 +/* bpo-31095: UnTrack is needed before calling any callbacks */
3327 +_PyObject_GC_UNTRACK(dv);
3322 3328 Py_XDECREF(dv->dv_dict);
3323 3329 PyObject_GC_Del(dv);
3324 3330 }
Original file line number Diff line number Diff line change
@@ -472,6 +472,7 @@ set_dealloc(PySetObject *so)
472 472 setentry *entry;
473 473 Py_ssize_t used = so->used;
474 474
475 +/* bpo-31095: UnTrack is needed before calling any callbacks */
475 476 PyObject_GC_UnTrack(so);
476 477 Py_TRASHCAN_SAFE_BEGIN(so)
477 478 if (so->weakreflist != NULL)
@@ -736,6 +737,8 @@ typedef struct {
736 737 static void
737 738 setiter_dealloc(setiterobject *si)
738 739 {
740 +/* bpo-31095: UnTrack is needed before calling any callbacks */
741 +_PyObject_GC_UNTRACK(si);
739 742 Py_XDECREF(si->si_set);
740 743 PyObject_GC_Del(si);
741 744 }
Original file line number Diff line number Diff line change
@@ -630,6 +630,8 @@ def visitModule(self, mod):
630 630 static void
631 631 ast_dealloc(AST_object *self)
632 632 {
633 + /* bpo-31095: UnTrack is needed before calling any callbacks */
634 + PyObject_GC_UnTrack(self);
633 635 Py_CLEAR(self->dict);
634 636 Py_TYPE(self)->tp_free(self);
635 637 }
Original file line number Diff line number Diff line change
@@ -486,6 +486,8 @@ typedef struct {
486 486 static void
487 487 ast_dealloc(AST_object *self)
488 488 {
489 +/* bpo-31095: UnTrack is needed before calling any callbacks */
490 +PyObject_GC_UnTrack(self);
489 491 Py_CLEAR(self->dict);
490 492 Py_TYPE(self)->tp_free(self);
491 493 }