8000 bpo-9263: _PyObject_Dump() detects freed memory (GH-10061) (GH-10662)… · python/cpython@c9b3fc6 · GitHub
[go: up one dir, main page]

Skip to content 8000

Commit c9b3fc6

Browse files
authored
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 82af0b6) * 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 2cf5d32) (cherry picked from commit 95036ea)
1 parent 0c15e50 commit c9b3fc6

File tree

4 files changed

+79
-28
lines changed

4 files changed

+79
-28
lines changed

Include/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ struct _Py_Identifier;
520520
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
521521
PyAPI_FUNC(void) _Py_BreakPoint(void);
522522
PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
523+
PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *);
523524
#endif
524525
PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
525526
PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *);

Include/pymem.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ PyAPI_FUNC(int) _PyTraceMalloc_Untrack(
5959
PyAPI_FUNC(PyObject*) _PyTraceMalloc_GetTraceback(
6060
_PyTraceMalloc_domain_t domain,
6161
uintptr_t ptr);
62-
#endif /* !Py_LIMITED_API */
62+
63+
PyAPI_FUNC(int) _PyMem_IsFreed(void *ptr, size_t size);
64+
#endif /* !defined(Py_LIMITED_API) */
6365

6466

6567
/* BEWARE:

Objects/object.c

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -422,40 +422,71 @@ _Py_BreakPoint(void)
422422
}
423423

424424

425+
/* Heuristic checking if the object memory has been deallocated.
426+
Rely on the debug hooks on Python memory allocators which fills the memory
427+
with DEADBYTE (0xDB) when memory is deallocated.
428+
429+
The function can be used to prevent segmentation fault on dereferencing
430+
pointers like 0xdbdbdbdbdbdbdbdb. Such pointer is very unlikely to be mapped
431+
in memory. */
432+
int
433+
_PyObject_IsFreed(PyObject *op)
434+
{
435+
uintptr_t ptr = (uintptr_t)op;
436+
if (_PyMem_IsFreed(&ptr, sizeof(ptr))) {
437+
return 1;
438+
}
439+
int freed = _PyMem_IsFreed(&op->ob_type, sizeof(op->ob_type));
440+
/* ignore op->ob_ref: the value can have be modified
441+
by Py_INCREF() and Py_DECREF(). */
442+
#ifdef Py_TRACE_REFS
443+
freed &= _PyMem_IsFreed(&op->_ob_next, sizeof(op->_ob_next));
444+
freed &= _PyMem_IsFreed(&op->_ob_prev, sizeof(op->_ob_prev));
445+
#endif
446+
return freed;
447+
}
448+
449+
425450
/* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */
426451
void
427452
_PyObject_Dump(PyObject* op)
428453
{
429-
if (op == NULL)
430-
fprintf(stderr, "NULL\n");
431-
else {
432-
#ifdef WITH_THREAD
433-
PyGILState_STATE gil;
434-
#endif
435-
PyObject *error_type, *error_value, *error_traceback;
454+
if (op == NULL) {
455+
fprintf(stderr, "<NULL object>\n");
456+
fflush(stderr);
457+
return;
458+
}
436459

437-
fprintf(stderr, "object : ");
438-
#ifdef WITH_THREAD
439-
gil = PyGILState_Ensure();
440-
#endif
460+
if (_PyObject_IsFreed(op)) {
461+
/* It seems like the object memory has been freed:
462+
don't access it to prevent a segmentation fault. */
463+
fprintf(stderr, "<freed object>\n");
464+
return;
465+
}
441466

442-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
443-
(void)PyObject_Print(op, stderr, 0);
444-
PyErr_Restore(error_type, error_value, error_traceback);
467+
PyGILState_STATE gil;
468+
PyObject *error_type, *error_value, *error_traceback;
445469

446-
#ifdef WITH_THREAD
447-
PyGILState_Release(gil);
448-
#endif
449-
/* XXX(twouters) cast refcount to long until %zd is
450-
universally available */
451-
fprintf(stderr, "\n"
452-
"type : %s\n"
453-
"refcount: %ld\n"
454-
"address : %p\n",
455-
Py_TYPE(op)==NULL ? "NULL" : Py_TYPE(op)->tp_name,
456-
(long)op->ob_refcnt,
457-
op);
458-
}
470+
fprintf(stderr, "object : ");
471+
fflush(stderr);
472+
gil = PyGILState_Ensure();
473+
474+
PyErr_Fetch(&error_type, &error_value, &error_traceback);
475+
(void)PyObject_Print(op, stderr, 0);
476+
fflush(stderr);
477+
PyErr_Restore(error_type, error_value, error_traceback);
478+
479+
PyGILState_Release(gil);
480+
/* XXX(twouters) cast refcount to long until %zd is
481+
universally available */
482+
fprintf(stderr, "\n"
483+
"type : %s\n"
484+
"refcount: %ld\n"
485+
"address : %p\n",
486+
Py_TYPE(op)==NULL ? "NULL" : Py_TYPE(op)->tp_name,
487+
(long)op->ob_refcnt,
488+
op);
489+
fflush(stderr);
459490
}
460491

461492
PyObject *

Objects/obmalloc.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,23 @@ _PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize)
19071907
return _PyMem_DebugRawAlloc(1, ctx, nbytes);
19081908
}
19091909

1910+
1911+
/* Heuristic checking if the memory has been freed. Rely on the debug hooks on
1912+
Python memory allocators which fills the memory with DEADBYTE (0xDB) when
1913+
memory is deallocated. */
1914+
int
1915+
_PyMem_IsFreed(void *ptr, size_t size)
1916+
{
1917+
unsigned char *bytes = ptr;
1918+
for (size_t i=0; i < size; i++) {
1919+
if (bytes[i] != DEADBYTE) {
1920+
return 0;
1921+
}
1922+
}
1923+
return 1;
1924+
}
1925+
1926+
19101927
/* The debug free first checks the 2*SST bytes on each end for sanity (in
19111928
particular, that the FORBIDDENBYTEs with the api ID are still intact).
19121929
Then fills the original bytes with DEADBYTE.

0 commit comments

Comments
 (0)
0