aboutsummaryrefslogtreecommitdiffstats
path: root/variable.c
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-02-28 14:59:33 -0500
committerPeter Zhu <peter@peterzhu.ca>2023-03-03 16:12:03 -0500
commit62c2082f1f726cb90d8c332fbedbecf41d5d82ec (patch)
treef768c3103ed1ad0d0e1fa3ab00a253af08cd6e79 /variable.c
parent0700d0fd1c77b4fddf803dea3c10be654df600ff (diff)
downloadruby-62c2082f1f726cb90d8c332fbedbecf41d5d82ec.tar.gz
[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>
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c23
1 files changed, 18 insertions, 5 deletions
diff --git a/variable.c b/variable.c
index 1a417da08e..e55819eae3 100644
--- a/variable.c
+++ b/variable.c
@@ -66,7 +66,10 @@ static st_table *generic_iv_tbl_;
struct ivar_update {
struct gen_ivtbl *ivtbl;
uint32_t iv_index;
- rb_shape_t* shape;
+ uint32_t max_index;
+#if !SHAPE_IN_BASIC_FLAGS
+ rb_shape_t *shape;
+#endif
};
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);
- 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
*v = (st_data_t)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));
}
+ ivup.max_index = shape->next_iv_index;
+#if !SHAPE_IN_BASIC_FLAGS
ivup.shape = shape;
+#endif
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 *
-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;
RB_VM_LOCK_ENTER();
{
if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) {
- ivtbl = gen_ivtbl_resize(ivtbl, newsize);
- st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)obj, (st_data_t)ivtbl);
+ struct ivar_update ivup = {
+ .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);
}
}