diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 70e34edb7..b3b6bfdca 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2113,9 +2113,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src) // Ensure the string isn't mutated under us. // The classic API to use is `rb_str_locktmp`, but then we'd // need to use `rb_protect` to make sure we always unlock. - if (Vsource == src) { - Vsource = rb_str_new_frozen(Vsource); - } + Vsource = rb_str_new_frozen(Vsource); VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; rvalue_stack value_stack = { diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index d9e3256bd..0b9d5fc96 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -1045,6 +1045,41 @@ def test_mutating_source_string_during_parsing assert_equal expected, actual end + def test_adversary_encode + fake_string = Class.new(String) do + def encode(*) + 42 + end + end + + assert_raise TypeError, Encoding::InvalidByteSequenceError do + JSON.parse(fake_string.new("\xff".b, encoding: Encoding::UTF_16)) + end + + if RUBY_ENGINE != "jruby" + fake_string = Class.new(String) do + attr_reader :encoded_str + + def initialize(...) + super + @encoded_str = JSON.dump(1000.times.to_a) + end + + def encode(*) + @encoded_str + end + end + + fake = fake_string.new("blah", encoding: Encoding::UTF_16) + assert_equal 1000.times.to_a, JSON.parse(fake, on_load: ->(v) { + if v == 20 + fake.encoded_str.clear + end + v + }) + end + end + private def assert_equal_float(expected, actual, delta = 1e-2)