Repository navigation
Java parser: don't let the caller mutate the bytes being parsed - #1122
Conversation
ParserSession reads the source string's backing byte[] directly, but the string itself was never marked as shared. A decimal_class callback that calls source.tr! could then rewrite the bytes the parser is still reading. The existing test_mutating_source_string_during_parsing only passes on JRuby by accident: with an unknown coderange String#tr! happens to build a new buffer. Once the coderange is known (for example after source.ascii_only?), tr! writes in place and the parser sees the change. Freeze a copy of the source before parsing, like rb_str_new_frozen in parser.c. RubyString#newFrozen shares the ByteList and marks the original as shared, so a later mutation copies first.
The json Java parser reads the source string's bytes without marking it shared. String#tr! now writes in place more often, which exposes it; the same failure happens on master after source.ascii_only?.
headius
left a comment
There was a problem hiding this comment.
This is correct and matches the changes (only done to the C extension) here:
The Vsource == src check was later removed by @byroot in a round of "hardening against adversarial encode methods" with a test explicitly excluded on JRuby.
Now we match and should also be able to remove the exclusion of that test.
It might be worth checking for other guards against "adversarial" code that were never ported.
|
These changes will need a backport to the 2.x-stable branch and likely a 2.x release to fix the Java extension along that line like the C extension was fixed in v2.19.7. Note that the "hardening" changes only appear to have been pushed to v3.1.0. |
|
@sampokuokkanen I pushed the approve button too quickly... could you also unmask the test I linked, so we are back to parity with the C extension? |
|
I filed #1123 to audit other excluded tests and try to bring the Java extension in line. |
|
FYI we can't land jruby/jruby#10000 for JRuby until this is patched because it now fails the test added in #985 due to the unported fix (and thus will start failing jruby-head builds here). We need the backport because JRuby 10.0 and 10.1 must generally match CRuby 3.4 and 4.0 stdlib, neither of which ship json 3.x. |
|
I attempted to enable the masked test from #1107 but it fails with an EncodingError, possibly due to the C extensions lack of strictness in handling badly-encoded text (#138). We can merge this without enabling that test and examine it in more detail for #1123. |
The Java parser reads the source string's byte array directly, but it never marks the string as shared. So if a
decimal_classcallback callssource.tr!, it can rewrite the bytes the parser is still reading.test_mutating_source_string_during_parsingonly passes on JRuby by luck. If the string's coderange isn't known yet, JRuby'str!builds a new buffer. If it is known,tr!writes in place. Callingsource.ascii_only?before parsing is enough to break it:This came up in jruby/jruby#10000, which adds a faster
String#tr. That makestr!write in place much more often, so the existing test started failing there.The fix freezes a copy of the source before parsing, the same way
parser.cusesrb_str_new_frozen.RubyString#newFrozenshares the bytes and marks the original string as shared, so changing it later copies the bytes first. I added a test withascii_only?that fails without the fix. The full test suite passes on JRuby master and on the jruby/jruby#10000 branch.