diff options
author | Victor Stinner <vstinner@redhat.com> | 2018-11-22 17:40:53 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-22 17:40:53 +0100 |
commit | c9b3fc6b59b625c36c31ad437253e7140938af1a (patch) | |
tree | b38312e30130c19f67b8874556c76e1c317e9d89 /Objects/object.c | |
parent | 0c15e508baec7e542933db2b31ea950a646cd968 (diff) | |
download | cpython-git-c9b3fc6b59b625c36c31ad437253e7140938af1a.tar.gz |
bpo-9263: _PyObject_Dump() detects freed memory (GH-10061) (GH-10662) (GH-10663)
* 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.
(cherry picked from commit 82af0b63b07aa8d92b50098e382b458143cfc677)
* bpo-9263: Fix _PyObject_Dump() for freed object (#10661)
If _PyObject_Dump() detects that the object is freed, don't try to
dump it (exit immediately).
Enhance also _PyObject_IsFreed(): it now detects if the pointer
itself looks like freed memory.
(cherry picked from commit 2cf5d32fd9e61488e8b0be55a2e92a752ba8b06b)
(cherry picked from commit 95036ea25d47f0081bda2ba96ea327f3375cb6a4)
Diffstat (limited to 'Objects/object.c')
-rw-r--r-- | Objects/object.c | 85 |
1 files changed, 58 insertions, 27 deletions
diff --git a/Objects/object.c b/Objects/object.c index fdd41a6168..e1a0569ab9 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -422,40 +422,71 @@ _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) +{ + uintptr_t ptr = (uintptr_t)op; + if (_PyMem_IsFreed(&ptr, sizeof(ptr))) { + return 1; + } + 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 { -#ifdef WITH_THREAD - PyGILState_STATE gil; -#endif - PyObject *error_type, *error_value, *error_traceback; + if (op == NULL) { + fprintf(stderr, "<NULL object>\n"); + fflush(stderr); + return; + } - fprintf(stderr, "object : "); -#ifdef WITH_THREAD - gil = PyGILState_Ensure(); -#endif + 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"); + return; + } - PyErr_Fetch(&error_type, &error_value, &error_traceback); - (void)PyObject_Print(op, stderr, 0); - PyErr_Restore(error_type, error_value, error_traceback); + PyGILState_STATE gil; + PyObject *error_type, *error_value, *error_traceback; -#ifdef WITH_THREAD - PyGILState_Release(gil); -#endif - /* 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); - } + 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 * |