aboutsummaryrefslogtreecommitdiffstats
path: root/yjit
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2023-09-14 17:18:45 -0400
committerGitHub <noreply@github.com>2023-09-14 17:18:45 -0400
commit1961c5bb767a451928dc719d37c2b38f89d248c6 (patch)
tree9e92d1585057253c5349ed458af97c6d5f66ad07 /yjit
parent66ffa15ce01e1b8d46738032e714be18194af3ca (diff)
downloadruby-1961c5bb767a451928dc719d37c2b38f89d248c6.tar.gz
YJIT: Plug native stack overflow
Previously, TestStack#test_machine_stack_size failed pretty consistently on ARM64 macOS, with Rust code and part of the interpreter used for per-instruction fallback (rb_vm_invokeblock() and friends) touching the stack guard page and crashing with SEGV. I've also seen the same test fail on x64 Linux, though with a different symptom.
Diffstat (limited to 'yjit')
-rw-r--r--yjit/bindgen/src/main.rs1
-rw-r--r--yjit/src/core.rs12
-rw-r--r--yjit/src/cruby_bindings.inc.rs1
-rw-r--r--yjit/src/yjit.rs5
4 files changed, 16 insertions, 3 deletions
diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs
index baa71fc6cf..db93672a3d 100644
--- a/yjit/bindgen/src/main.rs
+++ b/yjit/bindgen/src/main.rs
@@ -449,6 +449,7 @@ fn main() {
.allowlist_function("rb_obj_class")
.allowlist_function("rb_obj_is_proc")
.allowlist_function("rb_vm_base_ptr")
+ .allowlist_function("rb_ec_stack_check")
// We define VALUE manually, don't import it
.blocklist_type("VALUE")
diff --git a/yjit/src/core.rs b/yjit/src/core.rs
index 57b742724b..8261051315 100644
--- a/yjit/src/core.rs
+++ b/yjit/src/core.rs
@@ -2482,9 +2482,6 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
(target.get_blockid(), target.get_ctx())
};
- let cb = CodegenGlobals::get_inline_cb();
- let ocb = CodegenGlobals::get_outlined_cb();
-
let (cfp, original_interp_sp) = unsafe {
let cfp = get_ec_cfp(ec);
let original_interp_sp = get_cfp_sp(cfp);
@@ -2506,9 +2503,18 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
// So we do it here instead.
rb_set_cfp_sp(cfp, reconned_sp);
+ // Bail if we're about to run out of native stack space.
+ // We've just reconstructed interpreter state.
+ if rb_ec_stack_check(ec as _) != 0 {
+ return CodegenGlobals::get_stub_exit_code().raw_ptr();
+ }
+
(cfp, original_interp_sp)
};
+ let cb = CodegenGlobals::get_inline_cb();
+ let ocb = CodegenGlobals::get_outlined_cb();
+
// Try to find an existing compiled version of this block
let mut block = find_block_version(target_blockid, &target_ctx);
let mut branch_modified = false;
diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs
index ccf5b530d6..3e2961aa72 100644
--- a/yjit/src/cruby_bindings.inc.rs
+++ b/yjit/src/cruby_bindings.inc.rs
@@ -1173,6 +1173,7 @@ extern "C" {
) -> *const rb_callable_method_entry_t;
pub fn rb_obj_info(obj: VALUE) -> *const ::std::os::raw::c_char;
pub fn rb_class_allocate_instance(klass: VALUE) -> VALUE;
+ pub fn rb_ec_stack_check(ec: *mut rb_execution_context_struct) -> ::std::os::raw::c_int;
pub fn rb_shape_id_offset() -> i32;
pub fn rb_shape_get_shape_by_id(shape_id: shape_id_t) -> *mut rb_shape_t;
pub fn rb_shape_get_shape_id(obj: VALUE) -> shape_id_t;
diff --git a/yjit/src/yjit.rs b/yjit/src/yjit.rs
index 97799923d2..2aed3c6a4b 100644
--- a/yjit/src/yjit.rs
+++ b/yjit/src/yjit.rs
@@ -114,6 +114,11 @@ fn rb_bug_panic_hook() {
/// See [jit_compile_exception] for details.
#[no_mangle]
pub extern "C" fn rb_yjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exception: bool) -> *const u8 {
+ // Don't compile when there is insufficient native stack space
+ if unsafe { rb_ec_stack_check(ec as _) } != 0 {
+ return std::ptr::null();
+ }
+
// Reject ISEQs with very large temp stacks,
// this will allow us to use u8/i8 values to track stack_size and sp_offset
let stack_max = unsafe { rb_get_iseq_body_stack_max(iseq) };