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

Reply via email to