зеркало из https://github.com/github/ruby.git
[Bug #19469] Fix crash when resizing generic iv list
The following script can sometimes trigger a crash: ```ruby GC.stress = true class Array def foo(bool) if bool @a = 1 @b = 2 @c = 1 else @c = 1 end end end obj = [] obj.foo(true) obj2 = [] obj2.foo(false) obj3 = [] obj3.foo(true) ``` This is because vm_setivar_default calls rb_ensure_generic_iv_list_size to resize the iv list. However, the call to gen_ivtbl_resize reallocs the iv list, and then inserts into the generic iv table. If the st_insert triggers a GC then the old iv list will be read during marking, causing a use-after-free bug. Co-Authored-By: Jemma Issroff <jemmaissroff@gmail.com>
This commit is contained in:
Родитель
0700d0fd1c
Коммит
62c2082f1f
|
@ -57,7 +57,7 @@ VALUE rb_gvar_defined(ID);
|
||||||
void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID);
|
void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID);
|
||||||
rb_shape_t * rb_grow_iv_list(VALUE obj);
|
rb_shape_t * rb_grow_iv_list(VALUE obj);
|
||||||
void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize);
|
void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize);
|
||||||
struct gen_ivtbl * rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize);
|
struct gen_ivtbl *rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize);
|
||||||
attr_index_t rb_obj_ivar_set(VALUE obj, ID id, VALUE val);
|
attr_index_t rb_obj_ivar_set(VALUE obj, ID id, VALUE val);
|
||||||
MJIT_SYMBOL_EXPORT_END
|
MJIT_SYMBOL_EXPORT_END
|
||||||
|
|
||||||
|
|
23
variable.c
23
variable.c
|
@ -66,7 +66,10 @@ static st_table *generic_iv_tbl_;
|
||||||
struct ivar_update {
|
struct ivar_update {
|
||||||
struct gen_ivtbl *ivtbl;
|
struct gen_ivtbl *ivtbl;
|
||||||
uint32_t iv_index;
|
uint32_t iv_index;
|
||||||
rb_shape_t* shape;
|
uint32_t max_index;
|
||||||
|
#if !SHAPE_IN_BASIC_FLAGS
|
||||||
|
rb_shape_t *shape;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1009,7 +1012,7 @@ generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FL_SET((VALUE)*k, FL_EXIVAR);
|
FL_SET((VALUE)*k, FL_EXIVAR);
|
||||||
ivtbl = gen_ivtbl_resize(ivtbl, ivup->shape->next_iv_index);
|
ivtbl = gen_ivtbl_resize(ivtbl, ivup->max_index);
|
||||||
// Reinsert in to the hash table because ivtbl might be a newly resized chunk of memory
|
// Reinsert in to the hash table because ivtbl might be a newly resized chunk of memory
|
||||||
*v = (st_data_t)ivtbl;
|
*v = (st_data_t)ivtbl;
|
||||||
ivup->ivtbl = ivtbl;
|
ivup->ivtbl = ivtbl;
|
||||||
|
@ -1272,7 +1275,10 @@ generic_ivar_set(VALUE obj, ID id, VALUE val)
|
||||||
RUBY_ASSERT(index == (shape->next_iv_index - 1));
|
RUBY_ASSERT(index == (shape->next_iv_index - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ivup.max_index = shape->next_iv_index;
|
||||||
|
#if !SHAPE_IN_BASIC_FLAGS
|
||||||
ivup.shape = shape;
|
ivup.shape = shape;
|
||||||
|
#endif
|
||||||
|
|
||||||
RB_VM_LOCK_ENTER();
|
RB_VM_LOCK_ENTER();
|
||||||
{
|
{
|
||||||
|
@ -1373,15 +1379,22 @@ rb_ensure_iv_list_size(VALUE obj, uint32_t current_capacity, uint32_t new_capaci
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gen_ivtbl *
|
struct gen_ivtbl *
|
||||||
rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize)
|
rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize)
|
||||||
{
|
{
|
||||||
struct gen_ivtbl * ivtbl = 0;
|
struct gen_ivtbl * ivtbl = 0;
|
||||||
|
|
||||||
RB_VM_LOCK_ENTER();
|
RB_VM_LOCK_ENTER();
|
||||||
{
|
{
|
||||||
if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) {
|
if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) {
|
||||||
ivtbl = gen_ivtbl_resize(ivtbl, newsize);
|
struct ivar_update ivup = {
|
||||||
st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)obj, (st_data_t)ivtbl);
|
.iv_index = newsize - 1,
|
||||||
|
.max_index = newsize,
|
||||||
|
#if !SHAPE_IN_BASIC_FLAGS
|
||||||
|
.shape = shape
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
st_update(generic_ivtbl_no_ractor_check(obj), (st_data_t)obj, generic_ivar_update, (st_data_t)&ivup);
|
||||||
|
ivtbl = ivup.ivtbl;
|
||||||
FL_SET_RAW(obj, FL_EXIVAR);
|
FL_SET_RAW(obj, FL_EXIVAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1416,11 +1416,11 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_i
|
||||||
shape_id_t source_shape_id = dest_shape->parent_id;
|
shape_id_t source_shape_id = dest_shape->parent_id;
|
||||||
|
|
||||||
if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) {
|
if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) {
|
||||||
ivtbl = rb_ensure_generic_iv_list_size(obj, index + 1);
|
ivtbl = rb_ensure_generic_iv_list_size(obj, dest_shape, index + 1);
|
||||||
#if SHAPE_IN_BASIC_FLAGS
|
#if SHAPE_IN_BASIC_FLAGS
|
||||||
RBASIC_SET_SHAPE_ID(obj, dest_shape_id);
|
RBASIC_SET_SHAPE_ID(obj, dest_shape_id);
|
||||||
#else
|
#else
|
||||||
ivtbl->shape_id = dest_shape_id;
|
RUBY_ASSERT(ivtbl->shape_id == dest_shape_id);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче