openjdk version "11.0.9" 2020-10-20
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.9+11)
Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.23.0, JRE 11 Linux amd64-64-Bit Compressed References 20201022_810 (JIT enabled, AOT enabled)
OpenJ9 - 0394ef754
OMR - 582366ae5
JCL - 3b09cfd7e9 based on jdk-11.0.9+11)
I have a program that never normally crashes, but when I run it with -XX:+CompactStrings I get a fairly-reproducible crash.
java.lang.ArrayIndexOutOfBoundsException: null
at com.ibm.jit.JITHelpers.getCharFromArrayByIndex(JITHelpers.java:523) ~[?:?]
at java.lang.String.decompressedArrayCopy(String.java:352) ~[?:?]
at java.lang.String.
at xxx.MultilineTextHelper.getTextLines(MultilineTextHelper.java:120) ~[xxx.jar:?]
at xxx.MultilineTextHelper.getTextLines(MultilineTextHelper.java:86) ~[xxx.jar:?]
The line that this fails on (120) is:
newLine = fullNormalizedText.substring(baseOffset, newOffset) + '\u205e';
Program doesn't crash. It just produces incorrect results.
What I think is happening is that after a while the optimiser kicks in and notices the "string + character" initialiser for the string, and converts it to new String(String s, char c). In my case s is an ASCII string, of roughly 400 bytes.
Line 674 now corresponds to https://github.com/eclipse/openj9/blob/0ad4ed91031ecc8d8daa16dac4d9a484c78747b2/jcl/src/java.base/share/classes/java/lang/String.java#L721 , but things start to go wrong on https://github.com/eclipse/openj9/blob/0ad4ed91031ecc8d8daa16dac4d9a484c78747b2/jcl/src/java.base/share/classes/java/lang/String.java#L710.
What I believe is happening is that s.coder == LATIN1, however c <= 255 is false, since c is '\u205e'. So we go to the else block. This allocates an byte array big enough to hold 400 characters, aka 800 bytes, and then calls decompressedArrayCopy(s.value, 0, value, 0, slen);. However this expects to copy from an uncompressed array to an uncompressed array. What it ends up doing is copying from a compressed array to an uncompressed array. Roughly half way through, it runs out of source bytes and throws the ArrayIndexOutOfBoundsException.
I'm not sure what the exact resolution should be, but there should either be a compressedToDecompressedArrayCopy method, and that should be used, or the array needs to be decompressed before copying.
@fjeremic fyi
Yes, it seems we should be calling decompress in the else path to decompress the compressed string, and then we need to copy c into the buffer. @dchopra001 could you look into providing a fix for this? Let's also check the other private constructors to ensure the same issue does not exist.
Here is an example in another private constructor where do decompress:
https://github.com/eclipse/openj9/blob/0ad4ed91031ecc8d8daa16dac4d9a484c78747b2/jcl/src/java.base/share/classes/java/lang/String.java#L986-L991
I'll tentatively tag this for the 0.25 milestone as there should be time to get a fix in before the release.
Most helpful comment
Yes, it seems we should be calling
decompressin theelsepath to decompress the compressed string, and then we need to copycinto the buffer. @dchopra001 could you look into providing a fix for this? Let's also check the other private constructors to ensure the same issue does not exist.Here is an example in another private constructor where do decompress:
https://github.com/eclipse/openj9/blob/0ad4ed91031ecc8d8daa16dac4d9a484c78747b2/jcl/src/java.base/share/classes/java/lang/String.java#L986-L991