Skip to content

Commit 80e37cb

Browse files
committed
Do not pass a raw pointer as the recursion key to rb_exec_recursive()
The spec added in 20ae94d passed a pointer to a 4-byte C stack struct as both the `obj` and `arg` arguments of rb_exec_recursive(). While `arg` is passed through to the callback as-is, `obj` is used as a key of the recursion guard hash in CRuby. Storing it there fires the GC write barrier, which reads the (8-byte) object flags from the non-object address, and AddressSanitizer reports a stack-buffer-overflow: ``` ==29649==ERROR: AddressSanitizer: stack-buffer-overflow ... READ of size 8 at 0x7f0d9b7c7b20 thread T0 #0 RB_BUILTIN_TYPE include/ruby/internal/value_type.h:191:30 #1 rb_gc_impl_writebarrier gc/default/default.c:6621:5 ... #6 exec_recursive thread.c:5602:13 #7 kernel_spec_rb_exec_recursive_with_pointer spec/ruby/optional/capi/ext/kernel_spec.c:370:10 ``` This crash has been blocking the daily `ruby-head` releases of ruby/ruby-dev-builder (`build (ubuntu-24.04, asan)`) since 2026-07-08: https://github.com/ruby/ruby-dev-builder/actions/runs/29044853326 As a result, downstream CIs that use `ruby-head` via ruby/setup-ruby are stuck with the 2026-07-06 snapshot, which predates ruby/ruby@291d0d95ea ("onig_reg_copy_body: fix copying of exact_end"). This is how the problem was found: the "Spec - ubuntu head" job on the default branch of rubocop/rubocop keeps failing because `Regexp.new(regexp)` copies made for the `AllowedPatterns` config silently fail to match on that snapshot: https://github.com/rubocop/rubocop/actions/runs/29061954887/job/86265559997 The intent of the spec is that the callback argument is passed as-is, so keep the raw pointer for `arg` and use a real Ruby object as the recursion key.
1 parent 072057e commit 80e37cb

1 file changed

Lines changed: 2 additions & 3 deletions

File tree

‎optional/capi/ext/kernel_spec.c‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,8 @@ struct rb_exec_recursive_pointer_data {
352352
};
353353

354354
static VALUE do_rec_pointer(VALUE obj, VALUE arg, int is_rec) {
355-
struct rb_exec_recursive_pointer_data *obj_data = (struct rb_exec_recursive_pointer_data *)obj;
356355
struct rb_exec_recursive_pointer_data *arg_data = (struct rb_exec_recursive_pointer_data *)arg;
357-
if (obj_data->magic != 0x1234 || arg_data->magic != 0x1234) {
356+
if (arg_data->magic != 0x1234) {
358357
rb_raise(rb_eRuntimeError, "invalid recursive pointer");
359358
}
360359

@@ -367,7 +366,7 @@ static VALUE do_rec_pointer(VALUE obj, VALUE arg, int is_rec) {
367366

368367
static VALUE kernel_spec_rb_exec_recursive_with_pointer(VALUE self) {
369368
struct rb_exec_recursive_pointer_data data = { 0x1234 };
370-
return rb_exec_recursive(do_rec_pointer, (VALUE)&data, (VALUE)&data);
369+
return rb_exec_recursive(do_rec_pointer, self, (VALUE)&data);
371370
}
372371

373372
static void write_io(VALUE io) {

0 commit comments

Comments
 (0)