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 | } |