Skip to content

Commit 74a5df8

Browse files
committed
Fix crash when instantiating classes in Ractors
[Bug #18119] When we create classes, it pushes the class to the subclass list of the superclass. This access needs to be synchronized because multiple Ractors may be creating classes with the same superclass, which would cause race conditions and cause the linked list to be corrupted. For example, we can reproduce with this script crashing: workers = (0...8).map do Ractor.new do loop do 100.times.map { Class.new } Ractor.yield nil end end end 100.times { Ractor.select(*workers) } With ASAN enabled, we can see that there are use-after-free errors: ==176013==ERROR: AddressSanitizer: heap-use-after-free on address 0x5030000974f0 at pc 0x62f9e56f892d bp 0x7a503f1ffd90 sp 0x7a503f1ffd88 WRITE of size 8 at 0x5030000974f0 thread T4 #0 0x62f9e56f892c in rb_class_remove_from_super_subclasses class.c:149:24 #1 0x62f9e58c9dd2 in rb_gc_obj_free gc.c:1262:9 ruby#2 0x62f9e58f6e19 in gc_sweep_plane gc/default/default.c:3450:21 ruby#3 0x62f9e58f686a in gc_sweep_page gc/default/default.c:3535:13 ruby#4 0x62f9e58f12b4 in gc_sweep_step gc/default/default.c:3810:9 ruby#5 0x62f9e58ed2a7 in gc_sweep gc/default/default.c:4058:13 ruby#6 0x62f9e58fac93 in gc_start gc/default/default.c:6402:13 ruby#7 0x62f9e58e8b69 in heap_prepare gc/default/default.c:2032:13 ruby#8 0x62f9e58e8b69 in heap_next_free_page gc/default/default.c:2255:9 ruby#9 0x62f9e58e8b69 in newobj_cache_miss gc/default/default.c:2362:38 ... 0x5030000974f0 is located 16 bytes inside of 24-byte region [0x5030000974e0,0x5030000974f8) freed by thread T4 here: #0 0x62f9e562f28a in free (miniruby+0x1fd28a) (BuildId: 5ad6d9e7cec8318df6726ea5ce34d3c76d0d0233) #1 0x62f9e58ca2ab in rb_gc_impl_free gc/default/default.c:8102:9 ruby#2 0x62f9e58ca2ab in ruby_sized_xfree gc.c:5029:13 ruby#3 0x62f9e58ca2ab in ruby_xfree gc.c:5040:5 ruby#4 0x62f9e56f88e6 in rb_class_remove_from_super_subclasses class.c:152:9 ruby#5 0x62f9e58c9dd2 in rb_gc_obj_free gc.c:1262:9 ruby#6 0x62f9e58f6e19 in gc_sweep_plane gc/default/default.c:3450:21 ruby#7 0x62f9e58f686a in gc_sweep_page gc/default/default.c:3535:13 ruby#8 0x62f9e58f12b4 in gc_sweep_step gc/default/default.c:3810:9 ruby#9 0x62f9e58ed2a7 in gc_sweep gc/default/default.c:4058:13 ... previously allocated by thread T5 here: #0 0x62f9e562f70d in calloc (miniruby+0x1fd70d) (BuildId: 5ad6d9e7cec8318df6726ea5ce34d3c76d0d0233) #1 0x62f9e58c8e1a in calloc1 gc/default/default.c:1472:12 ruby#2 0x62f9e58c8e1a in rb_gc_impl_calloc gc/default/default.c:8138:5 ruby#3 0x62f9e58c8e1a in ruby_xcalloc_body gc.c:4964:12 ruby#4 0x62f9e58c8e1a in ruby_xcalloc gc.c:4958:34 ruby#5 0x62f9e56f906e in push_subclass_entry_to_list class.c:88:13 ruby#6 0x62f9e56f906e in rb_class_subclass_add class.c:111:38 ruby#7 0x62f9e56f906e in RCLASS_SET_SUPER internal/class.h:257:9 ruby#8 0x62f9e56fca7a in make_metaclass class.c:786:5 ruby#9 0x62f9e59db982 in rb_class_initialize object.c:2101:5
1 parent e3452cf commit 74a5df8

File tree

2 files changed

+32
-13
lines changed

2 files changed

+32
-13
lines changed

bootstraptest/test_ractor.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,3 +2259,20 @@ def initialize(a)
22592259
:ok
22602260
end
22612261
}
2262+
2263+
# Creating classes inside of Ractors
2264+
# [Bug #18119]
2265+
assert_equal 'ok', %q{
2266+
workers = (0...8).map do
2267+
Ractor.new do
2268+
loop do
2269+
100.times.map { Class.new }
2270+
Ractor.yield nil
2271+
end
2272+
end
2273+
end
2274+
2275+
100.times { Ractor.select(*workers) }
2276+
2277+
'ok'
2278+
}

class.c

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,23 +83,25 @@ RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
8383
static rb_subclass_entry_t *
8484
push_subclass_entry_to_list(VALUE super, VALUE klass)
8585
{
86-
rb_subclass_entry_t *entry, *head;
87-
88-
entry = ZALLOC(rb_subclass_entry_t);
86+
rb_subclass_entry_t *entry = ZALLOC(rb_subclass_entry_t);
8987
entry->klass = klass;
9088

91-
head = RCLASS_SUBCLASSES(super);
92-
if (!head) {
93-
head = ZALLOC(rb_subclass_entry_t);
94-
RCLASS_SUBCLASSES(super) = head;
95-
}
96-
entry->next = head->next;
97-
entry->prev = head;
89+
RB_VM_LOCK_ENTER();
90+
{
91+
rb_subclass_entry_t *head = RCLASS_SUBCLASSES(super);
92+
if (!head) {
93+
head = ZALLOC(rb_subclass_entry_t);
94+
RCLASS_SUBCLASSES(super) = head;
95+
}
96+
entry->next = head->next;
97+
entry->prev = head;
9898

99-
if (head->next) {
100-
head->next->prev = entry;
99+
if (head->next) {
100+
head->next->prev = entry;
101+
}
102+
head->next = entry;
101103
}
102-
head->next = entry;
104+
RB_VM_LOCK_LEAVE();
103105

104106
return entry;
105107
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy