diff options
-rw-r--r-- | ChangeLog | 30 | ||||
-rw-r--r-- | gc.c | 16 | ||||
-rw-r--r-- | signal.c | 49 | ||||
-rw-r--r-- | thread.c | 8 | ||||
-rw-r--r-- | thread_pthread.c | 104 |
5 files changed, 185 insertions, 22 deletions
@@ -1,3 +1,33 @@ +Thu Nov 27 23:54:37 2008 Yukihiro Matsumoto <matz@ruby-lang.org> + + * gc.c (gc_mark): still needs to check stack depth during GC. + + * gc.c (stack_check): ditto. + +Thu Nov 27 16:32:53 2008 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * signal.c (register_sigaltstack): stores alt stack for debug + purpose. + +Thu Nov 27 16:12:33 2008 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * signal.c (ruby_sigaction_t): added. + +Thu Nov 27 15:59:16 2008 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * gc.c (ruby_stack_check): no check if using sigaltstack. + + * signal.c (ALT_STACK_SIZE): default minimum size is insufficient + for method calls. + + * signal.c (sigsegv): handles stack overflow if possible. + + * thread.c (ruby_thread_stack_overflow): helper function to raise + sysstack_error. + + * thread_pthread.c (ruby_stack_overflowed_p): checks for stack + overflow. + Wed Nov 26 23:15:47 2008 Yukihiro Matsumoto <matz@ruby-lang.org> * strftime.c (STRFTIME): use rb_strftime() recursively, instead of @@ -1047,8 +1047,8 @@ ruby_stack_length(VALUE **p) return STACK_LENGTH; } -int -ruby_stack_check(void) +static int +stack_check(void) { int ret; rb_thread_t *th = GET_THREAD(); @@ -1063,6 +1063,16 @@ ruby_stack_check(void) return ret; } +int +ruby_stack_check(void) +{ +#if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK) + return 0; +#else + return stack_check(); +#endif +} + static void init_mark_stack(rb_objspace_t *objspace) { @@ -1269,7 +1279,7 @@ gc_mark(rb_objspace_t *objspace, VALUE ptr, int lev) if (obj->as.basic.flags & FL_MARK) return; /* already marked */ obj->as.basic.flags |= FL_MARK; - if (lev > GC_LEVEL_MAX || (lev == 0 && ruby_stack_check())) { + if (lev > GC_LEVEL_MAX || (lev == 0 && stack_check())) { if (!mark_stack_overflow) { if (mark_stack_ptr - mark_stack < MARK_STACK_MAX) { *mark_stack_ptr = ptr; @@ -408,6 +408,13 @@ static struct { #endif typedef RETSIGTYPE (*sighandler_t)(int); +#ifdef SA_SIGINFO +typedef void ruby_sigaction_t(int, siginfo_t*, void*); +#define SIGINFO_ARG , siginfo_t *info, void *ctx +#else +typedef RETSIGTYPE ruby_sigaction_t(int); +#define SIGINFO_ARG +#endif #ifdef POSIX_SIGNAL #if defined(SIGSEGV) && defined(HAVE_SIGALTSTACK) @@ -416,31 +423,31 @@ typedef RETSIGTYPE (*sighandler_t)(int); #ifdef USE_SIGALTSTACK #ifdef SIGSTKSZ -#define ALT_STACK_SIZE SIGSTKSZ +#define ALT_STACK_SIZE (SIGSTKSZ*2) #else #define ALT_STACK_SIZE (4*1024) #endif /* alternate stack for SIGSEGV */ static void -register_sigaltstack() +register_sigaltstack(void) { - static int is_altstack_defined = 0; + static void *altstack = 0; stack_t newSS, oldSS; - if (is_altstack_defined) - return; + if (altstack) return; - newSS.ss_sp = malloc(ALT_STACK_SIZE); + newSS.ss_sp = altstack = malloc(ALT_STACK_SIZE); if (newSS.ss_sp == NULL) - /* should handle error */ - rb_bug("register_sigaltstack. malloc error\n"); + /* should handle error */ + rb_bug("register_sigaltstack. malloc error\n"); newSS.ss_size = ALT_STACK_SIZE; newSS.ss_flags = 0; if (sigaltstack(&newSS, &oldSS) < 0) - rb_bug("register_sigaltstack. error\n"); - is_altstack_defined = 1; + rb_bug("register_sigaltstack. error\n"); } +#else +#define register_sigaltstack() ((void)0) #endif static sighandler_t @@ -454,7 +461,7 @@ ruby_signal(int signum, sighandler_t handler) sigemptyset(&sigact.sa_mask); #ifdef SA_SIGINFO - sigact.sa_sigaction = (void (*)(int, siginfo_t*, void*))handler; + sigact.sa_sigaction = (ruby_sigaction_t*)handler; sigact.sa_flags = SA_SIGINFO; #else sigact.sa_handler = handler; @@ -467,7 +474,7 @@ ruby_signal(int signum, sighandler_t handler) #endif #if defined(SA_ONSTACK) && defined(USE_SIGALTSTACK) if (signum == SIGSEGV) - sigact.sa_flags |= SA_ONSTACK; + sigact.sa_flags |= SA_ONSTACK; #endif if (sigaction(signum, &sigact, &old) < 0) rb_bug("sigaction error.\n"); @@ -570,8 +577,16 @@ sigbus(int sig) #ifdef SIGSEGV static int segv_received = 0; static RETSIGTYPE -sigsegv(int sig) +sigsegv(int sig SIGINFO_ARG) { +#ifdef USE_SIGALTSTACK + int ruby_stack_overflowed_p(const rb_thread_t *, const void *); + NORETURN(void ruby_thread_stack_overflow(rb_thread_t *th)); + rb_thread_t *th = GET_THREAD(); + if (ruby_stack_overflowed_p(th, info->si_addr)) { + ruby_thread_stack_overflow(th); + } +#endif if (segv_received) { fprintf(stderr, "SEGV recieved in SEGV handler\n"); exit(EXIT_FAILURE); @@ -700,10 +715,8 @@ default_handler(int sig) #endif #ifdef SIGSEGV case SIGSEGV: - func = sigsegv; -#ifdef USE_SIGALTSTACK + func = (sighandler_t)sigsegv; register_sigaltstack(); -#endif break; #endif #ifdef SIGPIPE @@ -1111,10 +1124,8 @@ Init_signal(void) install_sighandler(SIGBUS, sigbus); #endif #ifdef SIGSEGV -#ifdef USE_SIGALTSTACK register_sigaltstack(); -#endif - install_sighandler(SIGSEGV, sigsegv); + install_sighandler(SIGSEGV, (sighandler_t)sigsegv); #endif } #ifdef SIGPIPE @@ -1184,6 +1184,14 @@ rb_thread_signal_exit(void *thptr) rb_thread_raise(2, argv, th->vm->main_thread); } +void +ruby_thread_stack_overflow(rb_thread_t *th) +{ + th->errinfo = sysstack_error; + th->raised_flag = 0; + TH_JUMP_TAG(th, TAG_RAISE); +} + int rb_thread_set_raised(rb_thread_t *th) { diff --git a/thread_pthread.c b/thread_pthread.c index 7ddfcea7b0..a492d7f02d 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -180,6 +180,75 @@ native_thread_destroy(rb_thread_t *th) #define USE_THREAD_CACHE 0 +#if STACK_GROW_DIRECTION +#define STACK_GROW_DIR_DETECTION +#define STACK_DIR_UPPER(a,b) STACK_UPPER(0, a, b) +#else +#define STACK_GROW_DIR_DETECTION VALUE stack_grow_dir_detection +#define STACK_DIR_UPPER(a,b) STACK_UPPER(&stack_grow_dir_detection, a, b) +#endif + +#if defined HAVE_PTHREAD_GETATTR_NP || defined HAVE_PTHREAD_ATTR_GET_NP +#define STACKADDR_AVAILABLE 1 +#elif defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP +#define STACKADDR_AVAILABLE 1 +#elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP +#define STACKADDR_AVAILABLE 1 +#endif + +#ifdef STACKADDR_AVAILABLE +static int +get_stack(void **addr, size_t *size) +{ +#define CHECK_ERR(expr) \ + {int err = (expr); if (err) return err;} +#if defined HAVE_PTHREAD_GETATTR_NP || defined HAVE_PTHREAD_ATTR_GET_NP + pthread_attr_t attr; + size_t guard = 0; + +# ifdef HAVE_PTHREAD_GETATTR_NP + CHECK_ERR(pthread_getattr_np(pthread_self(), &attr)); +# ifdef HAVE_PTHREAD_ATTR_GETSTACK + CHECK_ERR(pthread_attr_getstack(&attr, addr, size)); +# else + CHECK_ERR(pthread_attr_getstackaddr(&attr, addr)); + CHECK_ERR(pthread_attr_getstacksize(&attr, size)); +# endif + if (pthread_attr_getguardsize(&attr, &guard) == 0) { + STACK_GROW_DIR_DETECTION; + STACK_DIR_UPPER((void)0, *addr = (char *)*addr + guard); + *size -= guard; + } +# else + CHECK_ERR(pthread_attr_init(&attr)); + CHECK_ERR(pthread_attr_get_np(pthread_self(), &attr)); + CHECK_ERR(pthread_attr_getstackaddr(&attr, addr)); + CHECK_ERR(pthread_attr_getstacksize(&attr, size)); +# endif + CHECK_ERR(pthread_attr_getguardsize(&attr, &guard)); +# ifndef HAVE_PTHREAD_GETATTR_NP + pthread_attr_destroy(&attr); +# endif + size -= guard; +#elif defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP + pthread_t th = pthread_self(); + *addr = pthread_get_stackaddr_np(th); + *size = pthread_get_stacksize_np(th); +#elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP + stack_t stk; +# if defined HAVE_THR_STKSEGMENT + CHECK_ERR(thr_stksegment(&stk)); +# else + CHECK_ERR(pthread_stackseg_np(pthread_self(), &stk)); +# endif + *addr = stk.ss_sp; + *size = stk.ss_size; +#endif + return 0; +#undef CHECK_ERR +} +#endif + static struct { rb_thread_id_t id; size_t stack_maxsize; @@ -742,4 +811,39 @@ native_stop_timer_thread(void) return stopped; } +#ifdef HAVE_SIGALTSTACK +int +ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr) +{ + void *base; + size_t size; + const size_t water_mark = 1024 * 1024; + STACK_GROW_DIR_DETECTION; + + if (th) { + size = th->machine_stack_maxsize; + base = (char *)th->machine_stack_start - STACK_DIR_UPPER(0, size); + } +#ifdef STACKADDR_AVAILABLE + else if (get_stack(&base, &size) == 0) { + STACK_DIR_UPPER(base = (char *)base + size, (void)0); + } +#endif + else { + return 0; + } + size /= 5; + if (size > water_mark) size = water_mark; + if (STACK_DIR_UPPER(1, 0)) { + if (size > ~(size_t)base+1) size = ~(size_t)base+1; + if (addr > base && addr <= (void *)((char *)base + size)) return 1; + } + else { + if (size > (size_t)base) size = (size_t)base; + if (addr > (void *)((char *)base - size) && addr <= base) return 1; + } + return 0; +} +#endif + #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */ |