From 4c465382084b848cb0f23f18d43114cfca17b273 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Jun 2026 09:25:51 +0200 Subject: [PATCH] Integrate with Ruby 4.1 `ruby_sized_xfree` Ensure that buffer sizes have been properly tracked when running in debug mode, and appease the GC slightly when running in release mode. Ref: https://bugs.ruby-lang.org/issues/21861 --- CHANGES.md | 2 ++ ext/json/ext/fbuffer/fbuffer.h | 4 ++-- ext/json/ext/generator/extconf.rb | 1 + ext/json/ext/json.h | 18 ++++++++++++++++++ ext/json/ext/parser/extconf.rb | 1 + ext/json/ext/parser/parser.c | 6 +++--- 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1628bd0b..c4808e53 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ### Unreleased +* Integrate with Ruby 4.1 `ruby_sized_xfree`. + ### 2026-06-03 (2.19.8) * Fix 1-byte buffer overread on EOS errors. diff --git a/ext/json/ext/fbuffer/fbuffer.h b/ext/json/ext/fbuffer/fbuffer.h index b84a0735..b4f5266c 100644 --- a/ext/json/ext/fbuffer/fbuffer.h +++ b/ext/json/ext/fbuffer/fbuffer.h @@ -64,7 +64,7 @@ static inline void fbuffer_consumed(FBuffer *fb, size_t consumed) static void fbuffer_free(FBuffer *fb) { if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) { - ruby_xfree(fb->ptr); + JSON_SIZED_FREE_N(fb->ptr, fb->capa); } } @@ -88,7 +88,7 @@ static void fbuffer_realloc(FBuffer *fb, size_t required) fb->type = FBUFFER_HEAP_ALLOCATED; MEMCPY(fb->ptr, old_buffer, char, fb->len); } else { - REALLOC_N(fb->ptr, char, required); + JSON_SIZED_REALLOC_N(fb->ptr, char, required, fb->capa); } fb->capa = required; } diff --git a/ext/json/ext/generator/extconf.rb b/ext/json/ext/generator/extconf.rb index 205a887e..33af03ea 100644 --- a/ext/json/ext/generator/extconf.rb +++ b/ext/json/ext/generator/extconf.rb @@ -6,6 +6,7 @@ else append_cflags("-std=c99") have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3 + have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1 $defs << "-DJSON_GENERATOR" $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0" diff --git a/ext/json/ext/json.h b/ext/json/ext/json.h index 8737989a..cf9420d4 100644 --- a/ext/json/ext/json.h +++ b/ext/json/ext/json.h @@ -49,6 +49,24 @@ typedef unsigned char _Bool; #endif #endif +#ifndef HAVE_RUBY_XFREE_SIZED +static inline void ruby_xfree_sized(void *ptr, size_t oldsize) +{ + ruby_xfree(ptr); +} + +static inline void *ruby_xrealloc2_sized(void *ptr, size_t new_elems, size_t elem_size, size_t old_elems) +{ + return ruby_xrealloc2(ptr, new_elems, elem_size); +} +#endif + +# define JSON_SIZED_REALLOC_N(v, T, m, n) \ + ((v) = (T *)ruby_xrealloc2_sized((void *)(v), (m), sizeof(T), (n))) + +# define JSON_SIZED_FREE(v) ruby_xfree_sized((void *)(v), sizeof(*(v))) +# define JSON_SIZED_FREE_N(v, n) ruby_xfree_sized((void *)(v), sizeof(*(v)) * (n)) + #ifndef HAVE_RB_EXT_RACTOR_SAFE # undef RUBY_TYPED_FROZEN_SHAREABLE # define RUBY_TYPED_FROZEN_SHAREABLE 0 diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/parser/extconf.rb index f12fc2dd..a9d740c7 100644 --- a/ext/json/ext/parser/extconf.rb +++ b/ext/json/ext/parser/extconf.rb @@ -6,6 +6,7 @@ have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0 have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2 have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby +have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1 if RUBY_ENGINE == "ruby" have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3 diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 56b214fc..d0482f68 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -211,7 +211,7 @@ static rvalue_stack *rvalue_stack_grow(rvalue_stack *stack, VALUE *handle, rvalu if (stack->type == RVALUE_STACK_STACK_ALLOCATED) { stack = rvalue_stack_spill(stack, handle, stack_ref); } else { - REALLOC_N(stack->ptr, VALUE, required); + JSON_SIZED_REALLOC_N(stack->ptr, VALUE, required, stack->capa); stack->capa = required; } return stack; @@ -250,7 +250,7 @@ static void rvalue_stack_mark(void *ptr) static void rvalue_stack_free_buffer(rvalue_stack *stack) { - ruby_xfree(stack->ptr); + JSON_SIZED_FREE_N(stack->ptr, stack->capa); stack->ptr = NULL; } @@ -260,7 +260,7 @@ static void rvalue_stack_free(void *ptr) if (stack) { rvalue_stack_free_buffer(stack); #ifndef HAVE_RUBY_TYPED_EMBEDDABLE - ruby_xfree(stack); + JSON_SIZED_FREE(stack); #endif } }