8149169: SSLSocketInputRecord.decodeInputRecord buffer overflow
Reviewed-by: xuelei
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Thu Mar 17 08:47:46 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Fri Mar 18 13:14:53 2016 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -366,17 +366,6 @@
private static final Debug debug = Debug.getInstance("ssl");
/*
- * Is it the first application record to write?
- */
- private boolean isFirstAppOutputRecord = true;
-
- /*
- * If AppOutputStream needs to delay writes of small packets, we
- * will use this to store the data until we actually do the write.
- */
- private ByteArrayOutputStream heldRecordBuffer = null;
-
- /*
* Whether local cipher suites preference in server side should be
* honored during handshaking?
*/
@@ -998,9 +987,21 @@
Plaintext plainText = null;
while (((state = getConnectionState()) != cs_CLOSED) &&
(state != cs_ERROR) && (state != cs_APP_CLOSED)) {
- // clean the buffer
+
+ /*
+ * clean the buffer and check if it is too small, e.g. because
+ * the AppInputStream did not have the chance to see the
+ * current packet length but rather something like that of the
+ * handshake before. In that case we return 0 at this point to
+ * give the caller the chance to adjust the buffer.
+ */
if (buffer != null) {
buffer.clear();
+
+ if (buffer.remaining() <
+ inputRecord.bytesInCompletePacket(sockInput)) {
+ return 0;
+ }
}
/*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/SSLSocketImpl/LargePacketAfterHandshakeTest.java Fri Mar 18 13:14:53 2016 +0000
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/*
+ * @test
+ * @bug 8149169
+ * @summary Test for BufferOverflowException during read from SSLSocket when
+ * large packet is coming from server after server initiated handshake
+ * @run main/othervm LargePacketAfterHandshakeTest
+ */
+public class LargePacketAfterHandshakeTest {
+ static String pathToStores = "../../../../javax/net/ssl/etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ volatile static int serverport = -1;
+ volatile static boolean serverReady = false;
+ volatile static boolean clientDone = false;
+ volatile static Exception serverException = null;
+
+ public static void runServer() {
+ try {
+ System.out.println("Server: Started server thread.");
+ SSLServerSocketFactory ssf =
+ (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
+ SSLServerSocket s = (SSLServerSocket)ssf.createServerSocket(0);
+ serverport = s.getLocalPort();
+ System.out.println("Server: Started, listening on port " +
+ serverport + ".");
+ serverReady = true;
+ SSLSocket c = (SSLSocket)s.accept();
+ s.close();
+ System.out.println(
+ "Server: Accepted client connection and closed server socket.");
+ BufferedReader r = new BufferedReader(
+ new InputStreamReader(c.getInputStream()));
+ BufferedWriter w = new BufferedWriter(
+ new OutputStreamWriter(c.getOutputStream()));
+ String echostring = r.readLine();
+ System.out.println("Server: Read " + echostring.length() +
+ " chars of input data.");
+ c.startHandshake();
+ System.out.println("Server: Kicked new handshake.");
+ w.write(echostring);
+ w.newLine();
+ w.flush();
+ System.out.println("Server: Echoed " + echostring.length() +
+ " chars of input data.");
+ while (!clientDone) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ System.out.println("Server: Caught InterruptedException.");
+ }
+ }
+ r.close();
+ w.close();
+ c.close();
+ System.out.println(
+ "Server: Closed streams and client socket, exiting.");
+ } catch (Exception e) {
+ System.out.println("Server: Caught Exception.");
+ e.printStackTrace();
+ serverReady = true;
+ serverException = e;
+ }
+ }
+
+ public static void runClient() throws IOException {
+ try {
+ SSLSocketFactory f =
+ (SSLSocketFactory)SSLSocketFactory.getDefault();
+ System.out.println("Client: Initialized.");
+ while (!serverReady) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ System.out.println("Client: Caught InterruptedException.");
+ }
+ }
+ SSLSocket c = (SSLSocket)f.createSocket("localhost", serverport);
+ BufferedWriter w = new BufferedWriter(
+ new OutputStreamWriter(c.getOutputStream()));
+ BufferedReader r = new BufferedReader(
+ new InputStreamReader(c.getInputStream()));
+ System.out.println("Client: Connected.");
+ String echoPattern = "Otto";
+ StringBuilder echoBuilder =
+ new StringBuilder(4500 + echoPattern.length());
+ while (echoBuilder.length() < 4500) {
+ echoBuilder.append(echoPattern);
+ }
+ String echostring = echoBuilder.toString();
+ w.write(echostring);
+ w.newLine();
+ w.flush();
+ System.out.println("Client: Sent " + echostring.length() +
+ " chars of data.");
+ String echoresponse = r.readLine();
+ clientDone = true;
+ System.out.println("Client: Read " + echoresponse.length() +
+ " chars of data.");
+ w.close();
+ r.close();
+ c.close();
+ System.out.println("Client: Closed streams and socket, exiting.");
+ } catch (IOException e) {
+ System.out.println("Client: Caught Exception.");
+ e.printStackTrace();
+ clientDone = true;
+ throw e;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename = System.getProperty("test.src", "./") + "/" +
+ pathToStores + "/" + keyStoreFile;
+ String trustFilename = System.getProperty("test.src", "./") + "/" +
+ pathToStores + "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ Thread serverThread = new Thread() {
+ @Override
+ public void run() {
+ runServer();
+ }
+ };
+ serverThread.start();
+ runClient();
+ while (serverThread.isAlive()) {
+ try {
+ serverThread.join();
+ } catch (InterruptedException e) {
+ System.out.println("Main: Caught InterruptedException " +
+ " waiting for server Thread.");
+ }
+ }
+ if (serverException != null) {
+ throw serverException;
+ }
+ }
+}