summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2025-03-13 13:26:38 +0900
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2025-03-13 14:27:06 +0900
commitf89a334b555d9f91f0bb5a7c58b3097960dd7fb8 (patch)
tree56d82266863cb679e57820bc5986bfdd71ab5743
parent2c4951329122143b5e0375caf1d4481173e403cb (diff)
merge revision(s) bccec7fb468ad977be75e7e4c2644b4ea845ab0c, 5f8ebcada099351acbc22db264e7cd3773c2bdc4, e13575bb7938e9e5b6a79bfca1b3793123f479da, 4adcfc8cd7a17593a6590025da2b03eebf4fd63c: [Backport #19584]
Fix crash in rb_gc_register_address [Bug #19584] Some C extensions pass a pointer to a global variable to rb_gc_register_address. However, if a GC is triggered inside of rb_gc_register_address, then the object could get swept since it does not exist on the stack. [Bug #19584] Register global variable address before assignment [Bug #19584] Register global variables before assignment [Bug #19584] [DOC] Tweek description of `rb_gc_register_address`
-rw-r--r--gc.c13
-rw-r--r--include/ruby/internal/gc.h13
-rw-r--r--io.c7
-rw-r--r--ruby.c2
-rw-r--r--spec/ruby/optional/capi/ext/gc_spec.c6
-rw-r--r--version.h2
6 files changed, 30 insertions, 13 deletions
diff --git a/gc.c b/gc.c
index 46d2516fa5..919d57989a 100644
--- a/gc.c
+++ b/gc.c
@@ -9281,10 +9281,23 @@ rb_gc_register_address(VALUE *addr)
rb_objspace_t *objspace = &rb_objspace;
struct gc_list *tmp;
+ VALUE obj = *addr;
+
tmp = ALLOC(struct gc_list);
tmp->next = global_list;
tmp->varptr = addr;
global_list = tmp;
+
+ /*
+ * Because some C extensions have assignment-then-register bugs,
+ * we guard `obj` here so that it would not get swept defensively.
+ */
+ RB_GC_GUARD(obj);
+ if (0 && !SPECIAL_CONST_P(obj)) {
+ rb_warn("Object is assigned to registering address already: %"PRIsVALUE,
+ rb_obj_class(obj));
+ rb_print_backtrace();
+ }
}
void
diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h
index 66fc14e511..054e4b0f9c 100644
--- a/include/ruby/internal/gc.h
+++ b/include/ruby/internal/gc.h
@@ -26,10 +26,15 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
/**
- * Inform the garbage collector that `valptr` points to a live Ruby object that
- * should not be moved. Note that extensions should use this API on global
- * constants instead of assuming constants defined in Ruby are always alive.
- * Ruby code can remove global constants.
+ * Inform the garbage collector that the global or static variable pointed by
+ * `valptr` stores a live Ruby object that should not be moved. Note that
+ * extensions should use this API on global constants instead of assuming
+ * constants defined in Ruby are always alive. Ruby code can remove global
+ * constants.
+ *
+ * Because this registration itself has a possibility to trigger a GC, this
+ * function must be called before any GC-able objects is assigned to the
+ * address pointed by `valptr`.
*/
void rb_gc_register_address(VALUE *valptr);
diff --git a/io.c b/io.c
index e4be073267..99ec59da29 100644
--- a/io.c
+++ b/io.c
@@ -15547,13 +15547,12 @@ Init_IO(void)
rb_gvar_ractor_local("$>");
rb_gvar_ractor_local("$stderr");
- rb_stdin = rb_io_prep_stdin();
- rb_stdout = rb_io_prep_stdout();
- rb_stderr = rb_io_prep_stderr();
-
rb_global_variable(&rb_stdin);
+ rb_stdin = rb_io_prep_stdin();
rb_global_variable(&rb_stdout);
+ rb_stdout = rb_io_prep_stdout();
rb_global_variable(&rb_stderr);
+ rb_stderr = rb_io_prep_stderr();
orig_stdout = rb_stdout;
orig_stderr = rb_stderr;
diff --git a/ruby.c b/ruby.c
index 920a2490d2..09c81db86c 100644
--- a/ruby.c
+++ b/ruby.c
@@ -647,8 +647,8 @@ ruby_init_loadpath(void)
# endif
rb_obj_hide(selfpath);
OBJ_FREEZE_RAW(selfpath);
- rb_libruby_selfpath = selfpath;
rb_gc_register_address(&rb_libruby_selfpath);
+ rb_libruby_selfpath = selfpath;
# endif
#endif
diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c
index 082e4af59c..b323c2456d 100644
--- a/spec/ruby/optional/capi/ext/gc_spec.c
+++ b/spec/ruby/optional/capi/ext/gc_spec.c
@@ -28,8 +28,8 @@ static VALUE get_registered_before_rb_global_variable(VALUE self) {
}
static VALUE gc_spec_rb_gc_register_address(VALUE self) {
- rb_gc_register_address_outside_init = rb_str_new_cstr("rb_gc_register_address() outside Init_");
rb_gc_register_address(&rb_gc_register_address_outside_init);
+ rb_gc_register_address_outside_init = rb_str_new_cstr("rb_gc_register_address() outside Init_");
return rb_gc_register_address_outside_init;
}
@@ -67,14 +67,14 @@ static VALUE gc_spec_rb_gc_register_mark_object(VALUE self, VALUE obj) {
void Init_gc_spec(void) {
VALUE cls = rb_define_class("CApiGCSpecs", rb_cObject);
- registered_tagged_value = INT2NUM(10);
- registered_reference_value = rb_str_new2("Globally registered data");
rb_gc_register_address(&registered_tagged_value);
rb_gc_register_address(&registered_reference_value);
rb_gc_register_address(&registered_before_rb_gc_register_address);
rb_global_variable(&registered_before_rb_global_variable);
+ registered_tagged_value = INT2NUM(10);
+ registered_reference_value = rb_str_new2("Globally registered data");
registered_before_rb_gc_register_address = rb_str_new_cstr("registered before rb_gc_register_address()");
registered_before_rb_global_variable = rb_str_new_cstr("registered before rb_global_variable()");
diff --git a/version.h b/version.h
index 832cf2b4a5..057246acaa 100644
--- a/version.h
+++ b/version.h
@@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 7
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 258
+#define RUBY_PATCHLEVEL 259
#include "ruby/version.h"
#include "ruby/internal/abi.h"