Skip to content

Commit 1b18455

Browse files
authored
bpo-38392: PyObject_GC_Track() validates object in debug mode (GH-16615)
In debug mode, PyObject_GC_Track() now calls tp_traverse() of the object type to ensure that the object is valid: test that objects visited by tp_traverse() are valid. Fix pyexpat.c: only track the parser in the GC once the parser is fully initialized.
1 parent 7775349 commit 1b18455

File tree

4 files changed

+27
-4
lines changed

4 files changed

+27
-4
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
In debug mode, :c:func:`PyObject_GC_Track` now calls ``tp_traverse()`` of
2+
the object type to ensure that the object is valid: test that objects
3+
visited by ``tp_traverse()`` are valid.

Modules/_elementtree.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,8 +2317,6 @@ create_elementiter(ElementObject *self, PyObject *tag, int gettext)
23172317
Py_INCREF(self);
23182318
it->root_element = self;
23192319

2320-
PyObject_GC_Track(it);
2321-
23222320
it->parent_stack = PyMem_New(ParentLocator, INIT_PARENT_STACK_SIZE);
23232321
if (it->parent_stack == NULL) {
23242322
Py_DECREF(it);
@@ -2328,6 +2326,8 @@ create_elementiter(ElementObject *self, PyObject *tag, int gettext)
23282326
it->parent_stack_used = 0;
23292327
it->parent_stack_size = INIT_PARENT_STACK_SIZE;
23302328

2329+
PyObject_GC_Track(it);
2330+
23312331
return (PyObject *)it;
23322332
}
23332333

Modules/gcmodule.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,6 +1922,18 @@ _PyGC_Dump(PyGC_Head *g)
19221922
_PyObject_Dump(FROM_GC(g));
19231923
}
19241924

1925+
static int
1926+
visit_validate(PyObject *op, void *parent_raw)
1927+
{
1928+
PyObject *parent = _PyObject_CAST(parent_raw);
1929+
if (_PyObject_IsFreed(op)) {
1930+
_PyObject_ASSERT_FAILED_MSG(parent,
1931+
"PyObject_GC_Track() object is not valid");
1932+
}
1933+
return 0;
1934+
}
1935+
1936+
19251937
/* extension modules might be compiled with GC support so these
19261938
functions must always be available */
19271939

@@ -1935,6 +1947,13 @@ PyObject_GC_Track(void *op_raw)
19351947
"by the garbage collector");
19361948
}
19371949
_PyObject_GC_TRACK(op);
1950+
1951+
#ifdef Py_DEBUG
1952+
/* Check that the object is valid: validate objects traversed
1953+
by tp_traverse() */
1954+
traverseproc traverse = Py_TYPE(op)->tp_traverse;
1955+
(void)traverse(op, visit_validate, op);
1956+
#endif
19381957
}
19391958

19401959
void

Modules/pyexpat.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,6 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
938938
new_parser->handlers = 0;
939939
new_parser->intern = self->intern;
940940
Py_XINCREF(new_parser->intern);
941-
PyObject_GC_Track(new_parser);
942941

943942
if (self->buffer != NULL) {
944943
new_parser->buffer = PyMem_Malloc(new_parser->buffer_size);
@@ -975,6 +974,8 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
975974
handler_info[i].handler);
976975
}
977976
}
977+
978+
PyObject_GC_Track(new_parser);
978979
return (PyObject *)new_parser;
979980
}
980981

@@ -1122,7 +1123,6 @@ newxmlparseobject(const char *encoding, const char *namespace_separator, PyObjec
11221123
self->handlers = NULL;
11231124
self->intern = intern;
11241125
Py_XINCREF(self->intern);
1125-
PyObject_GC_Track(self);
11261126

11271127
/* namespace_separator is either NULL or contains one char + \0 */
11281128
self->itself = XML_ParserCreate_MM(encoding, &ExpatMemoryHandler,
@@ -1152,6 +1152,7 @@ newxmlparseobject(const char *encoding, const char *namespace_separator, PyObjec
11521152
}
11531153
clear_handlers(self, 1);
11541154

1155+
PyObject_GC_Track(self);
11551156
return (PyObject*)self;
11561157
}
11571158

0 commit comments

Comments
 (0)