~ chicken-core (chicken-5) 8d05f1f09297c7a8ca75b1440865c33e12ad4ea6


commit 8d05f1f09297c7a8ca75b1440865c33e12ad4ea6
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Sun Oct 7 22:48:21 2012 +0200
Commit:     Peter Bex <peter.bex@xs4all.nl>
CommitDate: Sat Oct 27 16:47:49 2012 +0200

    catch serious signals (SIGSEGV, SIGILL, SIGBUS, SIGPE)
    
    Signed-off-by: Peter Bex <peter.bex@xs4all.nl>

diff --git a/Makefile.bsd b/Makefile.bsd
index 4402c039..fb1f244f 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 3e1c5585..ff06c5bd 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 c55d55e9..b04e7f6e 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 a0a73fae..dbdefe3c 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 a946d0c9..516b607d 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 bd30e624..680687fb 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 14bdf436..6c1dc53b 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 5376149c..66325a6a 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 0881204c..f22a251e 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 77d8bca4..0277cc59 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 df27e315..1617c2cf 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 20fc7636..1d92ab9b 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)
Trap