Hi, I would like to be able to catch SIGSEGV in my Python code! So I started to hack Python trunk to support this feature. The idea is to use a signal handler which call longjmp(), and add setjmp() at Py_EvalFrameEx() enter.
See attached ("small") patch: segfault.patch Example read.py with the *evil* ctypes module of invalid memory read: ------------------- 8< -------------- from ctypes import string_at def fault(): text = string_at(1, 10) print("text = {0!r}".format(text)) def test(): print("test: 1") try: fault() except MemoryError, err: print "ooops!" print err print("test: 2") try: fault() except MemoryError, err: print "ooops!" print err print("test: end") def main(): test() if __name__ == "__main__": main() ------------------- 8< -------------- Result: ------------------- 8< -------------- $ python read.py test: 1 sizeof()=160 ooops! segmentation fault test: 2 sizeof()=160 ooops! segmentation fault test: end ------------------- 8< -------------- Example bug1.py of a stack overflow: ---------- loop = None, for i in xrange(10**5): loop = loop, None ---------- Result: ---------- $ python -i bug1.py >>> print loop (((((((((...Traceback (most recent call last): File "<stdin>", line 1, in <module> MemoryError: segmentation fault >>> ---------- Python is able to restore a valid state (stack/heap) after a segmentation fault and raise a classical Python exception (I choosed MemoryError, but it could be a specific exception). On my computer (Ubuntu Gutsy/i386), each segfault_frame takes sizeof(sigjmpbuf) + sizeof(void*) = 160 bytes, allocated on the stack. I don't know if it's huge or not, but that will limit the number of recursive calls. The feature can be optional if we add a configure option and some #ifdef/#endif. A dedicated stack is needed to be call the signal handler on stack overflow error. I choosed 4 KB, but since I only call longjmp(), smaller stack might also works. Does other VM support such feature? JVM, Mono, .NET, etc. ? I had the idea of catching SIGSEGV after reading the issue 1069092 (stack overflow because of too many recursive calls). -- Victor Stinner aka haypo http://www.haypocalc.com/blog/
Index: Include/segfault.h =================================================================== --- Include/segfault.h (révision 0) +++ Include/segfault.h (révision 0) @@ -0,0 +1,37 @@ + +/* Interface to execute compiled code */ + +#ifndef Py_SEGFAULT_H +#define Py_SEGFAULT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include <setjmp.h> +#include <signal.h> + +/* Use a custom stack for signal handlers, especially the segfault handler */ +#define SEGFAULT_STACK + +typedef struct _segfault_frame_t { + sigjmp_buf env; + struct _segfault_frame_t *previous; +} segfault_frame_t; + +typedef struct { + int init; + PyObject *text; + segfault_frame_t *current_frame; +#ifdef SEGFAULT_STACK + char stack[4096]; +#endif +} segfault_t; + +void segfault_enter(segfault_frame_t *frame); +void segfault_exit(segfault_frame_t *frame); +void segfault_set_error(void); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SEGFAULT_H */ Modification de propriétés sur Include/segfault.h ___________________________________________________________________ Nom : svn:eol-style + native Index: Python/segfault.c =================================================================== --- Python/segfault.c (révision 0) +++ Python/segfault.c (révision 0) @@ -0,0 +1,81 @@ +/** + * Python segmentation fault handler + */ + +#include "Python.h" +#include "segfault.h" + +static segfault_t segfault; + +void +segfault_set_error(void) +{ + PyErr_SetObject(PyExc_MemoryError, segfault.text); +} + +static int segfault_install(void); + +static void +segfault_handler(int sig_num) +{ + (void)segfault_install(); + siglongjmp(segfault.current_frame->env, 1); +} + +static int +segfault_install() +{ + struct sigaction context, ocontext; + context.sa_handler = segfault_handler; + sigemptyset(&context.sa_mask); +#ifdef SEGFAULT_STACK + context.sa_flags = SA_RESETHAND | SA_RESTART | SA_ONSTACK; +#else + context.sa_flags = SA_RESETHAND | SA_RESTART; +#endif + if (sigaction(SIGSEGV, &context, &ocontext) == -1) + return 1; + else + return 0; +} + +void +segfault_enter(segfault_frame_t *frame) +{ + frame->previous = segfault.current_frame; + segfault.current_frame = frame; +} + +void +segfault_exit(segfault_frame_t *frame) +{ + if (segfault.current_frame) + segfault.current_frame = segfault.current_frame->previous; +} + +void +PyOS_InitSegfault() +{ +#ifdef SEGFAULT_STACK + stack_t ss; + ss.ss_sp = segfault.stack; + ss.ss_size = sizeof(segfault.stack); + ss.ss_flags = 0; + if( sigaltstack(&ss, NULL)) { + /* FIXME: catch this error */ + } +#endif + + segfault.text = PyString_FromString("segmentation fault"); + /* FIXME + if (!segfault.text) ???; + */ + (void)segfault_install(); +} + +void +PyOS_FiniSegfault() +{ + Py_DECREF(segfault.text); +} + Modification de propriétés sur Python/segfault.c ___________________________________________________________________ Nom : svn:eol-style + native Index: Python/ceval.c =================================================================== --- Python/ceval.c (révision 66680) +++ Python/ceval.c (copie de travail) @@ -16,6 +16,7 @@ #include "eval.h" #include "opcode.h" #include "structmember.h" +#include "segfault.h" #include <ctype.h> @@ -553,6 +554,7 @@ PyObject *retval = NULL; /* Return value */ PyThreadState *tstate = PyThreadState_GET(); PyCodeObject *co; + segfault_frame_t segfault_frame; /* when tracing we set things up so that @@ -801,6 +803,13 @@ goto on_error; } + segfault_enter(&segfault_frame); + if (sigsetjmp(segfault_frame.env, 1)) { + segfault_set_error(); + why = WHY_EXCEPTION; + goto on_error; + } + for (;;) { #ifdef WITH_TSC if (inst1 == 0) { @@ -2697,6 +2706,8 @@ /* pop frame */ exit_eval_frame: + segfault_exit(&segfault_frame); + Py_LeaveRecursiveCall(); tstate->frame = f->f_back; Index: Makefile.pre.in =================================================================== --- Makefile.pre.in (révision 66680) +++ Makefile.pre.in (copie de travail) @@ -280,6 +280,7 @@ Python/pymath.o \ Python/pystate.o \ Python/pythonrun.o \ + Python/segfault.o \ Python/structmember.o \ Python/symtable.o \ Python/sysmodule.o \
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com