| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a9c964f commit 331b4b8
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -274,8 +274,13 @@ extern void _PyLineTable_InitAddressRange( | |||
| 274 | 274 | /** API for traversing the line number table. */ | |
| 275 | 275 | extern int _PyLineTable_NextAddressRange(PyCodeAddressRange *range); | |
| 276 | 276 | extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range); | |
| 277 | - // This is used in dump_frame() in traceback.c without an attached tstate. | ||
| 278 | - extern int _PyCode_Addr2LineNoTstate(PyCodeObject *co, int addr); | ||
| 277 | + | ||
| 278 | + // Similar to PyCode_Addr2Line(), but return -1 if the code object is invalid | ||
| 279 | + // and can be called without an attached tstate. Used by dump_frame() in | ||
| 280 | + // Python/traceback.c. The function uses heuristics to detect freed memory, | ||
| 281 | + // it's not 100% reliable. | ||
| 282 | + extern int _PyCode_SafeAddr2Line(PyCodeObject *co, int addr); | ||
| 283 | + | ||
| 279 | 284 | ||
| 280 | 285 | /** API for executors */ | |
| 281 | 286 | extern void _PyCode_Clear_Executors(PyCodeObject *code); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,6 +24,36 @@ static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) { | |||
| 24 | 24 | return (PyCodeObject *)executable; | |
| 25 | 25 | } | |
| 26 | 26 | ||
| 27 | + // Similar to _PyFrame_GetCode(), but return NULL if the frame is invalid or | ||
| 28 | + // freed. Used by dump_frame() in Python/traceback.c. The function uses | ||
| 29 | + // heuristics to detect freed memory, it's not 100% reliable. | ||
| 30 | + static inline PyCodeObject* | ||
| 31 | + _PyFrame_SafeGetCode(_PyInterpreterFrame *f) | ||
| 32 | + { | ||
| 33 | + // globals and builtins may be NULL on a legit frame, but it's unlikely. | ||
| 34 | + // It's more likely that it's a sign of an invalid frame. | ||
| 35 | + if (f->f_globals == NULL || f->f_builtins == NULL) { | ||
| 36 | + return NULL; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + if (PyStackRef_IsNull(f->f_executable)) { | ||
| 40 | + return NULL; | ||
| 41 | + } | ||
| 42 | + void *ptr; | ||
| 43 | + memcpy(&ptr, &f->f_executable, sizeof(f->f_executable)); | ||
| 44 | + if (_PyMem_IsPtrFreed(ptr)) { | ||
| 45 | + return NULL; | ||
| 46 | + } | ||
| 47 | + PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); | ||
| 48 | + if (_PyObject_IsFreed(executable)) { | ||
| 49 | + return NULL; | ||
| 50 | + } | ||
| 51 | + if (!PyCode_Check(executable)) { | ||
| 52 | + return NULL; | ||
| 53 | + } | ||
| 54 | + return (PyCodeObject *)executable; | ||
| 55 | + } | ||
| 56 | + | ||
| 27 | 57 | static inline _Py_CODEUNIT * | |
| 28 | 58 | _PyFrame_GetBytecode(_PyInterpreterFrame *f) | |
| 29 | 59 | { | |
@@ -37,6 +67,31 @@ _PyFrame_GetBytecode(_PyInterpreterFrame *f) | |||
| 37 | 67 | #endif | |
| 38 | 68 | } | |
| 39 | 69 | ||
| 70 | + // Similar to PyUnstable_InterpreterFrame_GetLasti(), but return NULL if the | ||
| 71 | + // frame is invalid or freed. Used by dump_frame() in Python/traceback.c. The | ||
| 72 | + // function uses heuristics to detect freed memory, it's not 100% reliable. | ||
| 73 | + static inline int | ||
| 74 | + _PyFrame_SafeGetLasti(struct _PyInterpreterFrame *f) | ||
| 75 | + { | ||
| 76 | + // Code based on _PyFrame_GetBytecode() but replace _PyFrame_GetCode() | ||
| 77 | + // with _PyFrame_SafeGetCode(). | ||
| 78 | + PyCodeObject *co = _PyFrame_SafeGetCode(f); | ||
| 79 | + if (co == NULL) { | ||
| 80 | + return -1; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + _Py_CODEUNIT *bytecode; | ||
| 84 | + #ifdef Py_GIL_DISABLED | ||
| 85 | + _PyCodeArray *tlbc = _PyCode_GetTLBCArray(co); | ||
| 86 | + assert(f->tlbc_index >= 0 && f->tlbc_index < tlbc->size); | ||
| 87 | + bytecode = (_Py_CODEUNIT *)tlbc->entries[f->tlbc_index]; | ||
| 88 | + #else | ||
| 89 | + bytecode = _PyCode_CODE(co); | ||
| 90 | + #endif | ||
| 91 | + | ||
| 92 | + return (int)(f->instr_ptr - bytecode) * sizeof(_Py_CODEUNIT); | ||
| 93 | + } | ||
| 94 | + | ||
| 40 | 95 | static inline PyFunctionObject *_PyFrame_GetFunction(_PyInterpreterFrame *f) { | |
| 41 | 96 | PyObject *func = PyStackRef_AsPyObjectBorrow(f->f_funcobj); | |
| 42 | 97 | assert(PyFunction_Check(func)); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,15 +54,17 @@ static inline int _PyMem_IsPtrFreed(const void *ptr) | |||
| 54 | 54 | { | |
| 55 | 55 | uintptr_t value = (uintptr_t)ptr; | |
| 56 | 56 | #if SIZEOF_VOID_P == 8 | |
| 57 | - return (value == 0 | ||
| 57 | + return (value <= 0xff // NULL, 0x1, 0x2, ..., 0xff | ||
| 58 | 58 | || value == (uintptr_t)0xCDCDCDCDCDCDCDCD | |
| 59 | 59 | || value == (uintptr_t)0xDDDDDDDDDDDDDDDD | |
| 60 | - || value == (uintptr_t)0xFDFDFDFDFDFDFDFD); | ||
| 60 | + || value == (uintptr_t)0xFDFDFDFDFDFDFDFD | ||
| 61 | + || value >= (uintptr_t)0xFFFFFFFFFFFFFF00); // -0xff, ..., -2, -1 | ||
| 61 | 62 | #elif SIZEOF_VOID_P == 4 | |
| 62 | - return (value == 0 | ||
| 63 | + return (value <= 0xff | ||
| 63 | 64 | || value == (uintptr_t)0xCDCDCDCD | |
| 64 | 65 | || value == (uintptr_t)0xDDDDDDDD | |
| 65 | - || value == (uintptr_t)0xFDFDFDFD); | ||
| 66 | + || value == (uintptr_t)0xFDFDFDFD | ||
| 67 | + || value >= (uintptr_t)0xFFFFFF00); | ||
| 66 | 68 | #else | |
| 67 | 69 | # error "unknown pointer size" | |
| 68 | 70 | #endif | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + :mod:`faulthandler` now detects if a frame or a code object is invalid or | ||
| 2 | + freed. Patch by Victor Stinner. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1013,8 +1013,8 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) | |||
| 1013 | 1013 | * source location tracking (co_lines/co_positions) | |
| 1014 | 1014 | ******************/ | |
| 1015 | 1015 | ||
| 1016 | - int | ||
| 1017 | - _PyCode_Addr2LineNoTstate(PyCodeObject *co, int addrq) | ||
| 1016 | + static int | ||
| 1017 | + _PyCode_Addr2Line(PyCodeObject *co, int addrq) | ||
| 1018 | 1018 | { | |
| 1019 | 1019 | if (addrq < 0) { | |
| 1020 | 1020 | return co->co_firstlineno; | |
@@ -1028,12 +1028,29 @@ _PyCode_Addr2LineNoTstate(PyCodeObject *co, int addrq) | |||
| 1028 | 1028 | return _PyCode_CheckLineNumber(addrq, &bounds); | |
| 1029 | 1029 | } | |
| 1030 | 1030 | ||
| 1031 | + int | ||
| 1032 | + _PyCode_SafeAddr2Line(PyCodeObject *co, int addrq) | ||
| 1033 | + { | ||
| 1034 | + if (addrq < 0) { | ||
| 1035 | + return co->co_firstlineno; | ||
| 1036 | + } | ||
| 1037 | + if (co->_co_monitoring && co->_co_monitoring->lines) { | ||
| 1038 | + return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); | ||
| 1039 | + } | ||
| 1040 | + if (!(addrq >= 0 && addrq < _PyCode_NBYTES(co))) { | ||
| 1041 | + return -1; | ||
| 1042 | + } | ||
| 1043 | + PyCodeAddressRange bounds; | ||
| 1044 | + _PyCode_InitAddressRange(co, &bounds); | ||
| 1045 | + return _PyCode_CheckLineNumber(addrq, &bounds); | ||
| 1046 | + } | ||
| 1047 | + | ||
| 1031 | 1048 | int | |
| 1032 | 1049 | PyCode_Addr2Line(PyCodeObject *co, int addrq) | |
| 1033 | 1050 | { | |
| 1034 | 1051 | int lineno; | |
| 1035 | 1052 | Py_BEGIN_CRITICAL_SECTION(co); | |
| 1036 | - lineno = _PyCode_Addr2LineNoTstate(co, addrq); | ||
| 1053 | + lineno = _PyCode_Addr2Line(co, addrq); | ||
| 1037 | 1054 | Py_END_CRITICAL_SECTION(); | |
| 1038 | 1055 | return lineno; | |
| 1039 | 1056 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -976,44 +976,61 @@ _Py_DumpASCII(int fd, PyObject *text) | |||
| 976 | 976 | ||
| 977 | 977 | /* Write a frame into the file fd: "File "xxx", line xxx in xxx". | |
| 978 | 978 | ||
| 979 | - This function is signal safe. */ | ||
| 979 | + This function is signal safe. | ||
| 980 | 980 | ||
| 981 | - static void | ||
| 981 | + Return 0 on success. Return -1 if the frame is invalid. */ | ||
| 982 | + | ||
| 983 | + static int | ||
| 982 | 984 | dump_frame(int fd, _PyInterpreterFrame *frame) | |
| 983 | 985 | { | |
| 984 | - assert(frame->owner < FRAME_OWNED_BY_INTERPRETER); | ||
| 986 | + if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { | ||
| 987 | + /* Ignore trampoline frame */ | ||
| 988 | + return 0; | ||
| 989 | + } | ||
| 985 | 990 | ||
| 986 | - PyCodeObject *code =_PyFrame_GetCode(frame); | ||
| 991 | + PyCodeObject *code = _PyFrame_SafeGetCode(frame); | ||
| 992 | + if (code == NULL) { | ||
| 993 | + return -1; | ||
| 994 | + } | ||
| 995 | + | ||
| 996 | + int res = 0; | ||
| 987 | 997 | PUTS(fd, " File "); | |
| 988 | 998 | if (code->co_filename != NULL | |
| 989 | 999 | && PyUnicode_Check(code->co_filename)) | |
| 990 | 1000 | { | |
| 991 | 1001 | PUTS(fd, "\""); | |
| 992 | 1002 | _Py_DumpASCII(fd, code->co_filename); | |
| 993 | 1003 | PUTS(fd, "\""); | |
| 994 | - } else { | ||
| 1004 | + } | ||
| 1005 | + else { | ||
| 995 | 1006 | PUTS(fd, "???"); | |
| 1007 | + res = -1; | ||
| 996 | 1008 | } | |
| 997 | - int lasti = PyUnstable_InterpreterFrame_GetLasti(frame); | ||
| 998 | - int lineno = _PyCode_Addr2LineNoTstate(code, lasti); | ||
| 1009 | + | ||
| 999 | 1010 | PUTS(fd, ", line "); | |
| 1011 | + int lasti = _PyFrame_SafeGetLasti(frame); | ||
| 1012 | + int lineno = -1; | ||
| 1013 | + if (lasti >= 0) { | ||
| 1014 | + lineno = _PyCode_SafeAddr2Line(code, lasti); | ||
| 1015 | + } | ||
| 1000 | 1016 | if (lineno >= 0) { | |
| 1001 | 1017 | _Py_DumpDecimal(fd, (size_t)lineno); | |
| 1002 | 1018 | } | |
| 1003 | 1019 | else { | |
| 1004 | 1020 | PUTS(fd, "???"); | |
| 1021 | + res = -1; | ||
| 1005 | 1022 | } | |
| 1006 | - PUTS(fd, " in "); | ||
| 1007 | 1023 | ||
| 1008 | - if (code->co_name != NULL | ||
| 1009 | - && PyUnicode_Check(code->co_name)) { | ||
| 1024 | + PUTS(fd, " in "); | ||
| 1025 | + if (code->co_name != NULL && PyUnicode_Check(code->co_name)) { | ||
| 1010 | 1026 | _Py_DumpASCII(fd, code->co_name); | |
| 1011 | 1027 | } | |
| 1012 | 1028 | else { | |
| 1013 | 1029 | PUTS(fd, "???"); | |
| 1030 | + res = -1; | ||
| 1014 | 1031 | } | |
| 1015 | - | ||
| 1016 | 1032 | PUTS(fd, "\n"); | |
| 1033 | + return res; | ||
| 1017 | 1034 | } | |
| 1018 | 1035 | ||
| 1019 | 1036 | static int | |
@@ -1056,17 +1073,6 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) | |||
| 1056 | 1073 | ||
| 1057 | 1074 | unsigned int depth = 0; | |
| 1058 | 1075 | while (1) { | |
| 1059 | - if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { | ||
| 1060 | - /* Trampoline frame */ | ||
| 1061 | - frame = frame->previous; | ||
| 1062 | - if (frame == NULL) { | ||
| 1063 | - break; | ||
| 1064 | - } | ||
| 1065 | - | ||
| 1066 | - /* Can't have more than one shim frame in a row */ | ||
| 1067 | - assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); | ||
| 1068 | - } | ||
| 1069 | - | ||
| 1070 | 1076 | if (MAX_FRAME_DEPTH <= depth) { | |
| 1071 | 1077 | if (MAX_FRAME_DEPTH < depth) { | |
| 1072 | 1078 | PUTS(fd, "plus "); | |
@@ -1076,7 +1082,15 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) | |||
| 1076 | 1082 | break; | |
| 1077 | 1083 | } | |
| 1078 | 1084 | ||
| 1079 | - dump_frame(fd, frame); | ||
| 1085 | + if (_PyMem_IsPtrFreed(frame)) { | ||
| 1086 | + PUTS(fd, " <freed frame>\n"); | ||
| 1087 | + break; | ||
| 1088 | + } | ||
| 1089 | + if (dump_frame(fd, frame) < 0) { | ||
| 1090 | + PUTS(fd, " <invalid frame>\n"); | ||
| 1091 | + break; | ||
| 1092 | + } | ||
| 1093 | + | ||
| 1080 | 1094 | frame = frame->previous; | |
| 1081 | 1095 | if (frame == NULL) { | |
| 1082 | 1096 | break; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments