diff options
author | Jean Boussier <[email protected]> | 2025-04-25 07:35:05 +0200 |
---|---|---|
committer | Jean Boussier <[email protected]> | 2025-04-30 08:12:41 +0200 |
commit | 8fe3fb5d5a020d9c567cce92dc8812ae7bd8c327 () | |
tree | d18ad5da183ea47b9ef7c797e618d4e39416d6e6 | |
parent | 18dac125cb48768b546a5913378e7349ee687492 (diff) |
[ruby/json] Stop caching the generator state pointer
Fix: https://.com/ruby/json/issues/790 If we end up calling something that spills the state on the heap, the pointer we received is outdated and may be out of sync. https://.com/ruby/json/commit/2ffa4ea46b
-rw-r--r-- | ext/json/generator/generator.c | 179 | ||||
-rw-r--r-- | test/json/json_common_interface_test.rb | 24 |
2 files changed, 114 insertions, 89 deletions
@@ -45,7 +45,7 @@ static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_n struct generate_json_data; -typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); struct generate_json_data { FBuffer *buffer; @@ -57,20 +57,20 @@ struct generate_json_data { static VALUE cState_from_state_s(VALUE self, VALUE opts); static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io); -static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); #ifdef RUBY_INTEGER_UNIFICATION -static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); #endif -static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); -static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); static int usascii_encindex, utf8_encindex, binary_encindex; @@ -802,12 +802,12 @@ json_object_i(VALUE key, VALUE val, VALUE _arg) int j; if (arg->iter > 0) fbuffer_append_char(buffer, ','); - if (RB_UNLIKELY(state->object_nl)) { - fbuffer_append_str(buffer, state->object_nl); } - if (RB_UNLIKELY(state->indent)) { for (j = 0; j < depth; j++) { - fbuffer_append_str(buffer, state->indent); } } @@ -829,21 +829,22 @@ json_object_i(VALUE key, VALUE val, VALUE _arg) } if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) { - generate_json_string(buffer, data, state, key_to_s); } else { - generate_json(buffer, data, state, key_to_s); } - if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, state->space_before); fbuffer_append_char(buffer, ':'); - if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space); - generate_json(buffer, data, state, val); arg->iter++; return ST_CONTINUE; } -static inline long increase_depth(JSON_Generator_State *state) { long depth = ++state->depth; if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) { rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); @@ -851,14 +852,14 @@ static inline long increase_depth(JSON_Generator_State *state) return depth; } -static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { int j; - long depth = increase_depth(state); if (RHASH_SIZE(obj) == 0) { fbuffer_append(buffer, "{}", 2); - --state->depth; return; } @@ -870,49 +871,49 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat }; rb_hash_foreach(obj, json_object_i, (VALUE)&arg); - depth = --state->depth; - if (RB_UNLIKELY(state->object_nl)) { - fbuffer_append_str(buffer, state->object_nl); - if (RB_UNLIKELY(state->indent)) { for (j = 0; j < depth; j++) { - fbuffer_append_str(buffer, state->indent); } } } fbuffer_append_char(buffer, '}'); } -static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { int i, j; - long depth = increase_depth(state); if (RARRAY_LEN(obj) == 0) { fbuffer_append(buffer, "[]", 2); - --state->depth; return; } fbuffer_append_char(buffer, '['); - if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl); for(i = 0; i < RARRAY_LEN(obj); i++) { if (i > 0) { fbuffer_append_char(buffer, ','); - if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl); } - if (RB_UNLIKELY(state->indent)) { for (j = 0; j < depth; j++) { - fbuffer_append_str(buffer, state->indent); } } - generate_json(buffer, data, state, RARRAY_AREF(obj, i)); } - state->depth = --depth; - if (RB_UNLIKELY(state->array_nl)) { - fbuffer_append_str(buffer, state->array_nl); - if (RB_UNLIKELY(state->indent)) { for (j = 0; j < depth; j++) { - fbuffer_append_str(buffer, state->indent); } } } @@ -961,7 +962,7 @@ static inline VALUE ensure_valid_encoding(VALUE str) return str; } -static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { obj = ensure_valid_encoding(obj); @@ -977,9 +978,9 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat switch(rb_enc_str_coderange(obj)) { case ENC_CODERANGE_7BIT: case ENC_CODERANGE_VALID: - if (RB_UNLIKELY(state->ascii_only)) { - convert_UTF8_to_ASCII_only_JSON(&search, state->script_safe ? script_safe_escape_table : ascii_only_escape_table); - } else if (RB_UNLIKELY(state->script_safe)) { convert_UTF8_to_script_safe_JSON(&search); } else { convert_UTF8_to_JSON(&search); @@ -992,7 +993,7 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat fbuffer_append_char(buffer, '"'); } -static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { VALUE tmp; if (rb_respond_to(obj, i_to_json)) { @@ -1002,68 +1003,68 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d } else { tmp = rb_funcall(obj, i_to_s, 0); Check_Type(tmp, T_STRING); - generate_json_string(buffer, data, state, tmp); } } -static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { - if (state->strict) { - generate_json_string(buffer, data, state, rb_sym2str(obj)); } else { - generate_json_fallback(buffer, data, state, obj); } } -static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { fbuffer_append(buffer, "null", 4); } -static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { fbuffer_append(buffer, "false", 5); } -static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { fbuffer_append(buffer, "true", 4); } -static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { fbuffer_append_long(buffer, FIX2LONG(obj)); } -static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { VALUE tmp = rb_funcall(obj, i_to_s, 0); fbuffer_append_str(buffer, tmp); } #ifdef RUBY_INTEGER_UNIFICATION -static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { if (FIXNUM_P(obj)) - generate_json_fixnum(buffer, data, state, obj); else - generate_json_bignum(buffer, data, state, obj); } #endif -static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { double value = RFLOAT_VALUE(obj); - char allow_nan = state->allow_nan; if (isinf(value) || isnan(value)) { /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */ if (!allow_nan) { - if (state->strict && state->as_json) { - VALUE casted_obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil); if (casted_obj != obj) { - increase_depth(state); - generate_json(buffer, data, state, casted_obj); - state->depth--; return; } } @@ -1089,30 +1090,30 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data buffer->len += len; } -static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { VALUE fragment = RSTRUCT_GET(obj, 0); Check_Type(fragment, T_STRING); fbuffer_append_str(buffer, fragment); } -static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) { bool as_json_called = false; start: if (obj == Qnil) { - generate_json_null(buffer, data, state, obj); } else if (obj == Qfalse) { - generate_json_false(buffer, data, state, obj); } else if (obj == Qtrue) { - generate_json_true(buffer, data, state, obj); } else if (RB_SPECIAL_CONST_P(obj)) { if (RB_FIXNUM_P(obj)) { - generate_json_fixnum(buffer, data, state, obj); } else if (RB_FLONUM_P(obj)) { - generate_json_float(buffer, data, state, obj); } else if (RB_STATIC_SYM_P(obj)) { - generate_json_symbol(buffer, data, state, obj); } else { goto general; } @@ -1120,43 +1121,43 @@ start: VALUE klass = RBASIC_CLASS(obj); switch (RB_BUILTIN_TYPE(obj)) { case T_BIGNUM: - generate_json_bignum(buffer, data, state, obj); break; case T_HASH: if (klass != rb_cHash) goto general; - generate_json_object(buffer, data, state, obj); break; case T_ARRAY: if (klass != rb_cArray) goto general; - generate_json_array(buffer, data, state, obj); break; case T_STRING: if (klass != rb_cString) goto general; - generate_json_string(buffer, data, state, obj); break; case T_SYMBOL: - generate_json_symbol(buffer, data, state, obj); break; case T_FLOAT: if (klass != rb_cFloat) goto general; - generate_json_float(buffer, data, state, obj); break; case T_STRUCT: if (klass != cFragment) goto general; - generate_json_fragment(buffer, data, state, obj); break; default: general: - if (state->strict) { - if (RTEST(state->as_json) && !as_json_called) { - obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil); as_json_called = true; goto start; } else { raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj)); } } else { - generate_json_fallback(buffer, data, state, obj); } } } @@ -1166,7 +1167,7 @@ static VALUE generate_json_try(VALUE d) { struct generate_json_data *data = (struct generate_json_data *)d; - data->func(data->buffer, data, data->state, data->obj); return Qnil; } @@ -91,6 +91,30 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase def test_pretty_generate assert_equal "[\n 1,\n 2,\n 3\n]", JSON.pretty_generate([ 1, 2, 3 ]) end def test_load |