diff options
author | Peter Zhu <peter@peterzhu.ca> | 2022-12-21 15:09:27 -0500 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2022-12-22 09:23:40 -0500 |
commit | d7388f720c706d94f21ee4e169678a71c73ce37c (patch) | |
tree | 2066b14be77756a043dd6ca91a0deb6d42c44623 /variable.c | |
parent | d1d61cabbc332790c0d53fea01f51d52a3489670 (diff) |
Fix buffer overrun with auto-compact for shapes
The following script crashes:
```ruby
GC.auto_compact = true
GC.stress = true
class Foo
def initialize
@a = @b = @c = 0
end
def add_ivars
@d = @e = @f = 0
end
end
ary = 1_000.times.map { Foo.new }
ary.each { |f| f.add_ivars }
```
This is because in rb_grow_iv_list, it first calls
rb_ensure_iv_list_size to allocate the buffer (and also unsets the
embed bit) then rb_shape_transition_shape_capa to get the new shape.
However, auto-compact can trigger in rb_shape_transition_shape_capa
which would re-embed the object since it doesn't have the new shape yet.
This causes a crash as the object is now embedded but has a non-embed
shape which would cause the object to have a buffer overrun.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/6986
Diffstat (limited to 'variable.c')
-rw-r--r-- | variable.c | 5 |
1 files changed, 4 insertions, 1 deletions
diff --git a/variable.c b/variable.c index 0c283738aa..0fd4a80887 100644 --- a/variable.c +++ b/variable.c @@ -1409,10 +1409,13 @@ rb_grow_iv_list(VALUE obj) uint32_t len = initial_shape->capacity; RUBY_ASSERT(len > 0); uint32_t newsize = (uint32_t)(len * 2); - rb_ensure_iv_list_size(obj, len, newsize); rb_shape_t * res = rb_shape_transition_shape_capa(initial_shape, newsize); + + rb_ensure_iv_list_size(obj, len, newsize); + rb_shape_set_shape(obj, res); + return res; } |