From 055fef00a1c27fdc8293114dc134ca7910b1dc79 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 16 Jun 2025 20:58:31 -0700 Subject: [PATCH] Free after insert in generic_ivar_set_shape_fields Previously we were performing a realloc and then inserting the new value into the table. If the table was flagged as requiring a rebuild, this could trigger GC work and marking within that GC could access the fields freed by realloc. --- variable.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/variable.c b/variable.c index 6bd9f69d06..eb232e52cf 100644 --- a/variable.c +++ b/variable.c @@ -1227,12 +1227,6 @@ gen_fields_tbl_bytes(size_t n) return offsetof(struct gen_fields_tbl, as.shape.fields) + n * sizeof(VALUE); } -static struct gen_fields_tbl * -gen_fields_tbl_resize(struct gen_fields_tbl *old, uint32_t new_capa) -{ - RUBY_ASSERT(new_capa > 0); - return xrealloc(old, gen_fields_tbl_bytes(new_capa)); -} void rb_mark_generic_ivar(VALUE obj) @@ -1837,13 +1831,28 @@ generic_ivar_set_shape_fields(VALUE obj, void *data) int existing = st_lookup(tbl, (st_data_t)obj, (st_data_t *)&fields_tbl); if (!existing || fields_lookup->resize) { + uint32_t new_capa = RSHAPE_CAPACITY(fields_lookup->shape_id); + uint32_t old_capa = RSHAPE_CAPACITY(RSHAPE_PARENT(fields_lookup->shape_id)); + if (existing) { RUBY_ASSERT(RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_OBJ_ID)); - RUBY_ASSERT(RSHAPE_CAPACITY(RSHAPE_PARENT(fields_lookup->shape_id)) < RSHAPE_CAPACITY(fields_lookup->shape_id)); + RUBY_ASSERT(old_capa < new_capa); + RUBY_ASSERT(fields_tbl); + } else { + RUBY_ASSERT(!fields_tbl); + RUBY_ASSERT(old_capa == 0); } + RUBY_ASSERT(new_capa > 0); - fields_tbl = gen_fields_tbl_resize(fields_tbl, RSHAPE_CAPACITY(fields_lookup->shape_id)); + struct gen_fields_tbl *old_fields_tbl = fields_tbl; + fields_tbl = xmalloc(gen_fields_tbl_bytes(new_capa)); + if (old_fields_tbl) { + memcpy(fields_tbl, old_fields_tbl, gen_fields_tbl_bytes(old_capa)); + } st_insert(tbl, (st_data_t)obj, (st_data_t)fields_tbl); + if (old_fields_tbl) { + xfree(old_fields_tbl); + } } if (fields_lookup->shape_id) { @@ -2371,7 +2380,9 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj) return; } - new_fields_tbl = gen_fields_tbl_resize(0, RSHAPE_CAPACITY(dest_shape_id)); + uint32_t dest_capa = RSHAPE_CAPACITY(dest_shape_id); + RUBY_ASSERT(dest_capa > 0); + new_fields_tbl = xmalloc(gen_fields_tbl_bytes(dest_capa)); VALUE *src_buf = obj_fields_tbl->as.shape.fields; VALUE *dest_buf = new_fields_tbl->as.shape.fields;