Path: blob/master/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineDeadlock.java
41152 views
/*1* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223// SunJSSE does not support dynamic system properties, no way to re-use24// system properties in samevm/agentvm mode.2526/*27* @test28* @bug 649287229* @summary Deadlock in SSLEngine30* @run main/othervm SSLEngineDeadlock31* @author Brad R. Wetmore32*/3334/**35* A SSLEngine usage example which simplifies the presentation36* by removing the I/O and multi-threading concerns.37*38* The test creates two SSLEngines, simulating a client and server.39* The "transport" layer consists two byte buffers: think of them40* as directly connected pipes.41*42* Note, this is a *very* simple example: real code will be much more43* involved. For example, different threading and I/O models could be44* used, transport mechanisms could close unexpectedly, and so on.45*46* When this application runs, notice that several messages47* (wrap/unwrap) pass before any application data is consumed or48* produced. (For more information, please see the SSL/TLS49* specifications.) There may several steps for a successful handshake,50* so it's typical to see the following series of operations:51*52* client server message53* ====== ====== =======54* wrap() ... ClientHello55* ... unwrap() ClientHello56* ... wrap() ServerHello/Certificate57* unwrap() ... ServerHello/Certificate58* wrap() ... ClientKeyExchange59* wrap() ... ChangeCipherSpec60* wrap() ... Finished61* ... unwrap() ClientKeyExchange62* ... unwrap() ChangeCipherSpec63* ... unwrap() Finished64* ... wrap() ChangeCipherSpec65* ... wrap() Finished66* unwrap() ... ChangeCipherSpec67* unwrap() ... Finished68*/6970import javax.net.ssl.*;71import javax.net.ssl.SSLEngineResult.*;72import java.io.*;73import java.security.*;74import java.nio.*;75import java.lang.management.*;7677public class SSLEngineDeadlock {7879/*80* Enables logging of the SSLEngine operations.81*/82private static boolean logging = false;8384/*85* Enables the JSSE system debugging system property:86*87* -Djavax.net.debug=all88*89* This gives a lot of low-level information about operations underway,90* including specific handshake messages, and might be best examined91* after gaining some familiarity with this application.92*/93private static boolean debug = false;9495private SSLContext sslc;9697private SSLEngine clientEngine; // client Engine98private ByteBuffer clientOut; // write side of clientEngine99private ByteBuffer clientIn; // read side of clientEngine100101private SSLEngine serverEngine; // server Engine102private ByteBuffer serverOut; // write side of serverEngine103private ByteBuffer serverIn; // read side of serverEngine104105private volatile boolean testDone = false;106107/*108* For data transport, this example uses local ByteBuffers. This109* isn't really useful, but the purpose of this example is to show110* SSLEngine concepts, not how to do network transport.111*/112private ByteBuffer cTOs; // "reliable" transport client->server113private ByteBuffer sTOc; // "reliable" transport server->client114115/*116* The following is to set up the keystores.117*/118private static String pathToStores = "../../../../javax/net/ssl/etc";119private static String keyStoreFile = "keystore";120private static String trustStoreFile = "truststore";121private static String passwd = "passphrase";122123private static String keyFilename =124System.getProperty("test.src", ".") + "/" + pathToStores +125"/" + keyStoreFile;126private static String trustFilename =127System.getProperty("test.src", ".") + "/" + pathToStores +128"/" + trustStoreFile;129130/*131* Main entry point for this test.132*/133public static void main(String args[]) throws Exception {134if (debug) {135System.setProperty("javax.net.debug", "all");136}137138// Turn off logging, and only output the test iteration to keep139// the noise down.140for (int i = 1; i <= 200; i++) {141if ((i % 5) == 0) {142System.out.println("Test #: " + i);143}144SSLEngineDeadlock test = new SSLEngineDeadlock();145test.runTest();146147detectDeadLock();148}149System.out.println("Test Passed.");150}151152/*153* Create an initialized SSLContext to use for these tests.154*/155public SSLEngineDeadlock() throws Exception {156157KeyStore ks = KeyStore.getInstance("JKS");158KeyStore ts = KeyStore.getInstance("JKS");159160char[] passphrase = "passphrase".toCharArray();161162ks.load(new FileInputStream(keyFilename), passphrase);163ts.load(new FileInputStream(trustFilename), passphrase);164165KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");166kmf.init(ks, passphrase);167168TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");169tmf.init(ts);170171SSLContext sslCtx = SSLContext.getInstance("TLS");172173sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);174175sslc = sslCtx;176}177178/*179* Create a thread which simply spins on tasks. This will hopefully180* trigger a deadlock between the wrap/unwrap and the tasks. On our181* slow, single-CPU build machine (sol8), it was very repeatable.182*/183private void doTask() {184Runnable task;185186while (!testDone) {187if ((task = clientEngine.getDelegatedTask()) != null) {188task.run();189}190if ((task = serverEngine.getDelegatedTask()) != null) {191task.run();192}193}194}195196/*197* Run the test.198*199* Sit in a tight loop, both engines calling wrap/unwrap regardless200* of whether data is available or not. We do this until both engines201* report back they are closed.202*203* The main loop handles all of the I/O phases of the SSLEngine's204* lifetime:205*206* initial handshaking207* application data transfer208* engine closing209*210* One could easily separate these phases into separate211* sections of code.212*/213private void runTest() throws Exception {214boolean dataDone = false;215216createSSLEngines();217createBuffers();218219SSLEngineResult clientResult; // results from client's last operation220SSLEngineResult serverResult; // results from server's last operation221222new Thread("SSLEngine Task Dispatcher") {223public void run() {224try {225doTask();226} catch (Exception e) {227System.err.println("Task thread died...test will hang");228}229}230}.start();231232/*233* Examining the SSLEngineResults could be much more involved,234* and may alter the overall flow of the application.235*236* For example, if we received a BUFFER_OVERFLOW when trying237* to write to the output pipe, we could reallocate a larger238* pipe, but instead we wait for the peer to drain it.239*/240while (!isEngineClosed(clientEngine) ||241!isEngineClosed(serverEngine)) {242243log("================");244245clientResult = clientEngine.wrap(clientOut, cTOs);246log("client wrap: ", clientResult);247248serverResult = serverEngine.wrap(serverOut, sTOc);249log("server wrap: ", serverResult);250251cTOs.flip();252sTOc.flip();253254log("----");255256clientResult = clientEngine.unwrap(sTOc, clientIn);257log("client unwrap: ", clientResult);258259serverResult = serverEngine.unwrap(cTOs, serverIn);260log("server unwrap: ", serverResult);261262cTOs.compact();263sTOc.compact();264265/*266* After we've transfered all application data between the client267* and server, we close the clientEngine's outbound stream.268* This generates a close_notify handshake message, which the269* server engine receives and responds by closing itself.270*/271if (!dataDone && (clientOut.limit() == serverIn.position()) &&272(serverOut.limit() == clientIn.position())) {273274/*275* A sanity check to ensure we got what was sent.276*/277checkTransfer(serverOut, clientIn);278checkTransfer(clientOut, serverIn);279280log("\tClosing clientEngine's *OUTBOUND*...");281clientEngine.closeOutbound();282serverEngine.closeOutbound();283dataDone = true;284}285}286testDone = true;287}288289/*290* Using the SSLContext created during object creation,291* create/configure the SSLEngines we'll use for this test.292*/293private void createSSLEngines() throws Exception {294/*295* Configure the serverEngine to act as a server in the SSL/TLS296* handshake. Also, require SSL client authentication.297*/298serverEngine = sslc.createSSLEngine();299serverEngine.setUseClientMode(false);300serverEngine.setNeedClientAuth(true);301302/*303* Similar to above, but using client mode instead.304*/305clientEngine = sslc.createSSLEngine("client", 80);306clientEngine.setUseClientMode(true);307}308309/*310* Create and size the buffers appropriately.311*/312private void createBuffers() {313314/*315* We'll assume the buffer sizes are the same316* between client and server.317*/318SSLSession session = clientEngine.getSession();319int appBufferMax = session.getApplicationBufferSize();320int netBufferMax = session.getPacketBufferSize();321322/*323* We'll make the input buffers a bit bigger than the max needed324* size, so that unwrap()s following a successful data transfer325* won't generate BUFFER_OVERFLOWS.326*327* We'll use a mix of direct and indirect ByteBuffers for328* tutorial purposes only. In reality, only use direct329* ByteBuffers when they give a clear performance enhancement.330*/331clientIn = ByteBuffer.allocate(appBufferMax + 50);332serverIn = ByteBuffer.allocate(appBufferMax + 50);333334cTOs = ByteBuffer.allocateDirect(netBufferMax);335sTOc = ByteBuffer.allocateDirect(netBufferMax);336337clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());338serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());339}340341private static boolean isEngineClosed(SSLEngine engine) {342return (engine.isOutboundDone() && engine.isInboundDone());343}344345/*346* Simple check to make sure everything came across as expected.347*/348private static void checkTransfer(ByteBuffer a, ByteBuffer b)349throws Exception {350a.flip();351b.flip();352353if (!a.equals(b)) {354throw new Exception("Data didn't transfer cleanly");355} else {356log("\tData transferred cleanly");357}358359a.position(a.limit());360b.position(b.limit());361a.limit(a.capacity());362b.limit(b.capacity());363}364365/*366* Detect dead lock367*/368private static void detectDeadLock() throws Exception {369ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();370long[] threadIds = threadBean.findDeadlockedThreads();371if (threadIds != null && threadIds.length != 0) {372for (long id : threadIds) {373ThreadInfo info =374threadBean.getThreadInfo(id, Integer.MAX_VALUE);375System.out.println("Deadlocked ThreadInfo: " + info);376}377throw new Exception("Found Deadlock!");378}379}380381/*382* Logging code383*/384private static boolean resultOnce = true;385386private static void log(String str, SSLEngineResult result) {387if (!logging) {388return;389}390if (resultOnce) {391resultOnce = false;392System.out.println("The format of the SSLEngineResult is: \n" +393"\t\"getStatus() / getHandshakeStatus()\" +\n" +394"\t\"bytesConsumed() / bytesProduced()\"\n");395}396HandshakeStatus hsStatus = result.getHandshakeStatus();397log(str +398result.getStatus() + "/" + hsStatus + ", " +399result.bytesConsumed() + "/" + result.bytesProduced() +400" bytes");401if (hsStatus == HandshakeStatus.FINISHED) {402log("\t...ready for application data");403}404}405406private static void log(String str) {407if (logging) {408System.out.println(str);409}410}411}412413414