Skip to content

Java generator: call as_json only once for invalid strings - #1127

Open
sferik wants to merge 2 commits into
ruby:masterfrom
sferik:java-as-json-symbol-recursion
Open

sferik wants to merge 2 commits into
ruby:masterfrom
sferik:java-as-json-symbol-recursion

Conversation

@sferik

@sferik sferik commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

On JRuby, JSON::Coder overflows the stack when a string with invalid encoding goes to as_json and as_json returns a Symbol:

require 'json'
symbol = "\xFF".b.to_sym
JSON::Coder.new { |obj, is_key| symbol }.dump([symbol])
# MRI:   JSON::GeneratorError: source sequence is illegal/malformed utf-8
# JRuby: Java::JavaLang::StackOverflowError

The Java generator turns the Symbol back into the same invalid string and calls as_json on it again, forever. generator.c calls as_json only once (as_json_called in generate_json_general, and convert_invalid_encoding). It raises if a new string from as_json is still invalid. This change does the same in StringHandler.

The RUBY_ENGINE == "ruby" guard on that part of test_json_coder_symbol_invalid_encoding is gone, so the test now runs on JRuby. It fails there without the fix.

A second commit removes the JRuby guard from the JSON.load case in test_parse_duplicate_key, which already passes on JRuby.

@sferik
sferik force-pushed the java-as-json-symbol-recursion branch from a62512c to 9227d68 Compare November 5, 2026 19:16
When a string with invalid encoding is generated in strict mode, the
Java and pure-Ruby generators call as_json on it. If as_json returned
a Symbol, the Symbol was turned back into the same invalid string, so
as_json was called again, forever. JRuby died with a StackOverflowError
and TruffleRuby with a SystemStackError.

Do what convert_invalid_encoding does in generator.c: if as_json
returned something other than the original string, turn a Symbol into
its String, and raise "source sequence is illegal/malformed utf-8" if
that string is still invalid instead of trying again or transcoding
it. The comparison happens before the Symbol is converted, because
TruffleRuby's Symbol#name returns the same cached string each time.

This lets test_json_coder_symbol_invalid_encoding run on JRuby and
TruffleRuby.

Part of ruby#1123
The JSON.load case in test_parse_duplicate_key with an on_load proc
that returns a non-String key was skipped on JRuby, but it passes
there now.

Part of ruby#1123
@sferik
sferik force-pushed the java-as-json-symbol-recursion branch from 9227d68 to 71c93ce Compare November 5, 2026 20:44

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant