8158906: JShell: crashes with extremely long result value
authorrfield
Fri, 19 Aug 2016 13:55:26 -0700
changeset 40512 b9359154240c
parent 40511 1b3c502e0bdc
child 40513 39b67170b045
8158906: JShell: crashes with extremely long result value Reviewed-by: jlahoda, shinyafox
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/ExecutionControlForwarder.java
langtools/test/jdk/jshell/SimpleRegressionTest.java
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/ExecutionControlForwarder.java	Fri Aug 19 12:54:02 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/ExecutionControlForwarder.java	Fri Aug 19 13:55:26 2016 -0700
@@ -44,6 +44,13 @@
  */
 class ExecutionControlForwarder {
 
+    /**
+     * Maximum number of characters for writeUTF().  Byte maximum is 65535, at
+     * maximum three bytes per character that is 65535 / 3 == 21845.  Minus one
+     * for safety.
+     */
+    private static final int MAX_UTF_CHARS = 21844;
+
     private final ExecutionControl ec;
     private final ObjectInput in;
     private final ObjectOutput out;
@@ -89,6 +96,9 @@
     private void writeUTF(String s) throws IOException {
         if (s == null) {
             s = "";
+        } else if (s.length() > MAX_UTF_CHARS) {
+            // Truncate extremely long strings to prevent writeUTF from crashing the VM
+            s = s.substring(0, MAX_UTF_CHARS);
         }
         out.writeUTF(s);
     }
--- a/langtools/test/jdk/jshell/SimpleRegressionTest.java	Fri Aug 19 12:54:02 2016 -0700
+++ b/langtools/test/jdk/jshell/SimpleRegressionTest.java	Fri Aug 19 13:55:26 2016 -0700
@@ -22,7 +22,7 @@
  */
 
 /*
- * @test 8130450
+ * @test 8130450 8158906
  * @summary simple regression test
  * @build KullaTesting TestingInputStream
  * @run testng SimpleRegressionTest
@@ -39,6 +39,8 @@
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 import static jdk.jshell.Snippet.Status.OVERWRITTEN;
 import static jdk.jshell.Snippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND;
 import static jdk.jshell.Snippet.Status.VALID;
@@ -100,6 +102,37 @@
         assertEquals(sne.typeName(), "Integer");
     }
 
+    public void testLongRemoteStrings() { //8158906
+        assertEval("String m(int x) { byte[] b = new byte[x]; for (int i = 0; i < x; ++i) b[i] = (byte) 'a'; return new String(b); }");
+        boolean[] shut = new boolean[1];
+        getState().onShutdown(j -> {
+            shut[0] = true;
+        });
+        for (String len : new String[]{"12345", "64000", "65535", "65536", "120000"}) {
+            List<SnippetEvent> el = assertEval("m(" + len + ");");
+            assertFalse(shut[0], "JShell died with long string");
+            assertEquals(el.size(), 1, "Excepted one event");
+            assertTrue(el.get(0).value().length() > 10000,
+                    "Expected truncated but long String, got: " + el.get(0).value().length());
+        }
+    }
+
+    public void testLongRemoteJapaneseStrings() { //8158906
+        assertEval("import java.util.stream.*;");
+        assertEval("String m(int x) { return Stream.generate(() -> \"\u3042\").limit(x).collect(Collectors.joining()); }");
+        boolean[] shut = new boolean[1];
+        getState().onShutdown(j -> {
+            shut[0] = true;
+        });
+        for (String len : new String[]{"12345", "21843", "21844", "21845", "21846", "64000", "65535", "65536", "120000"}) {
+            List<SnippetEvent> el = assertEval("m(" + len + ");");
+            assertFalse(shut[0], "JShell died with long string");
+            assertEquals(el.size(), 1, "Excepted one event");
+            assertTrue(el.get(0).value().length() > 10000,
+                    "Expected truncated but long String, got: " + el.get(0).value().length());
+        }
+    }
+
     // 8130450
     public void testDuplicate() {
         Snippet snm = methodKey(assertEval("void mm() {}", added(VALID)));