Skip to content

Commit 85dc154

Browse files
committed
JSSE: calling SSLSocket.close() should interrupt threads blocked in select()/poll()
1 parent e600b0f commit 85dc154

9 files changed

Lines changed: 819 additions & 64 deletions

File tree

native/com_wolfssl_WolfSSLSession.c

Lines changed: 361 additions & 29 deletions
Large diffs are not rendered by default.

native/com_wolfssl_WolfSSLSession.h

Lines changed: 18 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/java/com/wolfssl/WolfSSLSession.java

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,75 @@ public class WolfSSLSession {
102102
/**
103103
* Creates a new SSL/TLS session.
104104
*
105+
* Native session created also creates JNI SSLAppData for usage
106+
* internal to wolfSSL JNI. This constructor creates a default
107+
* pipe() to use for interrupting threads waiting in select()/poll()
108+
* when close() is called. To skip creation of this pipe() use
109+
* the WolfSSLSession(WolfSSLContext ctx, boolean setupIOPipe)
110+
* constructor with 'setupIOPipe' set to false.
111+
*
105112
* @param ctx WolfSSLContext object used to create SSL session.
106113
*
107114
* @throws com.wolfssl.WolfSSLException if session object creation
108115
* failed.
109116
*/
110117
public WolfSSLSession(WolfSSLContext ctx) throws WolfSSLException {
111118

112-
sslPtr = newSSL(ctx.getContextPtr());
119+
sslPtr = newSSL(ctx.getContextPtr(), true);
113120
if (sslPtr == 0) {
114121
throw new WolfSSLException("Failed to create SSL Object");
115122
}
116123

117124
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
118-
WolfSSLDebug.INFO, sslPtr, "creating new WolfSSLSession");
125+
WolfSSLDebug.INFO, sslPtr,
126+
"creating new WolfSSLSession (with I/O pipe)");
127+
128+
synchronized (stateLock) {
129+
this.active = true;
130+
}
131+
132+
/* save context reference for I/O callbacks from JNI */
133+
this.ctx = ctx;
134+
}
135+
136+
/**
137+
* Creates a new SSL/TLS session.
138+
*
139+
* Native session created also creates JNI SSLAppData for usage
140+
* internal to wolfSSL JNI. A pipe() can be created internally to wolfSSL
141+
* JNI to use for interrupting threads waiting in select()/poll()
142+
* when close() is called. To skip creation of this pipe(), set
143+
* 'setupIOPipe' to false.
144+
*
145+
* It is generally recommended to have wolfSSL JNI create the native
146+
* pipe(), unless you will be operating over non-Socket I/O. For example,
147+
* when this WolfSSLSession is being created from the JSSE level
148+
* SSLEngine class.
149+
*
150+
* @param ctx WolfSSLContext object used to create SSL session.
151+
* @param setupIOPipe true to create internal IO pipe(), otherwise
152+
* false
153+
*
154+
* @throws com.wolfssl.WolfSSLException if session object creation
155+
* failed.
156+
*/
157+
public WolfSSLSession(WolfSSLContext ctx, boolean setupIOPipe)
158+
throws WolfSSLException {
159+
160+
sslPtr = newSSL(ctx.getContextPtr(), false);
161+
if (sslPtr == 0) {
162+
throw new WolfSSLException("Failed to create SSL Object");
163+
}
164+
165+
if (setupIOPipe) {
166+
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
167+
WolfSSLDebug.INFO, sslPtr,
168+
"creating new WolfSSLSession (with I/O pipe)");
169+
} else {
170+
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
171+
WolfSSLDebug.INFO, sslPtr,
172+
"creating new WolfSSLSession (without I/O pipe)");
173+
}
119174

120175
synchronized (stateLock) {
121176
this.active = true;
@@ -240,7 +295,7 @@ private synchronized void confirmObjectIsActive()
240295

241296
/* ------------------ native method declarations -------------------- */
242297

243-
private native long newSSL(long ctx);
298+
private native long newSSL(long ctx, boolean withIOPipe);
244299
private native int setFd(long ssl, Socket sd, int type);
245300
private native int setFd(long ssl, DatagramSocket sd, int type);
246301
private native int useCertificateFile(long ssl, String file, int format);
@@ -357,6 +412,8 @@ private native int setTlsHmacInner(long ssl, byte[] inner, long sz,
357412
private native int set1SigAlgsList(long ssl, String list);
358413
private native int useSupportedCurve(long ssl, int name);
359414
private native int hasTicket(long session);
415+
private native int interruptBlockedIO(long ssl);
416+
private native int getThreadsBlockedInPoll(long ssl);
360417

361418
/* ------------------- session-specific methods --------------------- */
362419

@@ -4125,6 +4182,49 @@ public synchronized void setIOSend(WolfSSLIOSendCallback callback)
41254182
}
41264183
}
41274184

4185+
/**
4186+
* Interrupt native I/O operations blocked inside select()/poll().
4187+
*
4188+
* This is used by wolfJSSE when SSLSocket.close() is called, to wake up
4189+
* threads that are blocked in select()/poll().
4190+
*
4191+
* @return WolfSSL.SSL_SUCCESS on success, negative on error.
4192+
*
4193+
* @throws IllegalStateException WolfSSLSession has been freed
4194+
*/
4195+
public synchronized int interruptBlockedIO()
4196+
throws IllegalStateException {
4197+
4198+
confirmObjectIsActive();
4199+
4200+
/* Not synchronizing on sslLock, since we want to interrupt threads
4201+
* blocked on I/O operations, which will already hold sslLock */
4202+
4203+
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
4204+
WolfSSLDebug.INFO, "entered interruptBlockedIO()");
4205+
4206+
return interruptBlockedIO(this.sslPtr);
4207+
}
4208+
4209+
/**
4210+
* Get count of threads currently blocked in select() or poll()
4211+
* at the native JNI level.
4212+
*
4213+
* @return count of threads waiting in select() or poll()
4214+
*
4215+
* @throws IllegalStateException WolfSSLSession has been freed
4216+
*/
4217+
public synchronized int getThreadsBlockedInPoll()
4218+
throws IllegalStateException {
4219+
4220+
confirmObjectIsActive();
4221+
4222+
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
4223+
WolfSSLDebug.INFO, "entered getThreadsBlockedInPoll()");
4224+
4225+
return getThreadsBlockedInPoll(this.sslPtr);
4226+
}
4227+
41284228
/**
41294229
* Use SNI name with this session.
41304230
*

src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ private void initSSL() throws WolfSSLException, WolfSSLJNIException {
313313
}
314314

315315
/* will throw WolfSSLException if issue creating WOLFSSL */
316-
ssl = new WolfSSLSession(ctx);
316+
ssl = new WolfSSLSession(ctx, false);
317317

318318
enableExtraDebug();
319319
enableIODebug();

src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,10 @@ public synchronized void close() throws IOException {
20112011
handshakeFinished = this.handshakeComplete;
20122012
}
20132013

2014+
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
2015+
"signaling any blocked I/O threads to wake up");
2016+
ssl.interruptBlockedIO();
2017+
20142018
/* Try TLS shutdown procedure, only if handshake has finished */
20152019
if (ssl != null && handshakeFinished) {
20162020
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@@ -2036,12 +2040,15 @@ public synchronized void close() throws IOException {
20362040
}
20372041

20382042
try {
2043+
/* Use SO_LINGER value when calling
2044+
* shutdown here, since we are closing the
2045+
* socket */
20392046
if (this.socket != null) {
20402047
ret = ssl.shutdownSSL(
2041-
this.socket.getSoTimeout());
2048+
this.socket.getSoLinger());
20422049
} else {
20432050
ret = ssl.shutdownSSL(
2044-
super.getSoTimeout());
2051+
super.getSoLinger());
20452052
}
20462053

20472054
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@@ -2065,9 +2072,7 @@ public synchronized void close() throws IOException {
20652072
/* Release native verify callback (JNI global) */
20662073
this.EngineHelper.unsetVerifyCallback();
20672074

2068-
/* Connection is closed, free native WOLFSSL session
2069-
* to release native memory earlier than garbage
2070-
* collector might with finalize(). */
2075+
/* Close ConsumedRecvCtx data stream */
20712076
Object readCtx = this.ssl.getIOReadCtx();
20722077
if (readCtx != null &&
20732078
readCtx instanceof ConsumedRecvCtx) {
@@ -2076,14 +2081,6 @@ public synchronized void close() throws IOException {
20762081
"calling ConsumedRecvCtx.closeDataStreams()");
20772082
rctx.closeDataStreams();
20782083
}
2079-
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
2080-
"calling this.ssl.freeSSL()");
2081-
this.ssl.freeSSL();
2082-
this.ssl = null;
2083-
2084-
/* Reset internal WolfSSLEngineHelper to null */
2085-
this.EngineHelper.clearObjectState();
2086-
this.EngineHelper = null;
20872084

20882085
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
20892086
"thread exiting handshakeLock (shutdown)");
@@ -2112,6 +2109,33 @@ public synchronized void close() throws IOException {
21122109
this.outStream = null;
21132110
}
21142111
}
2112+
2113+
/* Free this.ssl here instead of above for use cases
2114+
* where a SSLSocket is created then closed()'d before
2115+
* connected or handshake is done. freeSSL() will
2116+
* release interruptFds[] pipe() and free up descriptor. */
2117+
synchronized (ioLock) {
2118+
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
2119+
"thread got ioLock (freeSSL)");
2120+
2121+
/* Connection is closed, free native WOLFSSL session
2122+
* to release native memory earlier than garbage
2123+
* collector might with finalize(), if we don't
2124+
* have any threads still waiting in poll/select. */
2125+
if (this.ssl.getThreadsBlockedInPoll() == 0) {
2126+
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
2127+
"calling this.ssl.freeSSL()");
2128+
this.ssl.freeSSL();
2129+
this.ssl = null;
2130+
}
2131+
2132+
/* Reset internal WolfSSLEngineHelper to null */
2133+
this.EngineHelper.clearObjectState();
2134+
this.EngineHelper = null;
2135+
2136+
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
2137+
"thread exiting ioLock (shutdown)");
2138+
} /* ioLock */
21152139
}
21162140

21172141
if (this.autoClose) {
@@ -2687,7 +2711,11 @@ public synchronized int read(byte[] b, int off, int len)
26872711
throw e;
26882712

26892713
} catch (IllegalStateException e) {
2690-
throw new IOException(e);
2714+
/* SSLSocket.close() may have already called freeSSL(),
2715+
* thus causing a 'WolfSSLSession object has been freed'
2716+
* IllegalStateException to be thrown from
2717+
* WolfSSLSession.read(). Return as a SocketException here. */
2718+
throw new SocketException(e.getMessage());
26912719
}
26922720

26932721
/* return number of bytes read */

0 commit comments

Comments
 (0)