mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-140815: Fix faulthandler for invalid/freed frame (#140921)
faulthandler now detects if a frame or a code object is invalid or freed. Add helper functions: * _PyCode_SafeAddr2Line() * _PyFrame_SafeGetCode() * _PyFrame_SafeGetLasti() _PyMem_IsPtrFreed() now detects pointers in [-0xff, 0xff] range as freed.
This commit is contained in:
parent
08115d241a
commit
a84181c31b
6 changed files with 128 additions and 33 deletions
|
|
@ -24,6 +24,36 @@ static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) {
|
|||
return (PyCodeObject *)executable;
|
||||
}
|
||||
|
||||
// Similar to _PyFrame_GetCode(), but return NULL if the frame is invalid or
|
||||
// freed. Used by dump_frame() in Python/traceback.c. The function uses
|
||||
// heuristics to detect freed memory, it's not 100% reliable.
|
||||
static inline PyCodeObject*
|
||||
_PyFrame_SafeGetCode(_PyInterpreterFrame *f)
|
||||
{
|
||||
// globals and builtins may be NULL on a legit frame, but it's unlikely.
|
||||
// It's more likely that it's a sign of an invalid frame.
|
||||
if (f->f_globals == NULL || f->f_builtins == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (PyStackRef_IsNull(f->f_executable)) {
|
||||
return NULL;
|
||||
}
|
||||
void *ptr;
|
||||
memcpy(&ptr, &f->f_executable, sizeof(f->f_executable));
|
||||
if (_PyMem_IsPtrFreed(ptr)) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable);
|
||||
if (_PyObject_IsFreed(executable)) {
|
||||
return NULL;
|
||||
}
|
||||
if (!PyCode_Check(executable)) {
|
||||
return NULL;
|
||||
}
|
||||
return (PyCodeObject *)executable;
|
||||
}
|
||||
|
||||
static inline _Py_CODEUNIT *
|
||||
_PyFrame_GetBytecode(_PyInterpreterFrame *f)
|
||||
{
|
||||
|
|
@ -37,6 +67,31 @@ _PyFrame_GetBytecode(_PyInterpreterFrame *f)
|
|||
#endif
|
||||
}
|
||||
|
||||
// Similar to PyUnstable_InterpreterFrame_GetLasti(), but return NULL if the
|
||||
// frame is invalid or freed. Used by dump_frame() in Python/traceback.c. The
|
||||
// function uses heuristics to detect freed memory, it's not 100% reliable.
|
||||
static inline int
|
||||
_PyFrame_SafeGetLasti(struct _PyInterpreterFrame *f)
|
||||
{
|
||||
// Code based on _PyFrame_GetBytecode() but replace _PyFrame_GetCode()
|
||||
// with _PyFrame_SafeGetCode().
|
||||
PyCodeObject *co = _PyFrame_SafeGetCode(f);
|
||||
if (co == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_Py_CODEUNIT *bytecode;
|
||||
#ifdef Py_GIL_DISABLED
|
||||
_PyCodeArray *tlbc = _PyCode_GetTLBCArray(co);
|
||||
assert(f->tlbc_index >= 0 && f->tlbc_index < tlbc->size);
|
||||
bytecode = (_Py_CODEUNIT *)tlbc->entries[f->tlbc_index];
|
||||
#else
|
||||
bytecode = _PyCode_CODE(co);
|
||||
#endif
|
||||
|
||||
return (int)(f->instr_ptr - bytecode) * sizeof(_Py_CODEUNIT);
|
||||
}
|
||||
|
||||
static inline PyFunctionObject *_PyFrame_GetFunction(_PyInterpreterFrame *f) {
|
||||
PyObject *func = PyStackRef_AsPyObjectBorrow(f->f_funcobj);
|
||||
assert(PyFunction_Check(func));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue