Issue #7141 has been reported by authorNari (Narihiro N.).
Bug #7141: ALT_STACK_SIZE is not enough
Author: authorNari (Narihiro N.)
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です。
以下で教えていただいたバックトレースが出ない件をもう少し追いかけてみた
ところ、どうもシグナルハンドラ内でスタックオーバーフローしているような
気がしています。
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=,
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 (MINSIGSTKSZ2)
+#define ALT_STACK_SIZE (MINSIGSTKSZ10)
#else
-#define ALT_STACK_SIZE (41024)
+#define ALT_STACK_SIZE (201024)
#endif
typedef struct rb_thread_struct {
以下のチケットも参考にしました。