Forum: Ruby-dev [ruby-trunk - Bug #7141][Open] ALT_STACK_SIZE is not enough

Posted by authorNari (Narihiro Nakamura) (Guest)
on 2012-10-11 14:56
(Received via mailing list)
Issue #7141 has been reported by authorNari (Narihiro Nakamura).

----------------------------------------
Bug #7141: ALT_STACK_SIZE is not enough
https://bugs.ruby-lang.org/issues/7141

Author: authorNari (Narihiro Nakamura)
Status: Open
Priority: Normal
Assignee:
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 <sys/mman.h>
+
 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=<optimized out>, 
format=<optimized out>) 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  <signal handler called>
....
(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
Posted by kosaki (Motohiro KOSAKI) (Guest)
on 2012-10-12 00:29
(Received via mailing list)
Issue #7141 has been updated by kosaki (Motohiro KOSAKI).

Status changed from Open to Assigned
Assignee set to kosaki (Motohiro KOSAKI)

あー、では私のミスっぽいのでちょっと預からさせてくださいませ
----------------------------------------
Bug #7141: ALT_STACK_SIZE is not enough
https://bugs.ruby-lang.org/issues/7141#change-30347

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 <sys/mman.h>
+
 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=<optimized out>, 
format=<optimized out>) 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  <signal handler called>
....
(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
Posted by kosaki (Motohiro KOSAKI) (Guest)
on 2012-12-15 14:31
(Received via mailing list)
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 <sys/mman.h>
+
 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=<optimized out>, 
format=<optimized out>) 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  <signal handler called>
....
(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
Posted by kosaki (Motohiro KOSAKI) (Guest)
on 2012-12-15 15:38
(Received via mailing list)
Issue #7141 has been updated by kosaki (Motohiro KOSAKI).


こういうのも取れました。ようするにglibcのvfprintfもruby内蔵のvfprintfと同じぐらいスタックを使うので(おそらく理由も同じ)
まったく同じことが起きているようです。
これは、そもそもfprintfを使っているのが間違いなので、全部書き換えるべきなんですが、2.0では単純にスタックサイズを増やして
逃げます。


altstack: 0x7ffff7d92000-0x7ffff7d93000...0x7ffff7d94000-0x7ffff7d95000

                                         $rsp 
stack_usage

#0  _IO_vfprintf_internal  0x7ffff7d92f80  1728
#1  ___fprintf_chk    0x7ffff7d93640  240
#2  fprintf      0x7ffff7d93730  0
#3  rb_vmdebug_stack_dump_raw  0x7ffff7d93730
#4  rb_vm_bugreport
#5  report_bug
#6  rb_bug
#7  sigsegv
#8  <signal handler called>


----------------------------------------
Bug #7141: ALT_STACK_SIZE is not enough
https://bugs.ruby-lang.org/issues/7141#change-34765

Author: authorNari (Narihiro Nakamura)
Status: Closed
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 <sys/mman.h>
+
 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=<optimized out>, 
format=<optimized out>) 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  <signal handler called>
....
(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
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.