Skip to content

Commit 6acd852

Browse files
Merge pull request #275 from cconlon/jniLockFixes
Fixes to avoid use-after-free issues in WolfSSLSocket
2 parents 3ee581d + 8692585 commit 6acd852

2 files changed

Lines changed: 97 additions & 6 deletions

File tree

native/com_wolfssl_WolfSSLSession.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1673,6 +1673,8 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeSSL
16731673
jobject* g_cachedVerifyCb;
16741674
SSLAppData* appData;
16751675
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
1676+
wolfSSL_Mutex* sessLock = NULL;
1677+
byte lockAvailable = 1;
16761678
(void)jcl;
16771679
#if defined(HAVE_PK_CALLBACKS) && (defined(HAVE_ECC) || !defined(NO_RSA))
16781680
internCtx* pkCtx = NULL;
@@ -1684,9 +1686,41 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeSSL
16841686
return;
16851687
}
16861688

1687-
/* free session mutex lock */
1689+
/* Get and set WOLFSSL app data back to NULL. Try to lock on jniSessLock
1690+
* to prevent race conditions between multiple threads getting
1691+
* and using/freeing app data. */
16881692
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
16891693
if (appData != NULL) {
1694+
if (appData->jniSessLock != NULL) {
1695+
sessLock = appData->jniSessLock;
1696+
if (wc_LockMutex(sessLock) != 0) {
1697+
lockAvailable = 0;
1698+
}
1699+
/* Re-check getting appData after we have mutex */
1700+
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
1701+
if (appData != NULL) {
1702+
/* Set SSLAppData back to NULL inside WOLFSSL struct, if other
1703+
* threads are trying to use it they will fail with error
1704+
* instead of use after free faults. */
1705+
wolfSSL_set_app_data(ssl, NULL);
1706+
}
1707+
if (lockAvailable) {
1708+
/* Unlock on saved mutex, in case appData is now NULL */
1709+
wc_UnLockMutex(sessLock);
1710+
sessLock = NULL;
1711+
}
1712+
} else {
1713+
/* If no jniSessLock available, fall back to just
1714+
* setting app data to NULL in WOLFSSL. We should always have
1715+
* appData->jniSessLock though. */
1716+
wolfSSL_set_app_data(ssl, NULL);
1717+
}
1718+
}
1719+
1720+
/* appData may have been refreshed/NULL retrieved above inside
1721+
* mutex lock, check again against NULL before continuing freeing. */
1722+
if (appData != NULL) {
1723+
/* free session mutex lock */
16901724
if (appData->jniSessLock != NULL) {
16911725
wc_FreeMutex(appData->jniSessLock);
16921726
XFREE(appData->jniSessLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);

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

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,6 +1967,30 @@ public synchronized boolean sessionResumed() throws SSLException {
19671967
return false;
19681968
}
19691969

1970+
/**
1971+
* Internal private method to check if WolfSSLInputStream
1972+
* and WolfSSLOutputStream are closed.
1973+
*
1974+
* @return true if both streams are closed (or were not created/null),
1975+
* otherwise false if they were created and are still active.
1976+
*/
1977+
private boolean ioStreamsAreClosed() {
1978+
1979+
if (this.inStream == null && this.outStream == null) {
1980+
return true;
1981+
}
1982+
1983+
if (this.inStream != null && !this.inStream.isClosed()) {
1984+
return false;
1985+
}
1986+
1987+
if (this.outStream != null && !this.outStream.isClosed()) {
1988+
return false;
1989+
}
1990+
1991+
return true;
1992+
}
1993+
19701994
/**
19711995
* Closes this SSLSocket.
19721996
*
@@ -2132,13 +2156,22 @@ public synchronized void close() throws IOException {
21322156

21332157
/* Connection is closed, free native WOLFSSL session
21342158
* to release native memory earlier than garbage
2135-
* collector might with finalize(), if we don't
2136-
* have any threads still waiting in poll/select. */
2137-
if (this.ssl.getThreadsBlockedInPoll() == 0) {
2159+
* collector might with finalize(), Don't free if we
2160+
* have threads still waiting in poll/select or if
2161+
* our WolfSSLInputStream or WolfSSLOutputStream are
2162+
* still open. */
2163+
if (this.ssl.getThreadsBlockedInPoll() == 0 &&
2164+
ioStreamsAreClosed()) {
21382165
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
21392166
() -> "calling this.ssl.freeSSL()");
21402167
this.ssl.freeSSL();
21412168
this.ssl = null;
2169+
} else {
2170+
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
2171+
() -> "deferring freeing this.ssl, threads " +
2172+
"blocked in poll: " +
2173+
this.ssl.getThreadsBlockedInPoll() +
2174+
", or streams not closed");
21422175
}
21432176

21442177
/* Reset internal WolfSSLEngineHelper to null */
@@ -2517,7 +2550,7 @@ class WolfSSLInputStream extends InputStream {
25172550

25182551
private WolfSSLSession ssl;
25192552
private WolfSSLSocket socket;
2520-
private boolean isClosed = true;
2553+
private volatile boolean isClosed = true;
25212554

25222555
/* Atomic boolean to indicate if this InputStream has started to
25232556
* close. Protects against deadlock between two threads calling
@@ -2530,6 +2563,18 @@ public WolfSSLInputStream(WolfSSLSession ssl, WolfSSLSocket socket) {
25302563
this.isClosed = false;
25312564
}
25322565

2566+
/**
2567+
* Non standard method to check if this InputStream has been
2568+
* closed. This is used by WolfSSLSocket to check if the associated
2569+
* WolfSSLInputStream has been closed before calling freeSSL()
2570+
* internally.
2571+
*
2572+
* @return true if this InputStream has been closed, otherwise false
2573+
*/
2574+
public boolean isClosed() {
2575+
return this.isClosed;
2576+
}
2577+
25332578
/**
25342579
* Close InputStream, but gives caller option to close underlying
25352580
* Socket or not.
@@ -2741,7 +2786,7 @@ class WolfSSLOutputStream extends OutputStream {
27412786

27422787
private WolfSSLSession ssl;
27432788
private WolfSSLSocket socket;
2744-
private boolean isClosed = true;
2789+
private volatile boolean isClosed = true;
27452790

27462791
/* Atomic boolean to indicate if this InputStream has started to
27472792
* close. Protects against deadlock between two threads calling
@@ -2754,6 +2799,18 @@ public WolfSSLOutputStream(WolfSSLSession ssl, WolfSSLSocket socket) {
27542799
this.isClosed = false;
27552800
}
27562801

2802+
/**
2803+
* Non standard method to check if this OutputStream has been
2804+
* closed. This is used by WolfSSLSocket to check if the associated
2805+
* WolfSSLOutputStream has been closed before calling freeSSL()
2806+
* internally.
2807+
*
2808+
* @return true if this OutputStream has been closed, otherwise false
2809+
*/
2810+
public boolean isClosed() {
2811+
return this.isClosed;
2812+
}
2813+
27572814
/**
27582815
* Close OutputStream, but gives caller option to close underlying
27592816
* Socket or not.

0 commit comments

Comments
 (0)