Skip to content

Commit 4d556fb

Browse files
authored
Merge pull request #180 from cconlon/SSLEngineGCFix
Release native verify callback with SSLEngine is closed
2 parents 9c63b7f + 66ac903 commit 4d556fb

7 files changed

Lines changed: 196 additions & 12 deletions

File tree

examples/provider/ClientJSSE.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ public void run(String[] args) throws Exception {
8787
boolean putEnabledProtocols = false; /* set enabled protocols */
8888
boolean sendGET = false; /* send HTTP GET */
8989

90+
/* Sleep 10 seconds before and after execution of main example,
91+
* to allow profilers like VisualVM to be attached. */
92+
boolean profileSleep = false;
93+
9094
boolean resumeSession = false; /* try one session resumption */
9195
byte[] firstSessionId = null; /* sess ID of first session */
9296
byte[] resumeSessionId = null; /* sess ID of resumed session */
@@ -180,6 +184,9 @@ public void run(String[] args) throws Exception {
180184
} else if (arg.equals("-r")) {
181185
resumeSession = true;
182186

187+
} else if (arg.equals("-profile")) {
188+
profileSleep = true;
189+
183190
} else {
184191
printUsage();
185192
}
@@ -197,6 +204,12 @@ public void run(String[] args) throws Exception {
197204
return;
198205
}
199206

207+
if (profileSleep) {
208+
System.out.println(
209+
"Sleeping 10 seconds to allow profiler to attach");
210+
Thread.sleep(10000);
211+
}
212+
200213
/* X509TrustManager that trusts all peer certificates. Used if peer
201214
* authentication (-d) has been passed in */
202215
TrustManager[] trustAllCerts = new TrustManager[] {
@@ -322,6 +335,25 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
322335
System.out.println("Server message : " + new String(back));
323336
sock.close();
324337
}
338+
339+
if (profileSleep) {
340+
/* Remove provider and set variables to null to help garbage
341+
* collector for profiling */
342+
Security.removeProvider("wolfJSSE");
343+
sock = null;
344+
sf = null;
345+
ctx = null;
346+
km = null;
347+
tm = null;
348+
349+
/* Try and kick start garbage collector before profiling
350+
* heap dump */
351+
System.gc();
352+
353+
System.out.println(
354+
"Sleeping 10 seconds to allow profiler to dump heap");
355+
Thread.sleep(10000);
356+
}
325357
}
326358

327359
private void showPeer(SSLSocket sock) {
@@ -385,6 +417,8 @@ private void printUsage() {
385417
System.out.println("-A <file>:<password>\tCertificate/key CA JKS file,\tdefault " +
386418
"../provider/ca-server.jks:wolfSSL test");
387419
System.out.println("-r Resume session");
420+
System.out.println("-profile\tSleep for 10 sec before/after running " +
421+
"to allow profilers to attach");
388422
System.exit(1);
389423
}
390424
}

examples/provider/MultiThreadedSSLClient.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ public class MultiThreadedSSLClient
7878
int successClientConnections = 0; /* successful client connections */
7979
int failedClientConnections = 0; /* failed client connections */
8080

81+
/* Sleep 10 seconds before and after execution of main example,
82+
* to allow profilers like VisualVM to be attached. */
83+
boolean profileSleep = false;
84+
8185
long totalConnectionTimeMs = 0; /* total handshake time, across clients */
8286
final Object timeLock = new Object();
8387

@@ -156,10 +160,6 @@ public MultiThreadedSSLClient(String[] args) {
156160
String jkspass = "wolfSSL test";
157161
char[] passArr = jkspass.toCharArray();
158162

159-
if (args.length != 2) {
160-
printUsage();
161-
}
162-
163163
/* pull in command line options from user */
164164
for (int i = 0; i < args.length; i++)
165165
{
@@ -170,12 +170,22 @@ public MultiThreadedSSLClient(String[] args) {
170170
printUsage();
171171
numClientConnections = Integer.parseInt(args[++i]);
172172

173+
} else if (arg.equals("-profile")) {
174+
profileSleep = true;
175+
173176
} else {
174177
printUsage();
175178
}
176179
}
177180

178181
try {
182+
183+
if (profileSleep) {
184+
System.out.println(
185+
"Sleeping 10 seconds to allow profiler to attach");
186+
Thread.sleep(10000);
187+
}
188+
179189
List<ClientThread> clientList = new ArrayList<ClientThread>();
180190
CountDownLatch latch = new CountDownLatch(numClientConnections);
181191

@@ -209,12 +219,22 @@ public MultiThreadedSSLClient(String[] args) {
209219
latch.await();
210220
executor.shutdown();
211221

222+
Security.removeProvider("wolfJSSE");
223+
224+
if (profileSleep) {
225+
/* Try and kick start garbage collector before profiling
226+
* heap dump */
227+
System.gc();
228+
229+
System.out.println(
230+
"Sleeping 10 seconds to allow profiler to dump heap");
231+
Thread.sleep(10000);
232+
}
233+
212234
} catch (Exception e) {
213235
e.printStackTrace();
214236
}
215237

216-
Security.removeProvider("wolfJSSE");
217-
218238
System.out.println("================================================");
219239
System.out.println("All Client Connections Finished");
220240
System.out.println("Successful = " + successClientConnections);
@@ -234,6 +254,8 @@ public static void main(String[] args) {
234254
private void printUsage() {
235255
System.out.println("Java wolfJSSE example threaded client usage:");
236256
System.out.println("-n <num>\tNumber of client connections");
257+
System.out.println("-profile\tSleep for 10 sec before/after running " +
258+
"to allow profilers to attach");
237259
System.exit(1);
238260
}
239261
}

examples/provider/ServerJSSE.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ public void run(String[] args) {
5757
boolean verifyPeer = true; /* verify peer by default */
5858
boolean useEnvVar = false; /* load cert/key from enviornment variable */
5959
boolean listSuites = false; /* list all supported cipher suites */
60-
boolean listEnabledProtocols = false; /* show enabled protocols */
61-
boolean putEnabledProtocols = false; /* set enabled protocols */
60+
boolean listEnabledProtocols = false; /* show enabled protocols */
61+
boolean putEnabledProtocols = false; /* set enabled protocols */
62+
63+
/* Sleep 10 seconds before and after execution of main example,
64+
* to allow profilers like VisualVM to be attached. */
65+
boolean profileSleep = false;
6266

6367
/* cert info */
6468
String serverJKS = "../provider/server.jks";
@@ -86,6 +90,12 @@ public void run(String[] args) {
8690

8791
/* load WolfSSLprovider */
8892
Security.addProvider(new WolfSSLProvider());
93+
if (Security.getProvider("wolfJSSE") == null) {
94+
System.out.println("Can't find wolfJSSE provider");
95+
}
96+
else {
97+
System.out.println("Registered wolfJSSE provider");
98+
}
8999

90100
/* pull in command line options from user */
91101
for (int i = 0; i < args.length; i++)
@@ -151,6 +161,9 @@ public void run(String[] args) {
151161
protocols = args[++i].split(" ");
152162
sslVersion = -1;
153163

164+
} else if (arg.equals("-profile")) {
165+
profileSleep = true;
166+
154167
} else {
155168
printUsage();
156169
}
@@ -181,6 +194,12 @@ public void run(String[] args) {
181194
System.exit(1);
182195
}
183196

197+
if (profileSleep) {
198+
System.out.println(
199+
"Sleeping 10 seconds to allow profiler to attach");
200+
Thread.sleep(10000);
201+
}
202+
184203
/* set up keystore and truststore */
185204
KeyStore keystore = KeyStore.getInstance("JKS");
186205
keystore.load(new FileInputStream(serverJKS),
@@ -258,6 +277,32 @@ public void run(String[] args) {
258277
sock.getOutputStream().write(msg.getBytes());
259278

260279
sock.close();
280+
281+
if (profileSleep) {
282+
/* If profiling, only loop once */
283+
sock = null;
284+
break;
285+
}
286+
}
287+
288+
ss.close();
289+
290+
if (profileSleep) {
291+
/* Remove provider and set variables to null to help
292+
* garbage collector for profiling */
293+
Security.removeProvider("wolfJSSE");
294+
ss = null;
295+
ctx = null;
296+
km = null;
297+
tm = null;
298+
299+
/* Try and kick start garbage collector before profiling
300+
* heap dump */
301+
System.gc();
302+
303+
System.out.println(
304+
"Sleeping 10 seconds to allow profiler to dump heap");
305+
Thread.sleep(10000);
261306
}
262307

263308
} catch (Exception e) {
@@ -298,6 +343,8 @@ private void printUsage() {
298343
"../provider/server.jks:\"wolfSSL test\"");
299344
System.out.println("-A <file>:<password>\tCertificate/key CA JKS file,\tdefault " +
300345
"../provider/ca-client.jks:\"wolfSSL test\"");
346+
System.out.println("-profile\tSleep for 10 sec before/after running " +
347+
"to allow profilers to attach");
301348
System.exit(1);
302349
}
303350

native/com_wolfssl_WolfSSLSession.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3912,11 +3912,24 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_setVerify
39123912
return;
39133913
}
39143914

3915+
/* Release global reference if already set, before setting again */
3916+
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
3917+
if (appData != NULL) {
3918+
verifyCb = appData->g_verifySSLCbIfaceObj;
3919+
if (verifyCb != NULL) {
3920+
(*jenv)->DeleteGlobalRef(jenv, (jobject)(*verifyCb));
3921+
XFREE(verifyCb, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3922+
verifyCb = NULL;
3923+
appData->g_verifySSLCbIfaceObj = NULL;
3924+
}
3925+
}
3926+
3927+
/* Set verify callback to NULL (reset), or passed in callback */
39153928
if (!callbackIface) {
39163929
wolfSSL_set_verify(ssl, mode, NULL);
39173930
}
39183931
else {
3919-
/* get app data to store verify callback jobject */
3932+
/* Get app data to store verify callback jobject */
39203933
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
39213934
if (appData == NULL) {
39223935
printf("Error getting app data from WOLFSSL\n");

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,12 @@ public synchronized SSLEngineResult wrap(ByteBuffer[] in, int ofst, int len,
606606
*/
607607
if (produced >= 0 &&
608608
(!outBoundOpen || (!inBoundOpen && this.closeNotifySent))) {
609+
/* Mark SSLEngine status as CLOSED */
609610
status = SSLEngineResult.Status.CLOSED;
611+
/* Handshake has finished and SSLEngine is closed, release
612+
* global JNI verify callback pointer */
613+
this.EngineHelper.unsetVerifyCallback();
614+
610615
try {
611616
ClosingConnection();
612617
} catch (SocketException e) {
@@ -962,7 +967,11 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP &&
962967
if (outBoundOpen == false) {
963968
try {
964969
if (ClosingConnection() == WolfSSL.SSL_SUCCESS) {
970+
/* Mark SSLEngine status as CLOSED */
965971
status = SSLEngineResult.Status.CLOSED;
972+
/* Handshake has finished and SSLEngine is closed,
973+
* release, global JNI verify callback pointer */
974+
this.EngineHelper.unsetVerifyCallback();
966975
}
967976
} catch (SocketException e) {
968977
throw new SSLException(e);
@@ -1030,7 +1039,11 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP &&
10301039
}
10311040

10321041
if (outBoundOpen == false || this.closeNotifySent) {
1042+
/* Mark SSLEngine status as CLOSED */
10331043
status = SSLEngineResult.Status.CLOSED;
1044+
/* Handshake has finished and SSLEngine is closed,
1045+
* release, global JNI verify callback pointer */
1046+
this.EngineHelper.unsetVerifyCallback();
10341047
}
10351048

10361049
int err = ssl.getError(ret);
@@ -1773,7 +1786,7 @@ protected synchronized void finalize() throws Throwable {
17731786
this.ssl.freeSSL();
17741787
this.ssl = null;
17751788
}
1776-
EngineHelper = null;
1789+
this.EngineHelper = null;
17771790
super.finalize();
17781791
}
17791792
}

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ public class WolfSSLEngineHelper {
8787
/* Has setUseClientMode() been called on this object */
8888
private boolean modeSet = false;
8989

90+
/* wolfSSL verification mode, set inside setLocalAuth() */
91+
private int verifyMask = WolfSSL.SSL_VERIFY_PEER;
92+
9093
/* Internal Java verify callback, used when user/app is not using
9194
* com.wolfssl.provider.jsse.WolfSSLTrustX509 and instead using their
9295
* own TrustManager to perform verification via checkClientTrusted()
@@ -805,7 +808,7 @@ private void setLocalAuth(SSLSocket socket, SSLEngine engine) {
805808
* Algorithm has been set. To get this callback to be called,
806809
* native wolfSSL should be compiled with the following define:
807810
* WOLFSSL_ALWAYS_VERIFY_CB */
808-
this.ssl.setVerify(mask, wicb);
811+
this.verifyMask = mask;
809812

810813
} else {
811814
/* not our own TrustManager, set up callback so JSSE can use
@@ -814,8 +817,10 @@ private void setLocalAuth(SSLSocket socket, SSLEngine engine) {
814817
"X509TrustManager is not of type WolfSSLTrustX509");
815818
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
816819
"Using checkClientTrusted/ServerTrusted() for verification");
817-
this.ssl.setVerify(WolfSSL.SSL_VERIFY_PEER, wicb);
820+
this.verifyMask = WolfSSL.SSL_VERIFY_PEER;
818821
}
822+
823+
this.ssl.setVerify(this.verifyMask, wicb);
819824
}
820825

821826

@@ -1330,6 +1335,35 @@ else if (peerAddr != null) {
13301335
return ret;
13311336
}
13321337

1338+
/**
1339+
* Unset the native verify callback and reset internal verify
1340+
* callback state.
1341+
*
1342+
* This helper method is called by SSLEngine to reset the native
1343+
* wolfSSL verify callback back to null. Since a pointer to that verify
1344+
* callback is stored as a global JNI variable, it can prevent garbage
1345+
* collection from being done. This helper can be called when an SSLEngine
1346+
* or SSLSocket is closed/done to reset the verify callback.
1347+
*
1348+
* The verify callback will be set again if needed when
1349+
* initHandshake() is called.
1350+
*/
1351+
protected synchronized void unsetVerifyCallback() {
1352+
/* Set native callback to null, releases JNI global and allows for
1353+
* garbage collection if needed */
1354+
if (this.ssl != null) {
1355+
this.ssl.setVerify(this.verifyMask, null);
1356+
}
1357+
1358+
/* Reset internal state of WolfSSLInternalVerifyCallback, removes
1359+
* references to SSLSocket/SSLEngine to allow garbage collection if
1360+
* needed */
1361+
if (this.wicb != null) {
1362+
this.wicb.clearInternalVars();
1363+
this.wicb = null;
1364+
}
1365+
}
1366+
13331367
/**
13341368
* Saves session on connection close for resumption
13351369
*
@@ -1357,6 +1391,7 @@ protected synchronized void finalize() throws Throwable {
13571391
* may be used by wrapper object to WolfSSLEngineHelper and should
13581392
* be freed there */
13591393
this.ssl = null;
1394+
this.wicb = null;
13601395

13611396
this.session = null;
13621397
this.params = null;

0 commit comments

Comments
 (0)