8207317: SSLEngine negotiation fail exception behavior changed from fail-fast to fail-lazy
authorwetmore
Tue, 21 Aug 2018 11:30:48 -0700
changeset 51574 ed52ea83f830
parent 51573 3b3685479784
child 51575 76072a077ee1
8207317: SSLEngine negotiation fail exception behavior changed from fail-fast to fail-lazy Reviewed-by: xuelei
src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java
src/java.base/share/classes/sun/security/ssl/CertificateMessage.java
src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java
src/java.base/share/classes/sun/security/ssl/SSLCipher.java
src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java
src/java.base/share/classes/sun/security/ssl/SSLTransport.java
src/java.base/share/classes/sun/security/ssl/ServerHello.java
src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java
src/java.base/share/classes/sun/security/ssl/SignatureScheme.java
src/java.base/share/classes/sun/security/ssl/TransportContext.java
src/java.base/share/classes/sun/security/ssl/X509Authentication.java
test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java
test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java
test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineFailedALPN.java
--- a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java	Tue Aug 21 11:30:48 2018 -0700
@@ -191,12 +191,12 @@
             }
 
             // update the context
-            List<SignatureScheme> shemes =
+            List<SignatureScheme> schemes =
                     SignatureScheme.getSupportedAlgorithms(
                             shc.algorithmConstraints, shc.negotiatedProtocol,
                             spec.signatureSchemes);
-            shc.peerRequestedCertSignSchemes = shemes;
-            shc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+            shc.peerRequestedCertSignSchemes = schemes;
+            shc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
 
             if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) {
                 if (shc.sslConfig.clientAuthType !=
@@ -337,12 +337,12 @@
             }
 
             // update the context
-            List<SignatureScheme> shemes =
+            List<SignatureScheme> schemes =
                     SignatureScheme.getSupportedAlgorithms(
                             chc.algorithmConstraints, chc.negotiatedProtocol,
                             spec.signatureSchemes);
-            chc.peerRequestedCertSignSchemes = shemes;
-            chc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+            chc.peerRequestedCertSignSchemes = schemes;
+            chc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
         }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java	Tue Aug 21 11:30:48 2018 -0700
@@ -1031,8 +1031,8 @@
                 // Don't select a signature scheme unless we will be able to
                 // produce a CertificateVerify message later
                 if (SignatureScheme.getPreferableAlgorithm(
-                    hc.peerRequestedSignatureSchemes,
-                    ss, hc.negotiatedProtocol) == null) {
+                        hc.peerRequestedSignatureSchemes,
+                        ss, hc.negotiatedProtocol) == null) {
 
                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                         SSLLogger.warning(
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java	Tue Aug 21 11:30:48 2018 -0700
@@ -50,7 +50,7 @@
         this.outputRecord = outputRecord;
     }
 
-    // Complete a handshakin message writing. Called by HandshakeMessage.
+    // Complete a handshaking message write. Called by HandshakeMessage.
     void complete() throws IOException {
         if (size() < 4) {       // 4: handshake message header size
             // internal_error alert will be triggered
--- a/src/java.base/share/classes/sun/security/ssl/SSLCipher.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java	Tue Aug 21 11:30:48 2018 -0700
@@ -379,10 +379,10 @@
     private final Map.Entry<WriteCipherGenerator,
             ProtocolVersion[]>[] writeCipherGenerators;
 
-    // Map of Ciphers listed in jdk.tls.KeyLimit
+    // Map of Ciphers listed in jdk.tls.keyLimits
     private static final HashMap<String, Long> cipherLimits = new HashMap<>();
 
-    // Keywords found on the jdk.tls.KeyLimit security property.
+    // Keywords found on the jdk.tls.keyLimits security property.
     final static String tag[] = {"KEYUPDATE"};
 
     static  {
@@ -407,7 +407,7 @@
                     index = 0;
                 } else {
                     if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
-                        SSLLogger.fine("jdk.net.keyLimits:  Unknown action:  " +
+                        SSLLogger.fine("jdk.tls.keyLimits:  Unknown action:  " +
                                 entry);
                     }
                     continue;
@@ -423,17 +423,18 @@
                         size = Long.parseLong(values[2]);
                     }
                     if (size < 1 || size > max) {
-                        throw new NumberFormatException("Length exceeded limits");
+                        throw new NumberFormatException(
+                            "Length exceeded limits");
                     }
                 } catch (NumberFormatException e) {
                     if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
-                        SSLLogger.fine("jdk.net.keyLimits:  " + e.getMessage() +
+                        SSLLogger.fine("jdk.tls.keyLimits:  " + e.getMessage() +
                                 ":  " +  entry);
                     }
                     continue;
                 }
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
-                    SSLLogger.fine("jdk.net.keyLimits:  entry = " + entry +
+                    SSLLogger.fine("jdk.tls.keyLimits:  entry = " + entry +
                             ". " + values[0] + ":" + tag[index] + " = " + size);
                 }
                 cipherLimits.put(values[0] + ":" + tag[index], size);
--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Tue Aug 21 11:30:48 2018 -0700
@@ -127,9 +127,7 @@
         }
 
         // See if the handshaker needs to report back some SSLException.
-        if (conContext.outputRecord.isEmpty()) {
-            checkTaskThrown();
-        }   // Otherwise, deliver cached records before throwing task exception.
+        checkTaskThrown();
 
         // check parameters
         checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
@@ -896,18 +894,58 @@
         return true;
     }
 
+    /*
+     * Depending on whether the error was just a warning and the
+     * handshaker wasn't closed, or fatal and the handshaker is now
+     * null, report back the Exception that happened in the delegated
+     * task(s).
+     */
     private synchronized void checkTaskThrown() throws SSLException {
+
+        Exception exc = null;
+
+        // First check the handshake context.
         HandshakeContext hc = conContext.handshakeContext;
-        if (hc != null && hc.delegatedThrown != null) {
-            try {
-                throw getTaskThrown(hc.delegatedThrown);
-            } finally {
-                hc.delegatedThrown = null;
+        if ((hc != null) && (hc.delegatedThrown != null)) {
+            exc = hc.delegatedThrown;
+            hc.delegatedThrown = null;
+        }
+
+        /*
+         * hc.delegatedThrown and conContext.delegatedThrown are most likely
+         * the same, but it's possible we could have had a non-fatal
+         * exception and thus the new HandshakeContext is still valid
+         * (alert warning).  If so, then we may have a secondary exception
+         * waiting to be reported from the TransportContext, so we will
+         * need to clear that on a successive call.  Otherwise, clear it now.
+         */
+        if (conContext.delegatedThrown != null) {
+            if (exc != null) {
+                // hc object comparison
+                if (conContext.delegatedThrown == exc) {
+                    // clear if/only if both are the same
+                    conContext.delegatedThrown = null;
+                } // otherwise report the hc delegatedThrown
+            } else {
+                // Nothing waiting in HandshakeContext, but one is in the
+                // TransportContext.
+                exc = conContext.delegatedThrown;
+                conContext.delegatedThrown = null;
             }
         }
 
-        if (conContext.isBroken && conContext.closeReason != null) {
-            throw getTaskThrown(conContext.closeReason);
+        // Anything to report?
+        if (exc == null) {
+            return;
+        }
+
+        // If it wasn't a RuntimeException/SSLException, need to wrap it.
+        if (exc instanceof SSLException) {
+            throw (SSLException)exc;
+        } else if (exc instanceof RuntimeException) {
+            throw (RuntimeException)exc;
+        } else {
+            throw getTaskThrown(exc);
         }
     }
 
@@ -963,20 +1001,41 @@
                 } catch (PrivilegedActionException pae) {
                     // Get the handshake context again in case the
                     // handshaking has completed.
+                    Exception reportedException = pae.getException();
+
+                    // Report to both the TransportContext...
+                    if (engine.conContext.delegatedThrown == null) {
+                        engine.conContext.delegatedThrown = reportedException;
+                    }
+
+                    // ...and the HandshakeContext in case condition
+                    // wasn't fatal and the handshakeContext is still
+                    // around.
                     hc = engine.conContext.handshakeContext;
                     if (hc != null) {
-                        hc.delegatedThrown = pae.getException();
+                        hc.delegatedThrown = reportedException;
                     } else if (engine.conContext.closeReason != null) {
+                        // Update the reason in case there was a previous.
                         engine.conContext.closeReason =
-                                getTaskThrown(pae.getException());
+                                getTaskThrown(reportedException);
                     }
                 } catch (RuntimeException rte) {
                     // Get the handshake context again in case the
                     // handshaking has completed.
+
+                    // Report to both the TransportContext...
+                    if (engine.conContext.delegatedThrown == null) {
+                        engine.conContext.delegatedThrown = rte;
+                    }
+
+                    // ...and the HandshakeContext in case condition
+                    // wasn't fatal and the handshakeContext is still
+                    // around.
                     hc = engine.conContext.handshakeContext;
                     if (hc != null) {
                         hc.delegatedThrown = rte;
                     } else if (engine.conContext.closeReason != null) {
+                        // Update the reason in case there was a previous.
                         engine.conContext.closeReason = rte;
                     }
                 }
@@ -1000,13 +1059,6 @@
             @Override
             public Void run() throws Exception {
                 while (!context.delegatedActions.isEmpty()) {
-                    // Report back the task SSLException
-                    if (context.delegatedThrown != null) {
-                        Exception delegatedThrown = context.delegatedThrown;
-                        context.delegatedThrown = null;
-                        throw getTaskThrown(delegatedThrown);
-                    }
-
                     Map.Entry<Byte, ByteBuffer> me =
                             context.delegatedActions.poll();
                     if (me != null) {
--- a/src/java.base/share/classes/sun/security/ssl/SSLTransport.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java	Tue Aug 21 11:30:48 2018 -0700
@@ -167,9 +167,10 @@
             if (plainText == null) {
                 plainText = Plaintext.PLAINTEXT_NULL;
             } else {
-                // File the destination buffers.
-                if (dsts != null && dstsLength > 0 &&
-                    plainText.contentType == ContentType.APPLICATION_DATA.id) {
+                // Fill the destination buffers.
+                if ((dsts != null) && (dstsLength > 0) &&
+                        (plainText.contentType ==
+                            ContentType.APPLICATION_DATA.id)) {
 
                     ByteBuffer fragment = plainText.fragment;
                     int remains = fragment.remaining();
--- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java	Tue Aug 21 11:30:48 2018 -0700
@@ -40,6 +40,7 @@
 import javax.crypto.spec.IvParameterSpec;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLProtocolException;
 import sun.security.ssl.CipherSuite.KeyExchange;
 import sun.security.ssl.ClientHello.ClientHelloMessage;
 import sun.security.ssl.SSLCipher.SSLReadCipher;
@@ -139,8 +140,11 @@
 
             this.serverRandom = new RandomCookie(m);
             this.sessionId = new SessionId(Record.getBytes8(m));
-            sessionId.checkLength(serverVersion.id);
-
+            try {
+                sessionId.checkLength(serverVersion.id);
+            } catch (SSLProtocolException ex) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, ex);
+            }
 
             int cipherSuiteId = Record.getInt16(m);
             this.cipherSuite = CipherSuite.valueOf(cipherSuiteId);
--- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java	Tue Aug 21 11:30:48 2018 -0700
@@ -362,16 +362,16 @@
                 // certificates and server key exchange), it MUST send the
                 // signature_algorithms extension, listing the algorithms it
                 // is willing to accept.
-                List<SignatureScheme> shemes = Arrays.asList(
+                List<SignatureScheme> schemes = Arrays.asList(
                         SignatureScheme.RSA_PKCS1_SHA1,
                         SignatureScheme.DSA_SHA1,
                         SignatureScheme.ECDSA_SHA1
                 );
 
-                shc.peerRequestedSignatureSchemes = shemes;
+                shc.peerRequestedSignatureSchemes = schemes;
                 if (shc.peerRequestedCertSignSchemes == null ||
-                    shc.peerRequestedCertSignSchemes.isEmpty()) {
-                        shc.peerRequestedCertSignSchemes = shemes;
+                        shc.peerRequestedCertSignSchemes.isEmpty()) {
+                    shc.peerRequestedCertSignSchemes = schemes;
                 }
 
                 // Use the default peer signature algorithms.
--- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Tue Aug 21 11:30:48 2018 -0700
@@ -403,8 +403,8 @@
 
         for (SignatureScheme ss : schemes) {
             if (ss.isAvailable &&
-            ss.handshakeSupportedProtocols.contains(version) &&
-            certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
+                    ss.handshakeSupportedProtocols.contains(version) &&
+                    certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
 
                 return ss;
             }
--- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java	Tue Aug 21 11:30:48 2018 -0700
@@ -63,6 +63,7 @@
     boolean                         isInputCloseNotified = false;
     boolean                         peerUserCanceled = false;
     Exception                       closeReason = null;
+    Exception                       delegatedThrown = null;
 
     // negotiated security parameters
     SSLSessionImpl                  conSession;
@@ -364,12 +365,12 @@
             }
         }
 
-        // terminal handshake context
+        // terminate the handshake context
         if (handshakeContext != null) {
             handshakeContext = null;
         }
 
-        // terminal the transport
+        // terminate the transport
         try {
             transport.shutdown();
         } catch (IOException ioe) {
--- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java	Tue Aug 21 11:30:48 2018 -0700
@@ -73,7 +73,7 @@
     }
 
     static X509Authentication valueOf(SignatureScheme signatureScheme) {
-        for (X509Authentication au: X509Authentication.values()) {
+        for (X509Authentication au : X509Authentication.values()) {
             if (au.keyType.equals(signatureScheme.keyAlgorithm)) {
                 return au;
             }
@@ -291,9 +291,9 @@
                         ((ECPublicKey)serverPublicKey).getParams();
                 NamedGroup namedGroup = NamedGroup.valueOf(params);
                 if ((namedGroup == null) ||
-                    (!SupportedGroups.isSupported(namedGroup)) ||
-                    ((shc.clientRequestedNamedGroups != null) &&
-                    !shc.clientRequestedNamedGroups.contains(namedGroup))) {
+                        (!SupportedGroups.isSupported(namedGroup)) ||
+                        ((shc.clientRequestedNamedGroups != null) &&
+                        !shc.clientRequestedNamedGroups.contains(namedGroup))) {
 
                     if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
                         SSLLogger.warning(
--- a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java	Tue Aug 21 11:30:48 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, 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
@@ -30,7 +30,6 @@
  * @summary SSLEngine has not yet caused Solaris kernel to panic
  * @run main/othervm SSLEngineTemplate
  */
-
 /**
  * A SSLEngine usage example which simplifies the presentation
  * by removing the I/O and multi-threading concerns.
@@ -66,7 +65,6 @@
  *      unwrap()        ...             ChangeCipherSpec
  *      unwrap()        ...             Finished
  */
-
 import javax.net.ssl.*;
 import javax.net.ssl.SSLEngineResult.*;
 import java.io.*;
@@ -115,7 +113,7 @@
     private static final String pathToStores = "../etc";
     private static final String keyStoreFile = "keystore";
     private static final String trustStoreFile = "truststore";
-    private static final String passwd = "passphrase";
+    private static final char[] passphrase = "passphrase".toCharArray();
 
     private static final String keyFilename =
             System.getProperty("test.src", ".") + "/" + pathToStores +
@@ -146,8 +144,6 @@
         KeyStore ks = KeyStore.getInstance("JKS");
         KeyStore ts = KeyStore.getInstance("JKS");
 
-        char[] passphrase = "passphrase".toCharArray();
-
         ks.load(new FileInputStream(keyFilename), passphrase);
         ts.load(new FileInputStream(trustFilename), passphrase);
 
@@ -187,8 +183,11 @@
         createSSLEngines();
         createBuffers();
 
-        SSLEngineResult clientResult;   // results from client's last operation
-        SSLEngineResult serverResult;   // results from server's last operation
+        // results from client's last operation
+        SSLEngineResult clientResult;
+
+        // results from server's last operation
+        SSLEngineResult serverResult;
 
         /*
          * Examining the SSLEngineResults could be much more involved,
@@ -198,31 +197,62 @@
          * to write to the output pipe, we could reallocate a larger
          * pipe, but instead we wait for the peer to drain it.
          */
-        while (!isEngineClosed(clientEngine) ||
-                !isEngineClosed(serverEngine)) {
+        Exception clientException = null;
+        Exception serverException = null;
+
+        while (!isEngineClosed(clientEngine)
+                || !isEngineClosed(serverEngine)) {
 
             log("================");
 
-            clientResult = clientEngine.wrap(clientOut, cTOs);
-            log("client wrap: ", clientResult);
-            runDelegatedTasks(clientResult, clientEngine);
+            try {
+                clientResult = clientEngine.wrap(clientOut, cTOs);
+                log("client wrap: ", clientResult);
+            } catch (Exception e) {
+                clientException = e;
+                System.out.println("Client wrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(clientEngine);
+            runDelegatedTasks(clientEngine);
 
-            serverResult = serverEngine.wrap(serverOut, sTOc);
-            log("server wrap: ", serverResult);
-            runDelegatedTasks(serverResult, serverEngine);
+            log("----");
+
+            try {
+                serverResult = serverEngine.wrap(serverOut, sTOc);
+                log("server wrap: ", serverResult);
+            } catch (Exception e) {
+                serverException = e;
+                System.out.println("Server wrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(serverEngine);
+            runDelegatedTasks(serverEngine);
 
             cTOs.flip();
             sTOc.flip();
 
+            log("--------");
+
+            try {
+                clientResult = clientEngine.unwrap(sTOc, clientIn);
+                log("client unwrap: ", clientResult);
+            } catch (Exception e) {
+                clientException = e;
+                System.out.println("Client unwrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(clientEngine);
+            runDelegatedTasks(clientEngine);
+
             log("----");
 
-            clientResult = clientEngine.unwrap(sTOc, clientIn);
-            log("client unwrap: ", clientResult);
-            runDelegatedTasks(clientResult, clientEngine);
-
-            serverResult = serverEngine.unwrap(cTOs, serverIn);
-            log("server unwrap: ", serverResult);
-            runDelegatedTasks(serverResult, serverEngine);
+            try {
+                serverResult = serverEngine.unwrap(cTOs, serverIn);
+                log("server unwrap: ", serverResult);
+            } catch (Exception e) {
+                serverException = e;
+                System.out.println("Server unwrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(serverEngine);
+            runDelegatedTasks(serverEngine);
 
             cTOs.compact();
             sTOc.compact();
@@ -244,13 +274,22 @@
 
                 log("\tClosing clientEngine's *OUTBOUND*...");
                 clientEngine.closeOutbound();
+                logEngineStatus(clientEngine);
+
                 dataDone = true;
                 log("\tClosing serverEngine's *OUTBOUND*...");
                 serverEngine.closeOutbound();
+                logEngineStatus(serverEngine);
             }
         }
     }
 
+    private static void logEngineStatus(SSLEngine engine) {
+        log("\tCurrent HS State  " + engine.getHandshakeStatus().toString());
+        log("\tisInboundDone():  " + engine.isInboundDone());
+        log("\tisOutboundDone(): " + engine.isOutboundDone());
+    }
+
     /*
      * Using the SSLContext created during object creation,
      * create/configure the SSLEngines we'll use for this test.
@@ -264,11 +303,19 @@
         serverEngine.setUseClientMode(false);
         serverEngine.setNeedClientAuth(true);
 
+        // Get/set parameters if needed
+        SSLParameters paramsServer = serverEngine.getSSLParameters();
+        serverEngine.setSSLParameters(paramsServer);
+
         /*
          * Similar to above, but using client mode instead.
          */
         clientEngine = sslc.createSSLEngine("client", 80);
         clientEngine.setUseClientMode(true);
+
+        // Get/set parameters if needed
+        SSLParameters paramsClient = clientEngine.getSSLParameters();
+        clientEngine.setSSLParameters(paramsClient);
     }
 
     /*
@@ -307,13 +354,12 @@
      * If the result indicates that we have outstanding tasks to do,
      * go ahead and run them in this thread.
      */
-    private static void runDelegatedTasks(SSLEngineResult result,
-            SSLEngine engine) throws Exception {
+    private static void runDelegatedTasks(SSLEngine engine) throws Exception {
 
-        if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+        if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
             Runnable runnable;
             while ((runnable = engine.getDelegatedTask()) != null) {
-                log("\trunning delegated task...");
+                log("    running delegated task...");
                 runnable.run();
             }
             HandshakeStatus hsStatus = engine.getHandshakeStatus();
@@ -321,7 +367,7 @@
                 throw new Exception(
                     "handshake shouldn't need additional tasks");
             }
-            log("\tnew HandshakeStatus: " + hsStatus);
+            logEngineStatus(engine);
         }
     }
 
@@ -361,14 +407,14 @@
         if (resultOnce) {
             resultOnce = false;
             System.out.println("The format of the SSLEngineResult is: \n" +
-                "\t\"getStatus() / getHandshakeStatus()\" +\n" +
-                "\t\"bytesConsumed() / bytesProduced()\"\n");
+                    "\t\"getStatus() / getHandshakeStatus()\" +\n" +
+                    "\t\"bytesConsumed() / bytesProduced()\"\n");
         }
         HandshakeStatus hsStatus = result.getHandshakeStatus();
         log(str +
-            result.getStatus() + "/" + hsStatus + ", " +
-            result.bytesConsumed() + "/" + result.bytesProduced() +
-            " bytes");
+                result.getStatus() + "/" + hsStatus + ", " +
+                result.bytesConsumed() + "/" + result.bytesProduced() +
+                " bytes");
         if (hsStatus == HandshakeStatus.FINISHED) {
             log("\t...ready for application data");
         }
--- a/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java	Tue Aug 21 13:44:59 2018 +0100
+++ b/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java	Tue Aug 21 11:30:48 2018 -0700
@@ -74,6 +74,7 @@
 import java.nio.*;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Iterator;
 
 public class LengthCheckTest {
 
@@ -203,7 +204,11 @@
 
                 // Now send each ByteBuffer (each being a complete
                 // TLS record) into the client-side unwrap.
-                for (ByteBuffer bBuf : recList) {
+                // for (ByteBuffer bBuf : recList) {
+
+                Iterator<ByteBuffer> iter = recList.iterator();
+                while (!gotException && (iter.hasNext())) {
+                    ByteBuffer bBuf = iter.next();
                     dumpByteBuffer("SERVER-TO-CLIENT", bBuf);
                     try {
                         clientResult = clientEngine.unwrap(bBuf, clientIn);
@@ -232,8 +237,8 @@
             // was thrown and the proper action (a TLS alert) was
             // sent back to the server.
             if (gotException == false ||
-                !isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
-                        TLS_ALERT_UNEXPECTED_MSG)) {
+                    !isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
+                            TLS_ALERT_ILLEGAL_PARAMETER)) {
                 throw new SSLException(
                     "Client failed to throw Alert:fatal:internal_error");
             }
@@ -253,38 +258,36 @@
             ByteBuffer evilClientHello = createEvilClientHello(64);
             dumpByteBuffer("CLIENT-TO-SERVER", evilClientHello);
 
+            // Server consumes Client Hello
+            serverResult = serverEngine.unwrap(evilClientHello, serverIn);
+            log("server unwrap: ", serverResult);
+            runDelegatedTasks(serverResult, serverEngine);
+            evilClientHello.compact();
+
+            // Under normal circumstances this should be a ServerHello
+            // But should throw an exception instead due to the invalid
+            // session ID.
             try {
-                // Server consumes Client Hello
-                serverResult = serverEngine.unwrap(evilClientHello, serverIn);
-                log("server unwrap: ", serverResult);
-                runDelegatedTasks(serverResult, serverEngine);
-                evilClientHello.compact();
-
-                // Under normal circumstances this should be a ServerHello
-                // But should throw an exception instead due to the invalid
-                // session ID.
                 serverResult = serverEngine.wrap(serverOut, sTOc);
                 log("server wrap: ", serverResult);
                 runDelegatedTasks(serverResult, serverEngine);
-                sTOc.flip();
-                dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
-
-                // We expect to see the server generate an alert here
-                serverResult = serverEngine.wrap(serverOut, sTOc);
-                log("server wrap: ", serverResult);
-                runDelegatedTasks(serverResult, serverEngine);
-                sTOc.flip();
-                dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
             } catch (SSLProtocolException ssle) {
                 log("Received expected SSLProtocolException: " + ssle);
                 gotException = true;
             }
 
+            // We expect to see the server generate an alert here
+            serverResult = serverEngine.wrap(serverOut, sTOc);
+            log("server wrap: ", serverResult);
+            runDelegatedTasks(serverResult, serverEngine);
+            sTOc.flip();
+            dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
+
             // At this point we can verify that both an exception
             // was thrown and the proper action (a TLS alert) was
             // sent back to the client.
             if (gotException == false ||
-                !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
+                    !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
                         TLS_ALERT_ILLEGAL_PARAMETER)) {
                 throw new SSLException(
                     "Server failed to throw Alert:fatal:internal_error");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineFailedALPN.java	Tue Aug 21 11:30:48 2018 -0700
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2003, 2018, 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.
+
+/*
+ * @test
+ * @bug 8207317
+ * @summary SSLEngine negotiation fail Exception behavior changed from
+ *          fail-fast to fail-lazy
+ * @run main/othervm SSLEngineFailedALPN
+ */
+/**
+ * A SSLEngine usage example which simplifies the presentation
+ * by removing the I/O and multi-threading concerns.
+ *
+ * The test creates two SSLEngines, simulating a client and server.
+ * The "transport" layer consists two byte buffers:  think of them
+ * as directly connected pipes.
+ *
+ * Note, this is a *very* simple example: real code will be much more
+ * involved.  For example, different threading and I/O models could be
+ * used, transport mechanisms could close unexpectedly, and so on.
+ *
+ * When this application runs, notice that several messages
+ * (wrap/unwrap) pass before any application data is consumed or
+ * produced.  (For more information, please see the SSL/TLS
+ * specifications.)  There may several steps for a successful handshake,
+ * so it's typical to see the following series of operations:
+ *
+ *      client          server          message
+ *      ======          ======          =======
+ *      wrap()          ...             ClientHello
+ *      ...             unwrap()        ClientHello
+ *      ...             wrap()          ServerHello/Certificate
+ *      unwrap()        ...             ServerHello/Certificate
+ *      wrap()          ...             ClientKeyExchange
+ *      wrap()          ...             ChangeCipherSpec
+ *      wrap()          ...             Finished
+ *      ...             unwrap()        ClientKeyExchange
+ *      ...             unwrap()        ChangeCipherSpec
+ *      ...             unwrap()        Finished
+ *      ...             wrap()          ChangeCipherSpec
+ *      ...             wrap()          Finished
+ *      unwrap()        ...             ChangeCipherSpec
+ *      unwrap()        ...             Finished
+ */
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.io.*;
+import java.security.*;
+import java.nio.*;
+
+public class SSLEngineFailedALPN {
+
+    /*
+     * Enables logging of the SSLEngine operations.
+     */
+    private static final boolean logging = true;
+
+    /*
+     * Enables the JSSE system debugging system property:
+     *
+     *     -Djavax.net.debug=all
+     *
+     * This gives a lot of low-level information about operations underway,
+     * including specific handshake messages, and might be best examined
+     * after gaining some familiarity with this application.
+     */
+    private static final boolean debug = false;
+
+    private final SSLContext sslc;
+
+    private SSLEngine clientEngine;     // client Engine
+    private ByteBuffer clientOut;       // write side of clientEngine
+    private ByteBuffer clientIn;        // read side of clientEngine
+
+    private SSLEngine serverEngine;     // server Engine
+    private ByteBuffer serverOut;       // write side of serverEngine
+    private ByteBuffer serverIn;        // read side of serverEngine
+
+    /*
+     * For data transport, this example uses local ByteBuffers.  This
+     * isn't really useful, but the purpose of this example is to show
+     * SSLEngine concepts, not how to do network transport.
+     */
+    private ByteBuffer cTOs;            // "reliable" transport client->server
+    private ByteBuffer sTOc;            // "reliable" transport server->client
+
+    /*
+     * The following is to set up the keystores.
+     */
+    private static final String pathToStores = "../../../../javax/net/ssl/etc";
+    private static final String keyStoreFile = "keystore";
+    private static final String trustStoreFile = "truststore";
+    private static final char[] passphrase = "passphrase".toCharArray();
+
+    private static final String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+    private static final String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+    /*
+     * Main entry point for this test.
+     */
+    public static void main(String args[]) throws Exception {
+        if (debug) {
+            System.setProperty("javax.net.debug", "all");
+        }
+
+        SSLEngineFailedALPN test = new SSLEngineFailedALPN();
+        test.runTest();
+
+        System.out.println("Test Passed.");
+    }
+
+    /*
+     * Create an initialized SSLContext to use for these tests.
+     */
+    public SSLEngineFailedALPN() throws Exception {
+
+        KeyStore ks = KeyStore.getInstance("JKS");
+        KeyStore ts = KeyStore.getInstance("JKS");
+
+        ks.load(new FileInputStream(keyFilename), passphrase);
+        ts.load(new FileInputStream(trustFilename), passphrase);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(ks, passphrase);
+
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(ts);
+
+        SSLContext sslCtx = SSLContext.getInstance("TLS");
+
+        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+        sslc = sslCtx;
+    }
+
+    /*
+     * Run the test.
+     *
+     * Sit in a tight loop, both engines calling wrap/unwrap regardless
+     * of whether data is available or not.  We do this until both engines
+     * report back they are closed.
+     *
+     * The main loop handles all of the I/O phases of the SSLEngine's
+     * lifetime:
+     *
+     *     initial handshaking
+     *     application data transfer
+     *     engine closing
+     *
+     * One could easily separate these phases into separate
+     * sections of code.
+     */
+    private void runTest() throws Exception {
+        boolean dataDone = false;
+
+        createSSLEngines();
+        createBuffers();
+
+        // results from client's last operation
+        SSLEngineResult clientResult;
+
+        // results from server's last operation
+        SSLEngineResult serverResult;
+
+        /*
+         * Examining the SSLEngineResults could be much more involved,
+         * and may alter the overall flow of the application.
+         *
+         * For example, if we received a BUFFER_OVERFLOW when trying
+         * to write to the output pipe, we could reallocate a larger
+         * pipe, but instead we wait for the peer to drain it.
+         */
+        Exception clientException = null;
+        Exception serverException = null;
+
+        while (!isEngineClosed(clientEngine)
+                || !isEngineClosed(serverEngine)) {
+
+            log("================");
+
+            try {
+                clientResult = clientEngine.wrap(clientOut, cTOs);
+                log("client wrap: ", clientResult);
+            } catch (Exception e) {
+                clientException = e;
+                System.out.println("Client wrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(clientEngine);
+            runDelegatedTasks(clientEngine);
+
+            log("----");
+
+            try {
+                serverResult = serverEngine.wrap(serverOut, sTOc);
+                log("server wrap: ", serverResult);
+            } catch (Exception e) {
+                serverException = e;
+                System.out.println("Server wrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(serverEngine);
+            runDelegatedTasks(serverEngine);
+
+            cTOs.flip();
+            sTOc.flip();
+
+            log("--------");
+
+            try {
+                clientResult = clientEngine.unwrap(sTOc, clientIn);
+                log("client unwrap: ", clientResult);
+            } catch (Exception e) {
+                clientException = e;
+                System.out.println("Client unwrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(clientEngine);
+            runDelegatedTasks(clientEngine);
+
+            log("----");
+
+            try {
+                serverResult = serverEngine.unwrap(cTOs, serverIn);
+                log("server unwrap: ", serverResult);
+            } catch (Exception e) {
+                serverException = e;
+                System.out.println("Server unwrap() threw: " + e.getMessage());
+            }
+            logEngineStatus(serverEngine);
+            runDelegatedTasks(serverEngine);
+
+            cTOs.compact();
+            sTOc.compact();
+
+            /*
+             * After we've transfered all application data between the client
+             * and server, we close the clientEngine's outbound stream.
+             * This generates a close_notify handshake message, which the
+             * server engine receives and responds by closing itself.
+             */
+            if (!dataDone && (clientOut.limit() == serverIn.position()) &&
+                    (serverOut.limit() == clientIn.position())) {
+
+                /*
+                 * A sanity check to ensure we got what was sent.
+                 */
+                checkTransfer(serverOut, clientIn);
+                checkTransfer(clientOut, serverIn);
+
+                log("\tClosing clientEngine's *OUTBOUND*...");
+                clientEngine.closeOutbound();
+                logEngineStatus(clientEngine);
+
+                dataDone = true;
+                log("\tClosing serverEngine's *OUTBOUND*...");
+                serverEngine.closeOutbound();
+                logEngineStatus(serverEngine);
+            }
+        }
+
+        log("================");
+
+        if ((clientException != null) &&
+                (clientException instanceof SSLHandshakeException)) {
+            log("Client threw proper exception");
+            clientException.printStackTrace(System.out);
+        } else {
+            throw new Exception("Client Exception not seen");
+        }
+
+        if ((serverException != null) &&
+                (serverException instanceof SSLHandshakeException)) {
+            log("Server threw proper exception:");
+            serverException.printStackTrace(System.out);
+        } else {
+            throw new Exception("Server Exception not seen");
+        }
+    }
+
+    private static void logEngineStatus(SSLEngine engine) {
+        log("\tCurrent HS State  " + engine.getHandshakeStatus().toString());
+        log("\tisInboundDone():  " + engine.isInboundDone());
+        log("\tisOutboundDone(): " + engine.isOutboundDone());
+    }
+
+    /*
+     * Using the SSLContext created during object creation,
+     * create/configure the SSLEngines we'll use for this test.
+     */
+    private void createSSLEngines() throws Exception {
+        /*
+         * Configure the serverEngine to act as a server in the SSL/TLS
+         * handshake.  Also, require SSL client authentication.
+         */
+        serverEngine = sslc.createSSLEngine();
+        serverEngine.setUseClientMode(false);
+        serverEngine.setNeedClientAuth(true);
+
+        // Get/set parameters if needed
+        SSLParameters paramsServer = serverEngine.getSSLParameters();
+        paramsServer.setApplicationProtocols(new String[]{"one"});
+        serverEngine.setSSLParameters(paramsServer);
+
+        /*
+         * Similar to above, but using client mode instead.
+         */
+        clientEngine = sslc.createSSLEngine("client", 80);
+        clientEngine.setUseClientMode(true);
+
+        // Get/set parameters if needed
+        SSLParameters paramsClient = clientEngine.getSSLParameters();
+        paramsClient.setApplicationProtocols(new String[]{"two"});
+        clientEngine.setSSLParameters(paramsClient);
+    }
+
+    /*
+     * Create and size the buffers appropriately.
+     */
+    private void createBuffers() {
+
+        /*
+         * We'll assume the buffer sizes are the same
+         * between client and server.
+         */
+        SSLSession session = clientEngine.getSession();
+        int appBufferMax = session.getApplicationBufferSize();
+        int netBufferMax = session.getPacketBufferSize();
+
+        /*
+         * We'll make the input buffers a bit bigger than the max needed
+         * size, so that unwrap()s following a successful data transfer
+         * won't generate BUFFER_OVERFLOWS.
+         *
+         * We'll use a mix of direct and indirect ByteBuffers for
+         * tutorial purposes only.  In reality, only use direct
+         * ByteBuffers when they give a clear performance enhancement.
+         */
+        clientIn = ByteBuffer.allocate(appBufferMax + 50);
+        serverIn = ByteBuffer.allocate(appBufferMax + 50);
+
+        cTOs = ByteBuffer.allocateDirect(netBufferMax);
+        sTOc = ByteBuffer.allocateDirect(netBufferMax);
+
+        clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
+        serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
+    }
+
+    /*
+     * If the result indicates that we have outstanding tasks to do,
+     * go ahead and run them in this thread.
+     */
+    private static void runDelegatedTasks(SSLEngine engine) throws Exception {
+
+        if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+            Runnable runnable;
+            while ((runnable = engine.getDelegatedTask()) != null) {
+                log("    running delegated task...");
+                runnable.run();
+            }
+            HandshakeStatus hsStatus = engine.getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_TASK) {
+                throw new Exception(
+                    "handshake shouldn't need additional tasks");
+            }
+            logEngineStatus(engine);
+        }
+    }
+
+    private static boolean isEngineClosed(SSLEngine engine) {
+        return (engine.isOutboundDone() && engine.isInboundDone());
+    }
+
+    /*
+     * Simple check to make sure everything came across as expected.
+     */
+    private static void checkTransfer(ByteBuffer a, ByteBuffer b)
+            throws Exception {
+        a.flip();
+        b.flip();
+
+        if (!a.equals(b)) {
+            throw new Exception("Data didn't transfer cleanly");
+        } else {
+            log("\tData transferred cleanly");
+        }
+
+        a.position(a.limit());
+        b.position(b.limit());
+        a.limit(a.capacity());
+        b.limit(b.capacity());
+    }
+
+    /*
+     * Logging code
+     */
+    private static boolean resultOnce = true;
+
+    private static void log(String str, SSLEngineResult result) {
+        if (!logging) {
+            return;
+        }
+        if (resultOnce) {
+            resultOnce = false;
+            System.out.println("The format of the SSLEngineResult is: \n" +
+                    "\t\"getStatus() / getHandshakeStatus()\" +\n" +
+                    "\t\"bytesConsumed() / bytesProduced()\"\n");
+        }
+        HandshakeStatus hsStatus = result.getHandshakeStatus();
+        log(str +
+                result.getStatus() + "/" + hsStatus + ", " +
+                result.bytesConsumed() + "/" + result.bytesProduced() +
+                " bytes");
+        if (hsStatus == HandshakeStatus.FINISHED) {
+            log("\t...ready for application data");
+        }
+    }
+
+    private static void log(String str) {
+        if (logging) {
+            System.out.println(str);
+        }
+    }
+}