8230743: StringJoiner does not handle too large strings correctly

Reviewed-by: rriggs, psandoz, martin
This commit is contained in:
Jim Laskey 2020-06-05 09:37:14 -03:00
parent cb960ee7b5
commit 4de4200652
3 changed files with 77 additions and 2 deletions

View file

@ -125,6 +125,7 @@ public final class StringJoiner {
this.prefix = prefix.toString(); this.prefix = prefix.toString();
this.delimiter = delimiter.toString(); this.delimiter = delimiter.toString();
this.suffix = suffix.toString(); this.suffix = suffix.toString();
checkAddLength(0, 0);
} }
/** /**
@ -202,13 +203,22 @@ public final class StringJoiner {
} else { } else {
if (size == elts.length) if (size == elts.length)
elts = Arrays.copyOf(elts, 2 * size); elts = Arrays.copyOf(elts, 2 * size);
len += delimiter.length(); len = checkAddLength(len, delimiter.length());
} }
len += elt.length(); len = checkAddLength(len, elt.length());
elts[size++] = elt; elts[size++] = elt;
return this; return this;
} }
private int checkAddLength(int oldLen, int inc) {
long newLen = (long)oldLen + (long)inc;
long tmpLen = newLen + (long)prefix.length() + (long)suffix.length();
if (tmpLen != (int)tmpLen) {
throw new OutOfMemoryError("Requested array size exceeds VM limit");
}
return (int)newLen;
}
/** /**
* Adds the contents of the given {@code StringJoiner} without prefix and * Adds the contents of the given {@code StringJoiner} without prefix and
* suffix as the next element if it is non-empty. If the given {@code * suffix as the next element if it is non-empty. If the given {@code

View file

@ -25,12 +25,14 @@
* @test * @test
* @bug 8017231 8020977 8054221 * @bug 8017231 8020977 8054221
* @summary test StringJoiner::merge * @summary test StringJoiner::merge
* @modules java.base/jdk.internal.util
* @run testng MergeTest * @run testng MergeTest
*/ */
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static jdk.internal.util.ArraysSupport.MAX_ARRAY_LENGTH;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
@ -167,4 +169,19 @@ public class MergeTest {
assertEquals(sj.merge(sj).toString(), fixes.pre0 + "a,b,a,b,a,b,a,b" + fixes.suf0); assertEquals(sj.merge(sj).toString(), fixes.pre0 + "a,b,a,b,a,b,a,b" + fixes.suf0);
}); });
} }
public void OOM() {
String maxString = "*".repeat(MAX_ARRAY_LENGTH);
try {
StringJoiner sj1 = new StringJoiner("", "", "");
sj1.add(maxString);
StringJoiner sj2 = new StringJoiner("", "", "");
sj2.add(maxString);
sj1.merge(sj2);
fail("Should have thrown OutOfMemoryError");
} catch (OutOfMemoryError ex) {
// okay
}
}
} }

View file

@ -24,13 +24,17 @@
* @test * @test
* @bug 5015163 7172553 * @bug 5015163 7172553
* @summary tests StringJoinerTest * @summary tests StringJoinerTest
* @modules java.base/jdk.internal.util
* @run testng StringJoinerTest * @run testng StringJoinerTest
* @author Jim Gish * @author Jim Gish
*/ */
import java.util.ArrayList; import java.util.ArrayList;
import java.util.StringJoiner; import java.util.StringJoiner;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static jdk.internal.util.ArraysSupport.MAX_ARRAY_LENGTH;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
@Test(groups = {"unit","string","util","libs"}) @Test(groups = {"unit","string","util","libs"})
public class StringJoinerTest { public class StringJoinerTest {
@ -320,5 +324,49 @@ public class StringJoinerTest {
testCombos(",", "", ">"); testCombos(",", "", ">");
testCombos(",", "<", ">"); testCombos(",", "<", ">");
} }
public void OOM1() {
String maxString = "*".repeat(MAX_ARRAY_LENGTH);
try {
new StringJoiner(maxString, maxString, maxString).toString();
fail("Should have thrown OutOfMemoryError");
} catch (OutOfMemoryError ex) {
// okay
}
}
public void OOM2() {
String maxString = "*".repeat(MAX_ARRAY_LENGTH);
try {
new StringJoiner(maxString, maxString, "").toString();
fail("Should have thrown OutOfMemoryError");
} catch (OutOfMemoryError ex) {
// okay
}
}
public void OOM3() {
String maxString = "*".repeat(MAX_ARRAY_LENGTH);
try {
new StringJoiner(maxString, "", maxString).toString();
fail("Should have thrown OutOfMemoryError");
} catch (OutOfMemoryError ex) {
// okay
}
}
public void OOM4() {
String maxString = "*".repeat(MAX_ARRAY_LENGTH);
try {
new StringJoiner("", maxString, maxString).toString();
fail("Should have thrown OutOfMemoryError");
} catch (OutOfMemoryError ex) {
// okay
}
}
} }