From: "kosaki (Motohiro KOSAKI)" Date: 2012-12-15T22:30:49+09:00 Subject: [ruby-dev:46741] [ruby-trunk - Bug #7141] ALT_STACK_SIZE is not enough Issue #7141 has been updated by kosaki (Motohiro KOSAKI). うちではこんな感じになりました。 setarch x86_64 -R ./miniruby -e 'Process.kill :SEGV, $$' ----------------------------------------- altstack: 0x7ffff7d83000-0x7ffff7d84000...0x7ffff7d85000-0x7ffff7d86000 $RSP stack_usage stack_usage_total #0 BSD_vfprintf 0x7ffff7d83e60 1520 #2 control_frame_dump 0x7ffff7d84450 320 1840 #3 rb_vmdebug_stack_dump_raw 0x7ffff7d84590 288 2128 #4 rb_vm_bugreport 0x7ffff7d846b0 328 2456 #5 report_bug 0x7ffff7d84820 336 2792 #6 rb_bug 0x7ffff7d84970 272 3064 #7 sigsegv 0x7ffff7d84a80 原因は BSD_vfprintfが char buf[1024] とやっているのが主犯のようですが、これはfloatを使わないときは 68でOKみたいなんで、float使う時だけallocaで増やすのがよさそう。 2.0では時間がないので単純に ALT_STACK_SIZE を増やすので逃げられそう。いまが4096なんで8192まで 増やせばまず大丈夫。 それはそれとして、シグナルハンドラからfprintf呼んでるのはデッドロックの危険があるので、 snprintf + writeに書き換えないとダメじゃないかな。将来的には (rubyは自前のsnprintfを持ってるのでthread safeかつasync signal safeで動きそう) ---------------------------------------- Bug #7141: ALT_STACK_SIZE is not enough https://bugs.ruby-lang.org/issues/7141#change-34763 Author: authorNari (Narihiro Nakamura) Status: Assigned Priority: Normal Assignee: kosaki (Motohiro KOSAKI) Category: core Target version: 2.0.0 ruby -v: ruby 2.0.0dev (2012-10-05 trunk 36982) [x86_64-linux] nariです。 以下で教えていただいたバックトレースが出ない件をもう少し追いかけてみた ところ、どうもシグナルハンドラ内でスタックオーバーフローしているような 気がしています。 http://bugs.ruby-lang.org/issues/7095#note-6 r37088 のコミットで一応問題は再現しなくなったのですが、たぶんこれはスタッ クを突き破ってメモリ破壊したときに、たまたまセグメント違反にならないよ うなメモリの配置になったためだと思われます。 以下のようにaltstackの周りをmprotectして確認したところLinux64bit環境で もSEGVが発生しました。 --- パッチ --- diff --git a/gc.c b/gc.c index f1f7aaa..dbe3c3d 100644 --- a/gc.c +++ b/gc.c @@ -588,6 +588,8 @@ add_heap_slots(rb_objspace_t *objspace, size_t add) heaps_inc = 0; } +#include + static void init_heap(rb_objspace_t *objspace) { @@ -599,7 +601,17 @@ init_heap(rb_objspace_t *objspace) /* altstack of another threads are allocated in another place */ rb_thread_t *th = GET_THREAD(); void *tmp = th->altstack; - th->altstack = malloc(ALT_STACK_SIZE); + VALUE atmp = 0; + th->altstack = mmap(NULL, ALT_STACK_SIZE+80000, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + th->altstack = ((VALUE)th->altstack)+40000; + mprotect((void *)(((VALUE)th->altstack)-40000), 40000, PROT_NONE); + mprotect((void *)(((VALUE)th->altstack)+ALT_STACK_SIZE), 40000, PROT_NONE); + atmp = (VALUE)th->altstack; + fprintf(stderr, "altstack: %p-%p...%p-%p\n", + atmp-4000, atmp, atmp+ALT_STACK_SIZE, atmp+ALT_STACK_SIZE+4000); free(tmp); /* free previously allocated area */ } #endif diff --git a/vm.c b/vm.c index ae201dc..95d8202 100644 --- a/vm.c +++ b/vm.c @@ -1736,7 +1736,7 @@ thread_free(void *ptr) else { #ifdef USE_SIGALTSTACK if (th->altstack) { - free(th->altstack); + /* free(th->altstack); */ } #endif ruby_xfree(ptr); --- ここまで --- パッチを当てた後に./minirubyを実行。 % ./miniruby -e 'Process.kill :SEGV, $$' altstack: 0x7fd3f23a7000-0x7fd3f23a7190...0x7fd3f23a8190-0x7fd3f23a8320 -e:1: [BUG] Segmentation fault ruby 2.0.0dev (2012-10-05 trunk 36982) [x86_64-linux] -- Control frame information ----------------------------------------------- zsh: segmentation fault (core dumped) ./miniruby -e 'Process.kill :SEGV, $$' % ./miniruby -v altstack: 0x7f7ee72fb000-0x7f7ee72fb190...0x7f7ee72fc190-0x7f7ee72fc320 ruby 2.0.0dev (2012-10-05 trunk 36982) [x86_64-linux] 以下はgdbの抜粋です。 % gdb ./miniruby GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) 7.4-2012.04 ... (gdb) r -e 'Process.kill :SEGV, $$' ... Continuing. -e:1: [BUG] Segmentation fault ruby 2.0.0dev (2012-10-05 trunk 36982) [x86_64-linux] -- Control frame information ----------------------------------------------- Program received signal SIGSEGV, Segmentation fault. buffered_vfprintf (s=0x7ffff747c180, format=0x5555557358af "c:%04td ", args=0x7ffff7ff76a8) at vfprintf.c:2313 2313 vfprintf.c: No such file or directory. (gdb) bt #0 buffered_vfprintf (s=0x7ffff747c180, format=0x5555557358af "c:%04td ", args=0x7ffff7ff76a8) at vfprintf.c:2313 #1 0x00007ffff710bbfe in _IO_vfprintf_internal (s=0x7ffff747c180, format=0x5555557358af "c:%04td ", ap=0x7ffff7ff76a8) at vfprintf.c:1316 #2 0x00007ffff7116857 in __fprintf (stream=, format=) at fprintf.c:33 #3 0x00005555556fad8a in control_frame_dump (th=0x5555559de530, cfp=0x7ffff7fcff08) at vm_dump.c:111 #4 0x00005555556fafbf in rb_vmdebug_stack_dump_raw (th=0x5555559de530, cfp=0x7ffff7fcff08) at vm_dump.c:163 #5 0x00005555556fb626 in rb_vm_bugreport () at vm_dump.c:610 #6 0x00005555555b1599 in report_bug (file=0x555555a4dad8 "-e", line=1, fmt=0x55555572ebaf "Segmentation fault", args=0x7ffff7ff7b58) at error.c:306 #7 0x00005555555b16b6 in rb_bug (fmt=0x55555572ebaf "Segmentation fault") at error.c:325 #8 0x000055555568085e in sigsegv (sig=11, info=0x7ffff7ff7db0, ctx=0x7ffff7ff7c80) at signal.c:607 #9 .... (gdb) i f 0 Stack frame at 0x7ffff7ff7030: ... Locals at 0x7ffff7ff4ed8, Previous frame's sp is 0x7ffff7ff7030 ... (gdb) i f 8 Stack frame at 0x7ffff7ff7c80: ... (gdb) p 0x7ffff7ff4ed8 - 0x7ffff7ff7c80 $1 = -11688 一番先頭のフレームのLocalsが0x7ffff7ff4ed8で末尾が0x7ffff7ff7c80なので どうもスタックオーバーフローっぽいのですがどうでしょう…。 とりあえず、ALT_STACK_SIZEを5倍くらいすると現象は再現しなくなりました。 diff --git a/vm_core.h b/vm_core.h index 8d51407..13f12d4 100644 --- a/vm_core.h +++ b/vm_core.h @@ -416,9 +416,9 @@ struct rb_unblock_callback { struct rb_mutex_struct; #ifdef MINSIGSTKSZ -#define ALT_STACK_SIZE (MINSIGSTKSZ*2) +#define ALT_STACK_SIZE (MINSIGSTKSZ*10) #else -#define ALT_STACK_SIZE (4*1024) +#define ALT_STACK_SIZE (20*1024) #endif typedef struct rb_thread_struct { 以下のチケットも参考にしました。 http://bugs.ruby-lang.org/issues/5139 -- http://bugs.ruby-lang.org/