From 4c426e98a89015de0ccbd52f3ceb92aa71d31bb4 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 15 Oct 2025 13:53:57 -0400 Subject: [PATCH] ZJIT: Use rb_gc_disable() over rb_gc_disable_no_rest() no_rest() trips an assert inside the GC when we allocate with the GC disabled this way: (gc_continue) ../src/gc/default/default.c:2029 (newobj_cache_miss+0x128) [0x105040048] ../src/gc/default/default.c:2370 (rb_gc_impl_new_obj+0x7c) [0x105036374] ../src/gc/default/default.c:2482 (newobj_of) ../src/gc.c:995 (rb_method_entry_alloc+0x40) [0x1051e6c64] ../src/vm_method.c:1102 (rb_method_entry_complement_defined_class) ../src/vm_method.c:1180 (prepare_callable_method_entry+0x14c) [0x1051e87b8] ../src/vm_method.c:1728 (callable_method_entry_or_negative+0x1e8) [0x1051e809c] ../src/vm_method.c:1874 It's tries to continue the GC because it was out of space. Looks like it's not safe to allocate new objects after using rb_gc_disable_no_rest(); existing usages use it for malloc calls. --- zjit/bindgen/src/main.rs | 2 +- zjit/src/cruby.rs | 2 +- zjit/src/cruby_bindings.inc.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 77d482db4e..43fec09014 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -130,7 +130,7 @@ fn main() { .allowlist_function("rb_singleton_class") .allowlist_function("rb_define_class") .allowlist_function("rb_class_get_superclass") - .allowlist_function("rb_gc_disable_no_rest") + .allowlist_function("rb_gc_disable") .allowlist_function("rb_gc_enable") .allowlist_function("rb_gc_mark") .allowlist_function("rb_gc_mark_movable") diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 4eb1d3a17c..dca4d91805 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -897,7 +897,7 @@ where // 2. If we yield to the GC while compiling, it re-enters our mark and update functions. // This breaks `&mut` exclusivity since mark functions derive fresh `&mut` from statics // while there is a stack frame below it that has an overlapping `&mut`. That's UB. - let gc_disabled_pre_call = unsafe { rb_gc_disable_no_rest() }.test(); + let gc_disabled_pre_call = unsafe { rb_gc_disable() }.test(); let ret = match catch_unwind(func) { Ok(result) => result, diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 6a6263ab15..17cda12a0b 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -746,6 +746,7 @@ unsafe extern "C" { pub fn rb_gc_mark_movable(obj: VALUE); pub fn rb_gc_location(obj: VALUE) -> VALUE; pub fn rb_gc_enable() -> VALUE; + pub fn rb_gc_disable() -> VALUE; pub fn rb_gc_writebarrier(old: VALUE, young: VALUE); pub fn rb_class_get_superclass(klass: VALUE) -> VALUE; pub static mut rb_cObject: VALUE; @@ -877,7 +878,6 @@ unsafe extern "C" { buff_size: usize, obj: VALUE, ) -> *const ::std::os::raw::c_char; - pub fn rb_gc_disable_no_rest() -> VALUE; pub fn rb_ec_stack_check(ec: *mut rb_execution_context_struct) -> ::std::os::raw::c_int; pub fn rb_gc_writebarrier_remember(obj: VALUE); pub fn rb_shape_id_offset() -> i32;