From 6ae5083f999d32c2f564375bc5054b1f877b5a18 Mon Sep 17 00:00:00 2001 From: Ian Ker-Seymer Date: Fri, 3 Apr 2026 23:58:22 -0400 Subject: [PATCH] gc: prevent LTO from eliminating rb_gc_before_fork GCC LTO with -flto=auto creates a constprop clone of rb_fork_ruby where rb_multi_ractor_p() is constant false. In that clone, rb_gc_vm_barrier() and RB_GC_VM_LOCK() are both no-ops, so rb_gc_impl_before_fork reduces to a trivial store and GCC eliminates the entire call chain. This leaves no GC barrier before fork(2). RBIMPL_ATTR_NOINLINE() on rb_gc_before_fork prevents GCC from inlining it into the constprop clone, forcing it to treat the call as having unknown side effects. Additionally, call gc_rest() at the top of rb_gc_impl_before_fork to complete any in-progress incremental GC cycle before forking. This ensures the child does not inherit a mid-cycle heap. --- gc.c | 1 + gc/default/default.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/gc.c b/gc.c index 0afba4f22c52a0..4fbd74afb820a3 100644 --- a/gc.c +++ b/gc.c @@ -5435,6 +5435,7 @@ rb_obj_info_dump_loc(VALUE obj, const char *file, int line, const char *func) fprintf(stderr, " %s\n", func, file, line, rb_raw_obj_info(buff, 0x100, obj)); } +RBIMPL_ATTR_NOINLINE() void rb_gc_before_fork(void) { diff --git a/gc/default/default.c b/gc/default/default.c index 6c2b13c9f37d0b..d5a1525b66f374 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -9407,6 +9407,8 @@ rb_gc_impl_before_fork(void *objspace_ptr) { rb_objspace_t *objspace = objspace_ptr; + gc_rest(objspace); + objspace->fork_vm_lock_lev = RB_GC_VM_LOCK(); rb_gc_vm_barrier(); }