[3.14] gh-140815: Fix faulthandler for invalid/freed frame (GH-140921) (#140981)

gh-140815: Fix faulthandler for invalid/freed frame (GH-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.
(cherry picked from commit a84181c31b)

Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
Miss Islington (bot) 2025-11-04 12:18:23 +01:00 committed by GitHub
parent a9c964f368
commit 331b4b868c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 128 additions and 33 deletions

View file

@ -976,14 +976,24 @@ done:
/* Write a frame into the file fd: "File "xxx", line xxx in xxx".
This function is signal safe. */
This function is signal safe.
static void
Return 0 on success. Return -1 if the frame is invalid. */
static int
dump_frame(int fd, _PyInterpreterFrame *frame)
{
assert(frame->owner < FRAME_OWNED_BY_INTERPRETER);
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
/* Ignore trampoline frame */
return 0;
}
PyCodeObject *code =_PyFrame_GetCode(frame);
PyCodeObject *code = _PyFrame_SafeGetCode(frame);
if (code == NULL) {
return -1;
}
int res = 0;
PUTS(fd, " File ");
if (code->co_filename != NULL
&& PyUnicode_Check(code->co_filename))
@ -991,29 +1001,36 @@ dump_frame(int fd, _PyInterpreterFrame *frame)
PUTS(fd, "\"");
_Py_DumpASCII(fd, code->co_filename);
PUTS(fd, "\"");
} else {
PUTS(fd, "???");
}
int lasti = PyUnstable_InterpreterFrame_GetLasti(frame);
int lineno = _PyCode_Addr2LineNoTstate(code, lasti);
else {
PUTS(fd, "???");
res = -1;
}
PUTS(fd, ", line ");
int lasti = _PyFrame_SafeGetLasti(frame);
int lineno = -1;
if (lasti >= 0) {
lineno = _PyCode_SafeAddr2Line(code, lasti);
}
if (lineno >= 0) {
_Py_DumpDecimal(fd, (size_t)lineno);
}
else {
PUTS(fd, "???");
res = -1;
}
PUTS(fd, " in ");
if (code->co_name != NULL
&& PyUnicode_Check(code->co_name)) {
PUTS(fd, " in ");
if (code->co_name != NULL && PyUnicode_Check(code->co_name)) {
_Py_DumpASCII(fd, code->co_name);
}
else {
PUTS(fd, "???");
res = -1;
}
PUTS(fd, "\n");
return res;
}
static int
@ -1056,17 +1073,6 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
unsigned int depth = 0;
while (1) {
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
/* Trampoline frame */
frame = frame->previous;
if (frame == NULL) {
break;
}
/* Can't have more than one shim frame in a row */
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
}
if (MAX_FRAME_DEPTH <= depth) {
if (MAX_FRAME_DEPTH < depth) {
PUTS(fd, "plus ");
@ -1076,7 +1082,15 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
break;
}
dump_frame(fd, frame);
if (_PyMem_IsPtrFreed(frame)) {
PUTS(fd, " <freed frame>\n");
break;
}
if (dump_frame(fd, frame) < 0) {
PUTS(fd, " <invalid frame>\n");
break;
}
frame = frame->previous;
if (frame == NULL) {
break;