diff --git a/ext/-test-/stack/extconf.rb b/ext/-test-/stack/extconf.rb new file mode 100644 index 00000000000000..d786b15db98c7f --- /dev/null +++ b/ext/-test-/stack/extconf.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: false +require_relative "../auto_ext.rb" +auto_ext(inc: true) diff --git a/ext/-test-/stack/stack.c b/ext/-test-/stack/stack.c new file mode 100644 index 00000000000000..d69ca4da8413dd --- /dev/null +++ b/ext/-test-/stack/stack.c @@ -0,0 +1,24 @@ +#include "ruby.h" +#include "internal/string.h" + +static VALUE +stack_alloca_overflow(VALUE self) +{ + size_t i = 0; + + while (1) { + // Allocate and touch memory to force actual stack usage: + volatile char *stack = alloca(1024); + stack[0] = (char)i; + stack[1023] = (char)i; + i++; + } + + return Qnil; +} + +void +Init_stack(VALUE klass) +{ + rb_define_singleton_method(rb_cThread, "alloca_overflow", stack_alloca_overflow, 0); +} diff --git a/test/-ext-/stack/test_stack_overflow.rb b/test/-ext-/stack/test_stack_overflow.rb new file mode 100644 index 00000000000000..eadf6d292a0391 --- /dev/null +++ b/test/-ext-/stack/test_stack_overflow.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true +require 'test/unit' + +class Test_StackOverflow < Test::Unit::TestCase + def test_proc_overflow + omit("Windows stack overflow handling is missing") if RUBY_PLATFORM =~ /mswin|win32|mingw/ + + assert_separately([], <<~RUBY) + # GC may try to scan the top of the stack and cause a SEGV. + GC.disable + require '-test-/stack' + + assert_raise(SystemStackError) do + Thread.alloca_overflow + end + RUBY + end + + def test_thread_stack_overflow + omit("Windows stack overflow handling is missing") if RUBY_PLATFORM =~ /mswin|win32|mingw/ + + assert_separately([], <<~RUBY) + require '-test-/stack' + GC.disable + + thread = Thread.new do + Thread.current.report_on_exception = false + Thread.alloca_overflow + end + + assert_raise(SystemStackError) do + thread.join + end + RUBY + end + + def test_fiber_stack_overflow + omit("Windows stack overflow handling is missing") if RUBY_PLATFORM =~ /mswin|win32|mingw/ + + assert_separately([], <<~RUBY) + require '-test-/stack' + GC.disable + + fiber = Fiber.new do + Thread.alloca_overflow + end + + assert_raise(SystemStackError) do + fiber.resume + end + RUBY + end +end diff --git a/thread_pthread.c b/thread_pthread.c index 993437f02b1183..195fe69948780a 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -3149,8 +3149,12 @@ ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr) const size_t water_mark = 1024 * 1024; STACK_GROW_DIR_DETECTION; + if (th) { + size = th->ec->machine.stack_maxsize; + base = (char *)th->ec->machine.stack_start - STACK_DIR_UPPER(0, size); + } #ifdef STACKADDR_AVAILABLE - if (get_stack(&base, &size) == 0) { + else if (get_stack(&base, &size) == 0) { # ifdef __APPLE__ if (pthread_equal(th->nt->thread_id, native_main_thread.id)) { struct rlimit rlim; @@ -3161,15 +3165,11 @@ ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr) # endif base = (char *)base + STACK_DIR_UPPER(+size, -size); } - else #endif - if (th) { - size = th->ec->machine.stack_maxsize; - base = (char *)th->ec->machine.stack_start - STACK_DIR_UPPER(0, size); - } else { return 0; } + size /= RUBY_STACK_SPACE_RATIO; if (size > water_mark) size = water_mark; if (IS_STACK_DIR_UPPER()) {