chicken-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Chicken-hackers] [PATCH] catch "serious" signals SIGSEGV, SIGBUS, etc.


From: Felix
Subject: [Chicken-hackers] [PATCH] catch "serious" signals SIGSEGV, SIGBUS, etc.
Date: Sat, 27 Oct 2012 10:24:09 -0400 (EDT)

The attached patch adds signal handling for the "serious" signals
mentioned above. This is quite useful, since it makes it possible
to generate a call-trace on errors that would normally just abort
the process. In "csi", the REPL will be reentered.

Setting user-level signal handlers for these signals is not allowed,
as they the runtime-system needs to do some special processing for
these, but the exceptions thrown as a result of the signals can be
caught with the normal exception-handling procedures.

Recursive invocations of the signal handler (which would mean
something is seriously broken, like an inconsistent heap) will still
abort the process (this is also required to avoid having to siglongjmp
out of the recursive handler, which is undefined according to
POSIX).

The runtime option "-:S" can be used to disable setting up handlers
for these signals.Ã

THe patch also adds "signal/bus", which was missing from the
"posix" unit.


cheers,
felix
>From 34857dd7602065940392f64e7337b3bb8f721192 Mon Sep 17 00:00:00 2001
From: felix <address@hidden>
Date: Sun, 7 Oct 2012 22:48:21 +0200
Subject: [PATCH] catch serious signals (SIGSEGV, SIGILL, SIGBUS, SIGPE)

---
 Makefile.bsd      |    1 +
 Makefile.haiku    |    1 +
 Makefile.linux    |    1 +
 Makefile.macosx   |    1 +
 chicken.h         |   16 +++++--
 library.scm       |    4 ++
 manual/Exceptions |    7 +++
 manual/Unit posix |    3 +
 posix.import.scm  |    1 +
 posixunix.scm     |   33 +++++++++-----
 runtime.c         |  131 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 types.db          |    1 +
 12 files changed, 183 insertions(+), 17 deletions(-)

diff --git a/Makefile.bsd b/Makefile.bsd
index 4402c03..fb1f244 100644
--- a/Makefile.bsd
+++ b/Makefile.bsd
@@ -85,6 +85,7 @@ chicken-config.h: chicken-defaults.h
        echo "#define HAVE_MEMORY_H 1" >>$@
        echo "#define HAVE_SIGACTION 1" >>$@
        echo "#define HAVE_SIGSETJMP 1" >>$@
+       echo "#define HAVE_SIGPROCMASK 1" >>$@
        echo "#define HAVE_STDINT_H 1" >>$@
        echo "#define HAVE_STDLIB_H 1" >>$@
        echo "#define HAVE_STRERROR 1" >>$@
diff --git a/Makefile.haiku b/Makefile.haiku
index 3e1c558..ff06c5b 100644
--- a/Makefile.haiku
+++ b/Makefile.haiku
@@ -73,6 +73,7 @@ chicken-config.h: chicken-defaults.h
        echo "#define HAVE_MEMORY_H 1" >>$@
        echo "#define HAVE_SIGACTION 1" >>$@
        echo "#define HAVE_SIGSETJMP 1" >>$@
+       echo "#define HAVE_SIGPROCMASK 1" >>$@
        echo "#define HAVE_STDINT_H 1" >>$@
        echo "#define HAVE_STDLIB_H 1" >>$@
        echo "#define HAVE_STRERROR 1" >>$@
diff --git a/Makefile.linux b/Makefile.linux
index c55d55e..b04e7f6 100644
--- a/Makefile.linux
+++ b/Makefile.linux
@@ -74,6 +74,7 @@ chicken-config.h: chicken-defaults.h
        echo "#define HAVE_MEMORY_H 1" >>$@
        echo "#define HAVE_SIGACTION 1" >>$@
        echo "#define HAVE_SIGSETJMP 1" >>$@
+       echo "#define HAVE_SIGPROCMASK 1" >>$@
        echo "#define HAVE_STDINT_H 1" >>$@
        echo "#define HAVE_STDLIB_H 1" >>$@
        echo "#define HAVE_STRERROR 1" >>$@
diff --git a/Makefile.macosx b/Makefile.macosx
index a0a73fa..dbdefe3 100644
--- a/Makefile.macosx
+++ b/Makefile.macosx
@@ -98,6 +98,7 @@ chicken-config.h: chicken-defaults.h
        echo "#define HAVE_MEMORY_H 1" >>$@
        echo "#define HAVE_SIGACTION 1" >>$@
        echo "#define HAVE_SIGSETJMP 1" >>$@
+       echo "#define HAVE_SIGPROCMASK 1" >>$@
        echo "#define HAVE_STDINT_H 1" >>$@
        echo "#define HAVE_STDLIB_H 1" >>$@
        echo "#define HAVE_STRERROR 1" >>$@
diff --git a/chicken.h b/chicken.h
index a946d0c..516b607 100644
--- a/chicken.h
+++ b/chicken.h
@@ -571,6 +571,10 @@ static inline int isinf_ld (long double x)
 #define C_BAD_ARGUMENT_TYPE_NO_OUTPUT_PORT_ERROR      41
 #define C_PORT_CLOSED_ERROR                           42
 #define C_ASCIIZ_REPRESENTATION_ERROR                 43
+#define C_MEMORY_VIOLATION_ERROR                      44
+#define C_FLOATING_POINT_EXCEPTION_ERROR              45
+#define C_ILLEGAL_INSTRUCTION_ERROR                   46
+#define C_BUS_ERROR                                   47
 
 
 /* Platform information */
@@ -878,12 +882,14 @@ DECL_C_PROC_p0 (128,  1,0,0,0,0,0,0,0)
  * so try to use versions that we know won't try to save & restore.
  */
 # if defined(HAVE_SIGSETJMP)
-#   define C_setjmp(e)              sigsetjmp(e, 0)
-#   define C_longjmp(e,v)           siglongjmp(e, v)
-# else
-#   define C_setjmp                 setjmp
-#   define C_longjmp                longjmp
+#   define C_sigsetjmp              sigsetjmp
+#   define C_siglongjmp             siglongjmp
+# endif
+# ifdef HAVE_SIGPROCMASK
+#  define C_sigprocmask             sigprocmask
 # endif
+# define C_setjmp                   setjmp
+# define C_longjmp                  longjmp
 # define C_alloca                   alloca
 # define C_strerror                 strerror
 # define C_isalpha                  isalpha
diff --git a/library.scm b/library.scm
index bd30e62..680687f 100644
--- a/library.scm
+++ b/library.scm
@@ -4172,6 +4172,10 @@ EOF
        ((41) (apply ##sys#signal-hook #:type-error loc "bad argument type - 
not an output-port" args))
        ((42) (apply ##sys#signal-hook #:file-error loc "port already closed" 
args))
        ((43) (apply ##sys#signal-hook #:type-error loc "cannot represent 
string with NUL bytes as C string" args))
+       ((44) (apply ##sys#signal-hook #:memory-error loc "segmentation 
violation" args))
+       ((45) (apply ##sys#signal-hook #:arithmetic-error loc "floating-point 
exception" args))
+       ((46) (apply ##sys#signal-hook #:runtime-error loc "illegal 
instruction" args))
+       ((47) (apply ##sys#signal-hook #:memory-error loc "bus error" args))
        (else (apply ##sys#signal-hook #:runtime-error loc "unknown internal 
error" args)) ) ) ) )
 
 
diff --git a/manual/Exceptions b/manual/Exceptions
index 14bdf43..6c1dc53 100644
--- a/manual/Exceptions
+++ b/manual/Exceptions
@@ -83,6 +83,13 @@ argument. If the condition does not have a value for the 
desired property and
 if the optional argument is given, no error is signaled and the accessor
 returns the third argument.
 
+* On platforms that support the {{sigprocmask(3)}} POSIX API function,
+the signals {{SIGSEGV}}, {{SIGFPE}}, {{SIGBUS}} and {{SIGILL}} will be
+caught and trigger an exception instead of aborting the process, if
+possible. If the unwinding and handling of the signal raises one of
+these signals once again, the process will abort with an error
+message.
+
 === Additional API
 
 <macro>(condition-case EXPRESSION CLAUSE ...)</macro>
diff --git a/manual/Unit posix b/manual/Unit posix
index 5376149..66325a6 100644
--- a/manual/Unit posix 
+++ b/manual/Unit posix 
@@ -928,6 +928,8 @@ Notes
 
 * {{(set! (signal-handler SIG) PROC)}} can be used as an alternative to 
{{(set-signal-handler! SIG PROC)}}
 
+* Any signal handlers for the signals {{signal/segv}}, {{signal/bus}}, 
{{signal/fpe}} and {{signal/ill}} will be ignored and these signals will always 
trigger an exception, unless the executable was started with the {{-:S}} 
runtime option. This feature is only available on platforms that support the 
{{sigprocmask(3)}} POSIX API function.
+
 ==== set-signal-mask!
 
 <procedure>(set-signal-mask! SIGLIST)</procedure>
@@ -986,6 +988,7 @@ Unmasks (unblocks) the signal for the code {{SIGNUM}}.
 <constant>signal/xfsz</constant><br>
 <constant>signal/usr1</constant><br>
 <constant>signal/usr2</constant><br>
+<constant>signal/bus</constant><br>
 <constant>signal/winch</constant>
 
 These variables contain signal codes for use with {{process-signal}},  
{{set-signal-handler!}},  {{signal-handler}},  {{signal-masked?}},  
{{signal-mask!}},  or {{signal-unmask!}}.
diff --git a/posix.import.scm b/posix.import.scm
index 0881204..f22a251 100644
--- a/posix.import.scm
+++ b/posix.import.scm
@@ -216,6 +216,7 @@
    signal/chld
    signal/cont
    signal/fpe
+   signal/bus
    signal/hup
    signal/ill
    signal/int
diff --git a/posixunix.scm b/posixunix.scm
index 77d8bca..0277cc5 100644
--- a/posixunix.scm
+++ b/posixunix.scm
@@ -291,6 +291,7 @@ static C_TLS sigset_t C_sigset;
 #define C_sigprocmask_set(d)        C_fix(sigprocmask(SIG_SETMASK, &C_sigset, 
NULL))
 #define C_sigprocmask_block(d)      C_fix(sigprocmask(SIG_BLOCK, &C_sigset, 
NULL))
 #define C_sigprocmask_unblock(d)    C_fix(sigprocmask(SIG_UNBLOCK, &C_sigset, 
NULL))
+#define C_sigprocmask_get(d)        C_fix(sigprocmask(SIG_SETMASK, NULL, 
&C_sigset))
 
 #define C_open(fn, fl, m)   C_fix(open(C_c_string(fn), C_unfix(fl), 
C_unfix(m)))
 #define C_read(fd, b, n)    C_fix(read(C_unfix(fd), C_data_pointer(b), 
C_unfix(n)))
@@ -893,6 +894,7 @@ EOF
 (define-foreign-variable _sighup int "SIGHUP")
 (define-foreign-variable _sigfpe int "SIGFPE")
 (define-foreign-variable _sigill int "SIGILL")
+(define-foreign-variable _sigbus int "SIGBUS")
 (define-foreign-variable _sigsegv int "SIGSEGV")
 (define-foreign-variable _sigabrt int "SIGABRT")
 (define-foreign-variable _sigtrap int "SIGTRAP")
@@ -938,6 +940,7 @@ EOF
 (define signal/usr1 _sigusr1)
 (define signal/usr2 _sigusr2)
 (define signal/winch _sigwinch)
+(define signal/bus _sigbus)
 
 (define signals-list
   (list
@@ -945,7 +948,7 @@ EOF
     signal/segv signal/abrt signal/trap signal/quit signal/alrm signal/vtalrm
     signal/prof signal/io signal/urg signal/chld signal/cont signal/stop
     signal/tstp signal/pipe signal/xcpu signal/xfsz signal/usr1 signal/usr2
-    signal/winch))
+    signal/winch signal/bus))
 
 (define set-signal-mask!
   (lambda (sigs)
@@ -957,30 +960,38 @@ EOF
         (##core#inline "C_sigaddset" s) )
       sigs)
     (when (fx< (##core#inline "C_sigprocmask_set" 0) 0)
-      (posix-error #:process-error 'set-signal-mask! "cannot set signal mask") 
) ) )
+      (posix-error #:process-error 'set-signal-mask! "cannot set signal mask") 
)))
 
-(define (signal-mask)
-  (let loop ([sigs signals-list] [mask '()])
-    (if (null? sigs)
-        mask
-        (let ([sig (car sigs)])
-          (loop (cdr sigs) (if (##core#inline "C_sigismember" sig) (cons sig 
mask) mask)) ) ) ) )
+(define signal-mask
+  (getter-with-setter
+   (lambda ()
+     (##core#inline "C_sigprocmask_get" 0)
+     (let loop ([sigs signals-list] [mask '()])
+       (if (null? sigs)
+          mask
+          (let ([sig (car sigs)])
+            (loop (cdr sigs)
+                  (if (##core#inline "C_sigismember" sig) (cons sig mask) 
mask)) ) ) ) )
+   set-signal-mask!))
 
 (define (signal-masked? sig)
   (##sys#check-exact sig 'signal-masked?)
+  (##core#inline "C_sigprocmask_get" 0)
   (##core#inline "C_sigismember" sig) )
 
 (define (signal-mask! sig)
   (##sys#check-exact sig 'signal-mask!)
+  (##core#inline "C_sigemptyset" 0)
   (##core#inline "C_sigaddset" sig)
   (when (fx< (##core#inline "C_sigprocmask_block" 0) 0)
-      (posix-error #:process-error 'signal-mask! "cannot block signal") )  )
+    (posix-error #:process-error 'signal-mask! "cannot block signal") ))
 
 (define (signal-unmask! sig)
   (##sys#check-exact sig 'signal-unmask!)
-  (##core#inline "C_sigdelset" sig)
+  (##core#inline "C_sigemptyset" 0)
+  (##core#inline "C_sigaddset" sig)
   (when (fx< (##core#inline "C_sigprocmask_unblock" 0) 0)
-      (posix-error #:process-error 'signal-unmask! "cannot unblock signal") )  
)
+    (posix-error #:process-error 'signal-unmask! "cannot unblock signal") ) )
 
 
 ;;; Getting system-, group- and user-information:
diff --git a/runtime.c b/runtime.c
index df27e31..1617c2c 100644
--- a/runtime.c
+++ b/runtime.c
@@ -418,7 +418,9 @@ static C_TLS int
   heap_size_changed,
   chicken_is_running,
   chicken_ran_once,
+  pass_serious_signals = 1,
   callback_continuation_level;
+static volatile C_TLS int serious_signal_occurred = 0;
 static C_TLS unsigned int
   mutation_count,
   stack_size,
@@ -498,6 +500,10 @@ static C_ccall void callback_return_continuation(C_word c, 
C_word self, C_word r
 static void become_2(void *dummy) C_noret;
 static void copy_closure_2(void *dummy) C_noret;
 static void dump_heap_state_2(void *dummy) C_noret;
+static void C_fcall sigsegv_trampoline(void *) C_regparm;
+static void C_fcall sigill_trampoline(void *) C_regparm;
+static void C_fcall sigfpe_trampoline(void *) C_regparm;
+static void C_fcall sigbus_trampoline(void *) C_regparm;
 
 static C_PTABLE_ENTRY *create_initial_ptable();
 
@@ -536,6 +542,7 @@ int CHICKEN_main(int argc, char *argv[], void *toplevel)
 #endif
   }
 
+  pass_serious_signals = 0;
   CHICKEN_parse_command_line(argc, argv, &h, &s, &n);
   
   if(!CHICKEN_initialize(h, s, n, toplevel))
@@ -586,6 +593,9 @@ void parse_argv(C_char *cmds)
 int CHICKEN_initialize(int heap, int stack, int symbols, void *toplevel)
 {
   int i;
+#ifdef HAVE_SIGACTION
+  struct sigaction sa;
+#endif
 
   /*FIXME Should have C_tzset in chicken.h? */
 #ifdef C_NONUNIX
@@ -693,6 +703,28 @@ int CHICKEN_initialize(int heap, int stack, int symbols, 
void *toplevel)
   dlopen_flags = 0;
 #endif
 
+  /* setup signal handlers */
+  if(!pass_serious_signals) {
+#ifdef HAVE_SIGACTION
+    sa.sa_flags = 0;
+    sigemptyset(&sa.sa_mask);
+    sigaddset(&sa.sa_mask, SIGBUS);
+    sigaddset(&sa.sa_mask, SIGFPE);
+    sigaddset(&sa.sa_mask, SIGILL);
+    sigaddset(&sa.sa_mask, SIGSEGV);
+    sa.sa_handler = global_signal_handler;
+    C_sigaction(SIGBUS, &sa, NULL);
+    C_sigaction(SIGFPE, &sa, NULL);
+    C_sigaction(SIGILL, &sa, NULL);
+    C_sigaction(SIGSEGV, &sa, NULL);
+#else
+    C_signal(SIGBUS, global_signal_handler);
+    C_signal(SIGILL, global_signal_handler);
+    C_signal(SIGFPE, global_signal_handler);
+    C_signal(SIGSEGV, global_signal_handler);
+#endif
+  }
+
   mutation_count = gc_count_1 = gc_count_1_total = gc_count_2 = 0;
   lf_list = NULL;
   C_register_lf2(NULL, 0, create_initial_ptable());
@@ -985,12 +1017,71 @@ void initialize_symbol_table(void)
 }
 
 
+C_regparm void C_fcall 
+sigsegv_trampoline(void *dummy)
+{
+  barf(C_MEMORY_VIOLATION_ERROR, NULL);
+}
+
+
+C_regparm void C_fcall 
+sigbus_trampoline(void *dummy)
+{
+  barf(C_BUS_ERROR, NULL);
+}
+
+
+C_regparm void C_fcall 
+sigfpe_trampoline(void *dummy)
+{
+  barf(C_FLOATING_POINT_EXCEPTION_ERROR, NULL);
+}
+
+
+C_regparm void C_fcall 
+sigill_trampoline(void *dummy)
+{
+  barf(C_ILLEGAL_INSTRUCTION_ERROR, NULL);
+}
+
+
 /* This is called from POSIX signals: */
 
 void global_signal_handler(int signum)
 {
+#if defined(HAVE_SIGPROCMASK)
+  if(signum == SIGSEGV || signum == SIGFPE || signum == SIGILL || signum == 
SIGBUS) {
+    sigset_t sset;
+    
+    if(serious_signal_occurred || !chicken_is_running) {
+      switch(signum) {
+      case SIGSEGV: panic(C_text("unrecoverable segmentation violation"));
+      case SIGFPE: panic(C_text("unrecoverable floating-point exception"));
+      case SIGILL: panic(C_text("unrecoverable illegal instruction error"));
+      case SIGBUS: panic(C_text("unrecoverable bus error"));
+      default: panic(C_text("unrecoverable serious condition"));
+      }
+    }
+    else serious_signal_occurred = 1;
+
+    /* unblock signal to avoid nested invocation of the handler */
+    sigemptyset(&sset);
+    sigaddset(&sset, signum);
+    C_sigprocmask(SIG_UNBLOCK, &sset, NULL);
+
+    switch(signum) {
+    case SIGSEGV: C_reclaim(sigsegv_trampoline, NULL);
+    case SIGFPE: C_reclaim(sigfpe_trampoline, NULL);
+    case SIGILL: C_reclaim(sigill_trampoline, NULL);
+    case SIGBUS: C_reclaim(sigbus_trampoline, NULL);
+    default: panic(C_text("invalid serious signal"));
+    }
+  }
+#endif
+
   C_raise_interrupt(signal_mapping_table[ signum ]);
 #ifndef HAVE_SIGACTION
+  /* not necessarily needed, but older UNIXen may not leave the handler 
installed: */
   C_signal(signum, global_signal_handler);
 #endif
 }
@@ -1163,6 +1254,7 @@ void CHICKEN_parse_command_line(int argc, char *argv[], 
C_word *heap, C_word *st
                 " -:G              force GUI mode\n"
                 " -:aSIZE          set trace-buffer/call-chain size\n"
                 " -:H              dump heap state on exit\n"
+                " -:S              do not handle segfaults or other serious 
conditions\n"
                 "\n  SIZE may have a `k' (`K'), `m' (`M') or `g' (`G') suffix, 
meaning size\n"
                 "  times 1024, 1048576, and 1073741824, respectively.\n\n");
          exit(0);
@@ -1205,6 +1297,10 @@ void CHICKEN_parse_command_line(int argc, char *argv[], 
C_word *heap, C_word *st
          dump_heap_on_exit = 1;
          break;
 
+       case 'S':
+         pass_serious_signals = 1;
+         break;
+
        case 's':
          *stack = arg_val(ptr);
          stack_size_changed = 1;
@@ -1322,7 +1418,13 @@ C_word CHICKEN_run(void *toplevel)
     C_dbg(C_text("debug"), C_text("stack bottom is 0x%lx.\n"), 
(long)stack_bottom);
 
   /* The point of (usually) no return... */
+#ifdef HAVE_SIGSETJMP
+  C_sigsetjmp(C_restart, 0);
+#else
   C_setjmp(C_restart);
+#endif
+
+  serious_signal_occurred = 0;
 
   if(!return_to_host)
     (C_restart_trampoline)(C_restart_address);
@@ -1649,6 +1751,11 @@ void barf(int code, char *loc, ...)
     c = 1;
     break;
 
+  case C_MEMORY_VIOLATION_ERROR:
+    msg = C_text("segmentation violation");
+    c = 0;
+    break;
+
   default: panic(C_text("illegal internal error code"));
   }
   
@@ -1797,7 +1904,13 @@ C_word C_fcall C_callback(C_word closure, int argc)
   callback_returned_flag = 0;       
   chicken_is_running = 1;
 
+#ifdef HAVE_SIGSETJMP
+  if(!C_sigsetjmp(C_restart, 0)) C_do_apply(argc, closure, k);
+#else
   if(!C_setjmp(C_restart)) C_do_apply(argc, closure, k);
+#endif
+
+  serious_signal_occurred = 0;
 
   if(!callback_returned_flag) (C_restart_trampoline)(C_restart_address);
   else {
@@ -2657,7 +2770,11 @@ C_regparm void C_fcall C_reclaim(void *trampoline, void 
*proc)
   start = C_fromspace_top;
 
   /* Entry point for second-level GC (on explicit request or because of full 
fromspace): */
+#ifdef HAVE_SIGSETJMP
+  if(C_sigsetjmp(gc_restart, 0) || start >= C_fromspace_limit) {
+#else
   if(C_setjmp(gc_restart) || start >= C_fromspace_limit) {
+#endif
     if(gc_bell) {
       C_putchar(7);
       C_fflush(stdout);
@@ -2943,8 +3060,12 @@ C_regparm void C_fcall C_reclaim(void *trampoline, void 
*proc)
 
   if(C_post_gc_hook != NULL) C_post_gc_hook(gc_mode, (long)tgc);
 
-  /* Jump from the Empire State Building... */
+  /* Unwind stack completely */
+#ifdef HAVE_SIGSETJMP
+  C_siglongjmp(C_restart, 1);
+#else
   C_longjmp(C_restart, 1);
+#endif
 }
 
 
@@ -3011,7 +3132,11 @@ C_regparm void C_fcall really_mark(C_word *x)
     bytes = (h & C_BYTEBLOCK_BIT) ? n : n * sizeof(C_word);
 
     if(((C_byte *)p2 + bytes + sizeof(C_word)) > C_fromspace_limit)
+#ifdef HAVE_SIGSETJMP
+      C_siglongjmp(gc_restart, 1);
+#else
       C_longjmp(gc_restart, 1);
+#endif
 
     C_fromspace_top = (C_byte *)p2 + C_align(bytes) + sizeof(C_word);
 
@@ -3080,7 +3205,11 @@ C_regparm void C_fcall really_mark(C_word *x)
        panic(C_text("out of memory - heap full"));
       
       gc_mode = GC_REALLOC;
+#ifdef HAVE_SIGSETJMP
+      C_siglongjmp(gc_restart, 1);
+#else
       C_longjmp(gc_restart, 1);
+#endif
     }
 
     tospace_top = (C_byte *)p2 + C_align(bytes) + sizeof(C_word);
diff --git a/types.db b/types.db
index 20fc763..1d92ab9 100644
--- a/types.db
+++ b/types.db
@@ -1756,6 +1756,7 @@
 (signal/ill fixnum)
 (signal/int fixnum)
 (signal/io fixnum)
+(signal/bus fixnum)
 (signal/kill fixnum)
 (signal/pipe fixnum)
 (signal/prof fixnum)
-- 
1.7.0.4


reply via email to

[Prev in Thread] Current Thread [Next in Thread]