Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2eea952

Browse filesBrowse files
authored
bpo-31095: fix potential crash during GC (GH-3195)
(cherry picked from commit a6296d3)
1 parent 7d8282d commit 2eea952
Copy full SHA for 2eea952

File tree

Expand file treeCollapse file tree

14 files changed

+60
-13
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+60
-13
lines changed

‎Doc/extending/newtypes.rst

Copy file name to clipboardExpand all lines: Doc/extending/newtypes.rst
+20-9Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -728,8 +728,9 @@ functions. With :c:func:`Py_VISIT`, :c:func:`Noddy_traverse` can be simplified:
728728
uniformity across these boring implementations.
729729

730730
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+
::
733734

734735
static int
735736
Noddy_clear(Noddy *self)
@@ -747,13 +748,6 @@ to use it::
747748
return 0;
748749
}
749750

750-
static void
751-
Noddy_dealloc(Noddy* self)
752-
{
753-
Noddy_clear(self);
754-
Py_TYPE(self)->tp_free((PyObject*)self);
755-
}
756-
757751
Notice the use of a temporary variable in :c:func:`Noddy_clear`. We use the
758752
temporary variable so that we can set each member to *NULL* before decrementing
759753
its reference count. We do this because, as was discussed earlier, if the
@@ -776,6 +770,23 @@ be simplified::
776770
return 0;
777771
}
778772

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+
779790
Finally, we add the :const:`Py_TPFLAGS_HAVE_GC` flag to the class flags::
780791

781792
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */

‎Doc/includes/noddy4.c

Copy file name to clipboardExpand all lines: Doc/includes/noddy4.c
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Noddy_clear(Noddy *self)
4646
static void
4747
Noddy_dealloc(Noddy* self)
4848
{
49+
PyObject_GC_UnTrack(self);
4950
Noddy_clear(self);
5051
Py_TYPE(self)->tp_free((PyObject*)self);
5152
}
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix potential crash during GC caused by ``tp_dealloc`` which doesn't call
2+
``PyObject_GC_UnTrack()``.

‎Modules/_collectionsmodule.c

Copy file name to clipboardExpand all lines: Modules/_collectionsmodule.c
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,6 +1706,8 @@ dequeiter_traverse(dequeiterobject *dio, visitproc visit, void *arg)
17061706
static void
17071707
dequeiter_dealloc(dequeiterobject *dio)
17081708
{
1709+
/* bpo-31095: UnTrack is needed before calling any callbacks */
1710+
PyObject_GC_UnTrack(dio);
17091711
Py_XDECREF(dio->deque);
17101712
PyObject_GC_Del(dio);
17111713
}
@@ -2086,6 +2088,8 @@ static PyMemberDef defdict_members[] = {
20862088
static void
20872089
defdict_dealloc(defdictobject *dd)
20882090
{
2091+
/* bpo-31095: UnTrack is needed before calling any callbacks */
2092+
PyObject_GC_UnTrack(dd);
20892093
Py_CLEAR(dd->default_factory);
20902094
PyDict_Type.tp_dealloc((PyObject *)dd);
20912095
}

‎Modules/_elementtree.c

Copy file name to clipboardExpand all lines: Modules/_elementtree.c
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ element_gc_clear(ElementObject *self)
627627
static void
628628
element_dealloc(ElementObject* self)
629629
{
630+
/* bpo-31095: UnTrack is needed before calling any callbacks */
630631
PyObject_GC_UnTrack(self);
631632
Py_TRASHCAN_SAFE_BEGIN(self)
632633

@@ -2048,14 +2049,15 @@ elementiter_dealloc(ElementIterObject *it)
20482049
{
20492050
Py_ssize_t i = it->parent_stack_used;
20502051
it->parent_stack_used = 0;
2052+
/* bpo-31095: UnTrack is needed before calling any callbacks */
2053+
PyObject_GC_UnTrack(it);
20512054
while (i--)
20522055
Py_XDECREF(it->parent_stack[i].parent);
20532056
PyMem_Free(it->parent_stack);
20542057

20552058
Py_XDECREF(it->sought_tag);
20562059
Py_XDECREF(it->root_element);
20572060

2058-
PyObject_GC_UnTrack(it);
20592061
PyObject_GC_Del(it);
20602062
}
20612063

‎Modules/_functoolsmodule.c

Copy file name to clipboardExpand all lines: Modules/_functoolsmodule.c
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
116116
static void
117117
partial_dealloc(partialobject *pto)
118118
{
119+
/* bpo-31095: UnTrack is needed before calling any callbacks */
119120
PyObject_GC_UnTrack(pto);
120121
if (pto->weakreflist != NULL)
121122
PyObject_ClearWeakRefs((PyObject *) pto);
@@ -1038,7 +1039,11 @@ lru_cache_clear_list(lru_list_elem *link)
10381039
static void
10391040
lru_cache_dealloc(lru_cache_object *obj)
10401041
{
1041-
lru_list_elem *list = lru_cache_unlink_list(obj);
1042+
lru_list_elem *list;
1043+
/* bpo-31095: UnTrack is needed before calling any callbacks */
1044+
PyObject_GC_UnTrack(obj);
1045+
1046+
list = lru_cache_unlink_list(obj);
10421047
Py_XDECREF(obj->maxsize_O);
10431048
Py_XDECREF(obj->func);
10441049
Py_XDECREF(obj->cache);

‎Modules/_io/bytesio.c

Copy file name to clipboardExpand all lines: Modules/_io/bytesio.c
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,8 @@ bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg)
11311131
static void
11321132
bytesiobuf_dealloc(bytesiobuf *self)
11331133
{
1134+
/* bpo-31095: UnTrack is needed before calling any callbacks */
1135+
PyObject_GC_UnTrack(self);
11341136
Py_CLEAR(self->source);
11351137
Py_TYPE(self)->tp_free(self);
11361138
}

‎Modules/_json.c

Copy file name to clipboardExpand all lines: Modules/_json.c
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,8 @@ py_encode_basestring(PyObject* self UNUSED, PyObject *pystr)
655655
static void
656656
scanner_dealloc(PyObject *self)
657657
{
658-
/* Deallocate scanner object */
658+
/* bpo-31095: UnTrack is needed before calling any callbacks */
659+
PyObject_GC_UnTrack(self);
659660
scanner_clear(self);
660661
Py_TYPE(self)->tp_free(self);
661662
}
@@ -1793,7 +1794,8 @@ encoder_listencode_list(PyEncoderObject *s, _PyAccu *acc,
17931794
static void
17941795
encoder_dealloc(PyObject *self)
17951796
{
1796-
/* Deallocate Encoder */
1797+
/* bpo-31095: UnTrack is needed before calling any callbacks */
1798+
PyObject_GC_UnTrack(self);
17971799
encoder_clear(self);
17981800
Py_TYPE(self)->tp_free(self);
17991801
}

‎Modules/_ssl.c

Copy file name to clipboardExpand all lines: Modules/_ssl.c
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2776,6 +2776,8 @@ context_clear(PySSLContext *self)
27762776
static void
27772777
context_dealloc(PySSLContext *self)
27782778
{
2779+
/* bpo-31095: UnTrack is needed before calling any callbacks */
2780+
PyObject_GC_UnTrack(self);
27792781
context_clear(self);
27802782
SSL_CTX_free(self->ctx);
27812783
#ifdef OPENSSL_NPN_NEGOTIATED
@@ -4284,6 +4286,7 @@ static PyTypeObject PySSLMemoryBIO_Type = {
42844286
static void
42854287
PySSLSession_dealloc(PySSLSession *self)
42864288
{
4289+
/* bpo-31095: UnTrack is needed before calling any callbacks */
42874290
PyObject_GC_UnTrack(self);
42884291
Py_XDECREF(self->ctx);
42894292
if (self->session != NULL) {

‎Modules/_struct.c

Copy file name to clipboardExpand all lines: Modules/_struct.c
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,6 +1605,8 @@ typedef struct {
16051605
static void
16061606
unpackiter_dealloc(unpackiterobject *self)
16071607
{
1608+
/* bpo-31095: UnTrack is needed before calling any callbacks */
1609+
PyObject_GC_UnTrack(self);
16081610
Py_XDECREF(self->so);
16091611
PyBuffer_Release(&self->buf);
16101612
PyObject_GC_Del(self);

‎Objects/dictobject.c

Copy file name to clipboardExpand all lines: Objects/dictobject.c
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,6 +2006,8 @@ dict_dealloc(PyDictObject *mp)
20062006
PyObject **values = mp->ma_values;
20072007
PyDictKeysObject *keys = mp->ma_keys;
20082008
Py_ssize_t i, n;
2009+
2010+
/* bpo-31095: UnTrack is needed before calling any callbacks */
20092011
PyObject_GC_UnTrack(mp);
20102012
Py_TRASHCAN_SAFE_BEGIN(mp)
20112013
if (values != NULL) {
@@ -3432,6 +3434,8 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
34323434
static void
34333435
dictiter_dealloc(dictiterobject *di)
34343436
{
3437+
/* bpo-31095: UnTrack is needed before calling any callbacks */
3438+
_PyObject_GC_UNTRACK(di);
34353439
Py_XDECREF(di->di_dict);
34363440
Py_XDECREF(di->di_result);
34373441
PyObject_GC_Del(di);
@@ -3800,6 +3804,8 @@ dictiter_reduce(dictiterobject *di)
38003804
static void
38013805
dictview_dealloc(_PyDictViewObject *dv)
38023806
{
3807+
/* bpo-31095: UnTrack is needed before calling any callbacks */
3808+
_PyObject_GC_UNTRACK(dv);
38033809
Py_XDECREF(dv->dv_dict);
38043810
PyObject_GC_Del(dv);
38053811
}

‎Objects/setobject.c

Copy file name to clipboardExpand all lines: Objects/setobject.c
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ set_dealloc(PySetObject *so)
556556
setentry *entry;
557557
Py_ssize_t used = so->used;
558558

559+
/* bpo-31095: UnTrack is needed before calling any callbacks */
559560
PyObject_GC_UnTrack(so);
560561
Py_TRASHCAN_SAFE_BEGIN(so)
561562
if (so->weakreflist != NULL)
@@ -812,6 +813,8 @@ typedef struct {
812813
static void
813814
setiter_dealloc(setiterobject *si)
814815
{
816+
/* bpo-31095: UnTrack is needed before calling any callbacks */
817+
_PyObject_GC_UNTRACK(si);
815818
Py_XDECREF(si->si_set);
816819
PyObject_GC_Del(si);
817820
}

‎Parser/asdl_c.py

Copy file name to clipboardExpand all lines: Parser/asdl_c.py
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,8 @@ def visitModule(self, mod):
630630
static void
631631
ast_dealloc(AST_object *self)
632632
{
633+
/* bpo-31095: UnTrack is needed before calling any callbacks */
634+
PyObject_GC_UnTrack(self);
633635
Py_CLEAR(self->dict);
634636
Py_TYPE(self)->tp_free(self);
635637
}

‎Python/Python-ast.c

Copy file name to clipboardExpand all lines: Python/Python-ast.c
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@ typedef struct {
512512
static void
513513
ast_dealloc(AST_object *self)
514514
{
515+
/* bpo-31095: UnTrack is needed before calling any callbacks */
516+
PyObject_GC_UnTrack(self);
515517
Py_CLEAR(self->dict);
516518
Py_TYPE(self)->tp_free(self);
517519
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.