summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2026-01-28 13:39:33 +0100
committerJean Boussier <jean.boussier@gmail.com>2026-01-29 23:32:04 +0100
commit91619f0230c0e5a95c796c1bd4f784c151e15614 (patch)
treeb8cf37fc6092a51f318996f382ab9503ba390100
parent457bb11aa5b2ce4424b611acb489686d130261de (diff)
gc.c: Verify provided size in `rb_gc_impl_free`
For now the provided size is just for GC statistics, but in the future we may want to forward it to C23's `free_sized` and passing an incorrect size to it is undefined behavior.
-rw-r--r--array.c5
-rw-r--r--ext/-test-/string/cstr.c3
-rw-r--r--gc/default/default.c20
-rw-r--r--imemo.c14
-rw-r--r--internal/gc.h32
-rw-r--r--internal/imemo.h2
-rw-r--r--prism_compile.c2
-rw-r--r--set.c4
-rw-r--r--string.c6
-rw-r--r--vm_eval.c2
10 files changed, 40 insertions, 50 deletions
diff --git a/array.c b/array.c
index 4496dde262..4a4c44562d 100644
--- a/array.c
+++ b/array.c
@@ -387,13 +387,14 @@ rb_ary_make_embedded(VALUE ary)
if (!ARY_EMBED_P(ary)) {
const VALUE *buf = ARY_HEAP_PTR(ary);
long len = ARY_HEAP_LEN(ary);
+ long capa = ARY_HEAP_CAPA(ary);
FL_SET_EMBED(ary);
ARY_SET_EMBED_LEN(ary, len);
MEMCPY((void *)ARY_EMBED_PTR(ary), (void *)buf, VALUE, len);
- ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
+ ary_heap_free_ptr(ary, buf, capa * sizeof(VALUE));
}
}
@@ -428,7 +429,7 @@ ary_resize_capa(VALUE ary, long capacity)
if (len > capacity) len = capacity;
MEMCPY((VALUE *)RARRAY(ary)->as.ary, ptr, VALUE, len);
- ary_heap_free_ptr(ary, ptr, old_capa);
+ ary_heap_free_ptr(ary, ptr, old_capa * sizeof(VALUE));
FL_SET_EMBED(ary);
ARY_SET_LEN(ary, len);
diff --git a/ext/-test-/string/cstr.c b/ext/-test-/string/cstr.c
index b0b1ef5374..931220b46b 100644
--- a/ext/-test-/string/cstr.c
+++ b/ext/-test-/string/cstr.c
@@ -111,9 +111,10 @@ bug_str_s_cstr_noembed(VALUE self, VALUE str)
FL_SET((str2), STR_NOEMBED);
memcpy(buf, RSTRING_PTR(str), capacity);
RBASIC(str2)->flags &= ~(STR_SHARED | FL_USER5 | FL_USER6);
- RSTRING(str2)->as.heap.aux.capa = capacity;
+ RSTRING(str2)->as.heap.aux.capa = RSTRING_LEN(str);
RSTRING(str2)->as.heap.ptr = buf;
RSTRING(str2)->len = RSTRING_LEN(str);
+
TERM_FILL(RSTRING_END(str2), TERM_LEN(str));
return str2;
}
diff --git a/gc/default/default.c b/gc/default/default.c
index 5758fe1885..0920ccb11f 100644
--- a/gc/default/default.c
+++ b/gc/default/default.c
@@ -301,9 +301,24 @@ int ruby_rgengc_debug;
#ifndef GC_ENABLE_LAZY_SWEEP
# define GC_ENABLE_LAZY_SWEEP 1
#endif
+
+#ifndef VERIFY_FREE_SIZE
+#if RUBY_DEBUG
+#define VERIFY_FREE_SIZE 1
+#else
+#define VERIFY_FREE_SIZE 0
+#endif
+#endif
+
+#if VERIFY_FREE_SIZE
+#undef CALC_EXACT_MALLOC_SIZE
+#define CALC_EXACT_MALLOC_SIZE 1
+#endif
+
#ifndef CALC_EXACT_MALLOC_SIZE
# define CALC_EXACT_MALLOC_SIZE 0
#endif
+
#if defined(HAVE_MALLOC_USABLE_SIZE) || CALC_EXACT_MALLOC_SIZE > 0
# ifndef MALLOC_ALLOCATED_SIZE
# define MALLOC_ALLOCATED_SIZE 0
@@ -8255,6 +8270,11 @@ rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size)
}
#if CALC_EXACT_MALLOC_SIZE
struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1;
+#if VERIFY_FREE_SIZE
+ if (old_size && (old_size + sizeof(struct malloc_obj_info)) != info->size) {
+ rb_bug("buffer %p freed with size %lu, but was allocated with size %lu", ptr, old_size, info->size - sizeof(struct malloc_obj_info));
+ }
+#endif
ptr = info;
old_size = info->size;
#endif
diff --git a/imemo.c b/imemo.c
index d949466a77..0f7c260eb9 100644
--- a/imemo.c
+++ b/imemo.c
@@ -57,7 +57,7 @@ rb_imemo_tmpbuf_new(void)
rb_gc_register_pinning_obj((VALUE)obj);
obj->ptr = NULL;
- obj->cnt = 0;
+ obj->size = 0;
return (VALUE)obj;
}
@@ -71,7 +71,7 @@ rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t size, size_t cnt)
*store = (VALUE)tmpbuf;
void *ptr = ruby_xmalloc(size);
tmpbuf->ptr = ptr;
- tmpbuf->cnt = cnt;
+ tmpbuf->size = size;
return ptr;
}
@@ -94,9 +94,9 @@ rb_free_tmp_buffer(volatile VALUE *store)
rb_imemo_tmpbuf_t *s = (rb_imemo_tmpbuf_t*)ATOMIC_VALUE_EXCHANGE(*store, 0);
if (s) {
void *ptr = ATOMIC_PTR_EXCHANGE(s->ptr, 0);
- long cnt = s->cnt;
- s->cnt = 0;
- ruby_sized_xfree(ptr, sizeof(VALUE) * cnt);
+ long size = s->size;
+ s->size = 0;
+ ruby_sized_xfree(ptr, size);
}
}
@@ -261,7 +261,7 @@ rb_imemo_memsize(VALUE obj)
case imemo_throw_data:
break;
case imemo_tmpbuf:
- size += ((rb_imemo_tmpbuf_t *)obj)->cnt * sizeof(VALUE);
+ size += ((rb_imemo_tmpbuf_t *)obj)->size;
break;
case imemo_fields:
@@ -506,7 +506,7 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating)
const rb_imemo_tmpbuf_t *m = (const rb_imemo_tmpbuf_t *)obj;
if (!reference_updating) {
- rb_gc_mark_locations(m->ptr, m->ptr + m->cnt);
+ rb_gc_mark_locations(m->ptr, m->ptr + (m->size / sizeof(VALUE)));
}
break;
diff --git a/internal/gc.h b/internal/gc.h
index ee1f390e10..427b2f4553 100644
--- a/internal/gc.h
+++ b/internal/gc.h
@@ -292,36 +292,6 @@ void rb_gc_writebarrier_remember(VALUE obj);
const char *rb_obj_info(VALUE obj);
void ruby_annotate_mmap(const void *addr, unsigned long size, const char *name);
-#if defined(HAVE_MALLOC_USABLE_SIZE) || defined(HAVE_MALLOC_SIZE) || defined(_WIN32)
-
-static inline void *
-ruby_sized_xrealloc_inlined(void *ptr, size_t new_size, size_t old_size)
-{
- return ruby_xrealloc(ptr, new_size);
-}
-
-static inline void *
-ruby_sized_xrealloc2_inlined(void *ptr, size_t new_count, size_t elemsiz, size_t old_count)
-{
- return ruby_xrealloc2(ptr, new_count, elemsiz);
-}
-
-static inline void
-ruby_sized_xfree_inlined(void *ptr, size_t size)
-{
- ruby_xfree(ptr);
-}
-
-# define SIZED_REALLOC_N(x, y, z, w) REALLOC_N(x, y, z)
-
-static inline void *
-ruby_sized_realloc_n(void *ptr, size_t new_count, size_t element_size, size_t old_count)
-{
- return ruby_xrealloc2(ptr, new_count, element_size);
-}
-
-#else
-
static inline void *
ruby_sized_xrealloc_inlined(void *ptr, size_t new_size, size_t old_size)
{
@@ -349,8 +319,6 @@ ruby_sized_realloc_n(void *ptr, size_t new_count, size_t element_size, size_t ol
return ruby_sized_xrealloc2(ptr, new_count, element_size, old_count);
}
-#endif /* HAVE_MALLOC_USABLE_SIZE */
-
#define ruby_sized_xrealloc ruby_sized_xrealloc_inlined
#define ruby_sized_xrealloc2 ruby_sized_xrealloc2_inlined
#define ruby_sized_xfree ruby_sized_xfree_inlined
diff --git a/internal/imemo.h b/internal/imemo.h
index 31cc0be35a..6534cec5d7 100644
--- a/internal/imemo.h
+++ b/internal/imemo.h
@@ -94,7 +94,7 @@ struct vm_ifunc {
struct rb_imemo_tmpbuf_struct {
VALUE flags;
VALUE *ptr; /* malloc'ed buffer */
- size_t cnt; /* buffer size in VALUE */
+ size_t size; /* buffer size in bytes */
};
/*! MEMO
diff --git a/prism_compile.c b/prism_compile.c
index f3aae95487..771db13f89 100644
--- a/prism_compile.c
+++ b/prism_compile.c
@@ -11330,7 +11330,7 @@ pm_read_file(pm_string_t *string, const char *filepath)
}
size_t length = (size_t) len;
- uint8_t *source = malloc(length);
+ uint8_t *source = malloc(length); // FIXME: using raw malloc because that's what Prism uses.
memcpy(source, RSTRING_PTR(contents), length);
*string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length };
diff --git a/set.c b/set.c
index 484439a40a..0fcfb1ef14 100644
--- a/set.c
+++ b/set.c
@@ -141,7 +141,7 @@ set_mark(void *ptr)
static void
set_free_embedded(struct set_object *sobj)
{
- free((&sobj->table)->entries);
+ xfree((&sobj->table)->entries);
}
static void
@@ -1187,7 +1187,7 @@ set_reset_table_with_type(VALUE set, const struct st_hash_type *type)
set_iter(set, set_merge_i, (st_data_t)&args);
set_free_embedded(sobj);
memcpy(&sobj->table, new, sizeof(*new));
- free(new);
+ xfree(new);
}
else {
sobj->table.type = type;
diff --git a/string.c b/string.c
index a36eb6e9f3..3cfc77600b 100644
--- a/string.c
+++ b/string.c
@@ -1559,7 +1559,7 @@ rb_str_tmp_frozen_no_embed_acquire(VALUE orig)
}
RSTRING(str)->len = RSTRING(orig)->len;
- RSTRING(str)->as.heap.aux.capa = capa;
+ RSTRING(str)->as.heap.aux.capa = capa + (TERM_LEN(orig) - TERM_LEN(str));
return str;
}
@@ -3135,7 +3135,7 @@ str_subseq(VALUE str, long beg, long len)
const int termlen = TERM_LEN(str);
if (!SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) {
- str2 = rb_str_new(RSTRING_PTR(str) + beg, len);
+ str2 = rb_enc_str_new(RSTRING_PTR(str) + beg, len, rb_str_enc_get(str));
RB_GC_GUARD(str);
return str2;
}
@@ -7814,7 +7814,7 @@ mapping_buffer_free(void *p)
while (current_buffer) {
previous_buffer = current_buffer;
current_buffer = current_buffer->next;
- ruby_sized_xfree(previous_buffer, previous_buffer->capa);
+ ruby_sized_xfree(previous_buffer, offsetof(mapping_buffer, space) + previous_buffer->capa);
}
}
diff --git a/vm_eval.c b/vm_eval.c
index 652fc4d85f..cf01b4a62b 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1776,7 +1776,7 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
/* We need to duplicate the string because the Ruby string may
* be embedded so compaction could move the string and the pointer
* will change. */
- char *name_dup = xmalloc(length + 1);
+ char *name_dup = malloc(length + 1); // FIXME: using raw `malloc` because that is what Prism uses.
strlcpy(name_dup, name, length + 1);
RB_GC_GUARD(name_obj);