diff options
author | Victor Stinner <vstinner@redhat.com> | 2018-10-23 15:39:40 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-23 15:39:40 (GMT) |
commit | 82af0b63b07aa8d92b50098e382b458143cfc677 (patch) | |
tree | 77c2d497083f78ad622f75ffeb9680c6583d3317 /Objects/object.c | |
parent | 96f2c739542d48edd6bd15c26b555c7e59d14cce (diff) | |
download | cpython-82af0b63b07aa8d92b50098e382b458143cfc677.zip cpython-82af0b63b07aa8d92b50098e382b458143cfc677.tar.gz cpython-82af0b63b07aa8d92b50098e382b458143cfc677.tar.bz2 |
bpo-9263: _PyObject_Dump() detects freed memory (GH-10061)
_PyObject_Dump() now uses an heuristic to check if the object memory
has been freed: log "<freed object>" in that case.
The heuristic rely on the debug hooks on Python memory allocators
which fills the memory with DEADBYTE (0xDB) when memory is
deallocated. Use PYTHONMALLOC=debug to always enable these debug
hooks.
Diffstat (limited to 'Objects/object.c')
-rw-r--r-- | Objects/object.c | 78 |
1 files changed, 55 insertions, 23 deletions
diff --git a/Objects/object.c b/Objects/object.c index ab1baa7..00c0bad 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -410,34 +410,66 @@ _Py_BreakPoint(void) } +/* Heuristic checking if the object memory has been deallocated. + Rely on the debug hooks on Python memory allocators which fills the memory + with DEADBYTE (0xDB) when memory is deallocated. + + The function can be used to prevent segmentation fault on dereferencing + pointers like 0xdbdbdbdbdbdbdbdb. Such pointer is very unlikely to be mapped + in memory. */ +int +_PyObject_IsFreed(PyObject *op) +{ + int freed = _PyMem_IsFreed(&op->ob_type, sizeof(op->ob_type)); + /* ignore op->ob_ref: the value can have be modified + by Py_INCREF() and Py_DECREF(). */ +#ifdef Py_TRACE_REFS + freed &= _PyMem_IsFreed(&op->_ob_next, sizeof(op->_ob_next)); + freed &= _PyMem_IsFreed(&op->_ob_prev, sizeof(op->_ob_prev)); +#endif + return freed; +} + + /* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */ void _PyObject_Dump(PyObject* op) { - if (op == NULL) - fprintf(stderr, "NULL\n"); - else { - PyGILState_STATE gil; - PyObject *error_type, *error_value, *error_traceback; - - fprintf(stderr, "object : "); - gil = PyGILState_Ensure(); - - PyErr_Fetch(&error_type, &error_value, &error_traceback); - (void)PyObject_Print(op, stderr, 0); - PyErr_Restore(error_type, error_value, error_traceback); - - PyGILState_Release(gil); - /* XXX(twouters) cast refcount to long until %zd is - universally available */ - fprintf(stderr, "\n" - "type : %s\n" - "refcount: %ld\n" - "address : %p\n", - Py_TYPE(op)==NULL ? "NULL" : Py_TYPE(op)->tp_name, - (long)op->ob_refcnt, - op); + if (op == NULL) { + fprintf(stderr, "<NULL object>\n"); + fflush(stderr); + return; + } + + if (_PyObject_IsFreed(op)) { + /* It seems like the object memory has been freed: + don't access it to prevent a segmentation fault. */ + fprintf(stderr, "<freed object>\n"); } + + PyGILState_STATE gil; + PyObject *error_type, *error_value, *error_traceback; + + fprintf(stderr, "object : "); + fflush(stderr); + gil = PyGILState_Ensure(); + + PyErr_Fetch(&error_type, &error_value, &error_traceback); + (void)PyObject_Print(op, stderr, 0); + fflush(stderr); + PyErr_Restore(error_type, error_value, error_traceback); + + PyGILState_Release(gil); + /* XXX(twouters) cast refcount to long until %zd is + universally available */ + fprintf(stderr, "\n" + "type : %s\n" + "refcount: %ld\n" + "address : %p\n", + Py_TYPE(op)==NULL ? "NULL" : Py_TYPE(op)->tp_name, + (long)op->ob_refcnt, + op); + fflush(stderr); } PyObject * |