diff options
| author | Jean Boussier <jean.boussier@gmail.com> | 2026-01-28 13:39:33 +0100 |
|---|---|---|
| committer | Jean Boussier <jean.boussier@gmail.com> | 2026-01-29 23:32:04 +0100 |
| commit | 91619f0230c0e5a95c796c1bd4f784c151e15614 (patch) | |
| tree | b8cf37fc6092a51f318996f382ab9503ba390100 | |
| parent | 457bb11aa5b2ce4424b611acb489686d130261de (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.c | 5 | ||||
| -rw-r--r-- | ext/-test-/string/cstr.c | 3 | ||||
| -rw-r--r-- | gc/default/default.c | 20 | ||||
| -rw-r--r-- | imemo.c | 14 | ||||
| -rw-r--r-- | internal/gc.h | 32 | ||||
| -rw-r--r-- | internal/imemo.h | 2 | ||||
| -rw-r--r-- | prism_compile.c | 2 | ||||
| -rw-r--r-- | set.c | 4 | ||||
| -rw-r--r-- | string.c | 6 | ||||
| -rw-r--r-- | vm_eval.c | 2 |
10 files changed, 40 insertions, 50 deletions
@@ -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 @@ -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 }; @@ -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; @@ -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); } } @@ -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); |
