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.
This commit is contained in:
Victor Stinner 2019-10-08 00:09:31 +02:00 committed by GitHub
parent 7775349895
commit 1b18455695
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 4 deletions

View File

@ -0,0 +1,3 @@
In debug mode, :c:func:`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.

View File

@ -2317,8 +2317,6 @@ create_elementiter(ElementObject *self, PyObject *tag, int gettext)
Py_INCREF(self); Py_INCREF(self);
it->root_element = self; it->root_element = self;
PyObject_GC_Track(it);
it->parent_stack = PyMem_New(ParentLocator, INIT_PARENT_STACK_SIZE); it->parent_stack = PyMem_New(ParentLocator, INIT_PARENT_STACK_SIZE);
if (it->parent_stack == NULL) { if (it->parent_stack == NULL) {
Py_DECREF(it); Py_DECREF(it);
@ -2328,6 +2326,8 @@ create_elementiter(ElementObject *self, PyObject *tag, int gettext)
it->parent_stack_used = 0; it->parent_stack_used = 0;
it->parent_stack_size = INIT_PARENT_STACK_SIZE; it->parent_stack_size = INIT_PARENT_STACK_SIZE;
PyObject_GC_Track(it);
return (PyObject *)it; return (PyObject *)it;
} }

View File

@ -1922,6 +1922,18 @@ _PyGC_Dump(PyGC_Head *g)
_PyObject_Dump(FROM_GC(g)); _PyObject_Dump(FROM_GC(g));
} }
static int
visit_validate(PyObject *op, void *parent_raw)
{
PyObject *parent = _PyObject_CAST(parent_raw);
if (_PyObject_IsFreed(op)) {
_PyObject_ASSERT_FAILED_MSG(parent,
"PyObject_GC_Track() object is not valid");
}
return 0;
}
/* extension modules might be compiled with GC support so these /* extension modules might be compiled with GC support so these
functions must always be available */ functions must always be available */
@ -1935,6 +1947,13 @@ PyObject_GC_Track(void *op_raw)
"by the garbage collector"); "by the garbage collector");
} }
_PyObject_GC_TRACK(op); _PyObject_GC_TRACK(op);
#ifdef Py_DEBUG
/* Check that the object is valid: validate objects traversed
by tp_traverse() */
traverseproc traverse = Py_TYPE(op)->tp_traverse;
(void)traverse(op, visit_validate, op);
#endif
} }
void void

View File

@ -938,7 +938,6 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
new_parser->handlers = 0; new_parser->handlers = 0;
new_parser->intern = self->intern; new_parser->intern = self->intern;
Py_XINCREF(new_parser->intern); Py_XINCREF(new_parser->intern);
PyObject_GC_Track(new_parser);
if (self->buffer != NULL) { if (self->buffer != NULL) {
new_parser->buffer = PyMem_Malloc(new_parser->buffer_size); new_parser->buffer = PyMem_Malloc(new_parser->buffer_size);
@ -975,6 +974,8 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
handler_info[i].handler); handler_info[i].handler);
} }
} }
PyObject_GC_Track(new_parser);
return (PyObject *)new_parser; return (PyObject *)new_parser;
} }
@ -1122,7 +1123,6 @@ newxmlparseobject(const char *encoding, const char *namespace_separator, PyObjec
self->handlers = NULL; self->handlers = NULL;
self->intern = intern; self->intern = intern;
Py_XINCREF(self->intern); Py_XINCREF(self->intern);
PyObject_GC_Track(self);
/* namespace_separator is either NULL or contains one char + \0 */ /* namespace_separator is either NULL or contains one char + \0 */
self->itself = XML_ParserCreate_MM(encoding, &ExpatMemoryHandler, self->itself = XML_ParserCreate_MM(encoding, &ExpatMemoryHandler,
@ -1152,6 +1152,7 @@ newxmlparseobject(const char *encoding, const char *namespace_separator, PyObjec
} }
clear_handlers(self, 1); clear_handlers(self, 1);
PyObject_GC_Track(self);
return (PyObject*)self; return (PyObject*)self;
} }