From c416dbb3c700173d62c2d8381e3680b8aa4482ae Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 27 Apr 2022 12:23:52 -0400 Subject: [PATCH] Add missing write barriers to Array#replace Previously it made object references without using write barriers, creating GC inconsistencies. See: http://ci.rvm.jp/results/trunk-gc-asserts@phosphorus-docker/3925529 --- array.c | 6 ++++-- test/ruby/test_array.rb | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/array.c b/array.c index 9bb767d79f8887..7b3f5bd0b0a9bc 100644 --- a/array.c +++ b/array.c @@ -4680,14 +4680,16 @@ rb_ary_replace(VALUE copy, VALUE orig) * contents of orig. */ else if (ARY_EMBED_P(orig)) { long len = ARY_EMBED_LEN(orig); - VALUE *ptr = ary_heap_alloc(copy, len); - MEMCPY(ptr, ARY_EMBED_PTR(orig), VALUE, len); FL_UNSET_EMBED(copy); ARY_SET_PTR(copy, ptr); ARY_SET_LEN(copy, len); ARY_SET_CAPA(copy, len); + + // No allocation and exception expected that could leave `copy` in a + // bad state from the edits above. + ary_memcpy(copy, 0, len, RARRAY_CONST_PTR_TRANSIENT(orig)); } #endif /* Otherwise, orig is on heap and copy does not have enough space to embed diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index b100cf334ce918..e376d76a169d86 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1439,6 +1439,14 @@ def test_replace assert_raise(FrozenError) { fa.replace(42) } end + def test_replace_wb_variable_width_alloc + small_embed = [] + 4.times { GC.start } # age small_embed + large_embed = [1, 2, 3, 4, 5, Array.new] # new young object + small_embed.replace(large_embed) # adds old to young reference + GC.verify_internal_consistency + end + def test_reverse a = @cls[*%w( dog cat bee ant )] assert_equal(@cls[*%w(ant bee cat dog)], a.reverse)