282732 read/write/accept/close thread protection

This commit is contained in:
glen.beasley%sun.com 2005-04-05 20:41:46 +00:00
parent be752e9b17
commit a7873159bf
8 changed files with 383 additions and 163 deletions

View File

@ -1,7 +1,7 @@
;+#
;+# ***** BEGIN LICENSE BLOCK *****
;+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
;+#
LIBRARY jss4 ;-
;+# The contents of this file are subject to the Mozilla Public License Version
;+# 1.1 (the "License"); you may not use this file except in compliance with
;+# the License. You may obtain a copy of the License at
@ -50,7 +50,7 @@
;+JSS_3.0 { # JSS 3.0 release
;+ global:
LIBRARY jss3 ;-
LIBRARY jss4 ;-
EXPORTS ;-
Java_org_mozilla_jss_crypto_EncryptionAlgorithm_getIVLength;
Java_org_mozilla_jss_crypto_PQGParams_generateNative__I;
@ -284,3 +284,10 @@ Java_org_mozilla_jss_SecretDecoderRing_KeyManager_lookupUniqueNamedKeyNative;
;+ local:
;+ *;
;+};
;+JSS_4.1 { # JSS 4.1 release
;+ global:
Java_org_mozilla_jss_ssl_SSLSocket_abortReadWrite;
Java_org_mozilla_jss_ssl_SSLServerSocket_abortAccept;
;+ local:
;+ *;
;+};

View File

@ -81,6 +81,7 @@ Java_org_mozilla_jss_ssl_SSLServerSocket_socketAccept
JSSL_SocketData *newSD=NULL;
jbyteArray sdArray = NULL;
SECStatus status;
PRThread *me;
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish;
@ -96,41 +97,37 @@ Java_org_mozilla_jss_ssl_SSLServerSocket_socketAccept
}
}
for(;;) {
newFD = PR_Accept(sock->fd, &addr, ivtimeout);
/* Set the current thread doing the accept. */
me = PR_GetCurrentThread();
PR_Lock(sock->lock);
PR_ASSERT(sock->accepter == NULL);
sock->accepter = me;
PR_Unlock(sock->lock);
if( newFD != NULL ) {
/* success! */
break;
} else {
switch( PR_GetError() ) {
case PR_PENDING_INTERRUPT_ERROR:
case PR_IO_PENDING_ERROR:
break; /* out of the switch and loop again */
newFD = PR_Accept(sock->fd, &addr, ivtimeout);
PR_Lock(sock->lock);
PR_ASSERT(sock->accepter == me);
sock->accepter = NULL;
PR_Unlock(sock->lock);
if( newFD == NULL ) {
#ifdef WINNT
case PR_IO_TIMEOUT_ERROR:
/*
* if timeout was set, and the PR_Accept() timed out,
* then cancel the I/O on the port, otherwise PR_Accept()
* will always return PR_IO_PENDING_ERROR on subsequent
* calls
*/
PR_NT_CancelIo(sock->fd);
/* don't break here, let it fall through */
#endif
default:
JSSL_throwSSLSocketException(env,
"Failed to accept new connection");
goto finish;
}
PRErrorCode err = PR_GetError();
if( err == PR_PENDING_INTERRUPT_ERROR ||
err == PR_IO_TIMEOUT_ERROR ) {
PR_NT_CancelIo(sock->fd);
}
#endif
JSSL_throwSSLSocketException(env,
"Failed to accept new connection");
goto finish;
}
newSD = JSSL_CreateSocketData(env, newSock, newFD, NULL /* priv */);
newFD = NULL;
if( newSD == NULL ) {
goto finish;
}
newFD = NULL;
/* setup the handshake callback */
status = SSL_HandshakeCallback(newSD->fd, JSSL_HandshakeCallback,
@ -138,6 +135,7 @@ Java_org_mozilla_jss_ssl_SSLServerSocket_socketAccept
if( status != SECSuccess ) {
JSSL_throwSSLSocketException(env,
"Unable to install handshake callback");
goto finish;
}
/* pass the pointer back to Java */
@ -159,6 +157,30 @@ finish:
return sdArray;
}
JNIEXPORT void JNICALL
Java_org_mozilla_jss_ssl_SSLServerSocket_abortAccept(
JNIEnv *env, jobject self)
{
JSSL_SocketData *sock = NULL;
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish;
/*
* The java layer prevents I/O once close has been
* called but if an accept is in progress then abort it.
* For WINNT the accept method must check for
* PR_PENDING_INTERRUPT_ERROR and call PR_NT_CancelIo.
*/
PR_Lock(sock->lock);
if ( sock->accepter ) {
PR_Interrupt(sock->accepter);
}
PR_Unlock(sock->lock);
finish:
EXCEPTION_CHECK(env, sock)
return;
}
JNIEXPORT void JNICALL
Java_org_mozilla_jss_ssl_SSLServerSocket_clearSessionCache(
JNIEnv *env, jclass clazz)

View File

@ -49,6 +49,22 @@ import org.mozilla.jss.crypto.TokenException;
*/
public class SSLServerSocket extends java.net.ServerSocket {
/*
* Locking rules of SSLServerSocket
*
* isClosed and inAccept must be accessed with the object locked.
*
* acceptLock must be locked throughout the accept method. It is
* used to serialize accept calls on the object.
*/
private SocketProxy sockProxy = null;
private boolean handshakeAsClient = false;
private SocketBase base = new SocketBase();
private boolean isClosed = false;
private boolean inAccept = false;
private java.lang.Object acceptLock = new java.lang.Object();
/**
* The default size of the listen queue.
*/
@ -140,10 +156,6 @@ public class SSLServerSocket extends java.net.ServerSocket {
socketListen(backlog);
}
private SocketProxy sockProxy;
private boolean handshakeAsClient=false;
private SocketBase base = new SocketBase();
private native void socketListen(int backlog) throws SocketException;
private static InetAddress anyLocalAddr;
@ -158,14 +170,37 @@ public class SSLServerSocket extends java.net.ServerSocket {
* or the timeout is reached.
*/
public Socket accept() throws IOException {
SSLSocket s = new SSLSocket();
/* socketAccept can throw an exception for timeouts or IO errors */
/* so first get a socket pointer, and if successful create the SocketProxy */
byte[] socketPointer = null;
socketPointer = socketAccept(s, base.getTimeout(), handshakeAsClient);
SocketProxy sp = new SocketProxy(socketPointer );
s.setSockProxy(sp);
return s;
synchronized (acceptLock) {
synchronized (this) {
if (isClosed) {
throw new IOException(
"SSLServerSocket has been closed, and cannot be reused.");
}
inAccept = true;
}
SSLSocket s = new SSLSocket();
try {
/*
* socketAccept can throw an exception for timeouts,
* IO errors, or PR_Interrupt called by abortAccept.
* So first get a socket pointer, and if successful
* create the SocketProxy.
*/
byte[] socketPointer = null;
socketPointer = socketAccept(s, base.getTimeout(),
handshakeAsClient);
SocketProxy sp = new SocketProxy(socketPointer);
s.setSockProxy(sp);
} catch (Exception e) {
/* unnessary to do a s.close() since exception thrown*/
throw new IOException("accept method failed");
} finally {
synchronized (this) {
inAccept=false;
}
}
return s;
}
}
/**
@ -186,7 +221,7 @@ public class SSLServerSocket extends java.net.ServerSocket {
public native void setReuseAddress(boolean reuse) throws SocketException;
public native boolean getReuseAddress() throws SocketException;
private native void abortAccept() throws SocketException;
private native byte[] socketAccept(SSLSocket s, int timeout,
boolean handshakeAsClient) throws SocketException;
@ -195,7 +230,9 @@ public class SSLServerSocket extends java.net.ServerSocket {
*/
public static native void clearSessionCache();
protected void finalize() throws Throwable { }
protected void finalize() throws Throwable {
close(); /* in case user never called close */
}
/**
@ -209,7 +246,26 @@ public class SSLServerSocket extends java.net.ServerSocket {
* Closes this socket.
*/
public void close() throws IOException {
base.close();
synchronized (this) {
if( isClosed ) {
/* finalize calls close or user calls close more than once */
return;
}
isClosed = true;
if( sockProxy == null ) {
/* nothing to do */
return;
}
if( inAccept ) {
abortAccept();
}
}
/* Lock acceptLock to ensure that accept has been aborted. */
synchronized (acceptLock) {
base.close();
sockProxy = null;
base.setProxy(null);
}
}
// This directory is used as the default for the Session ID cache

View File

@ -39,7 +39,6 @@
#include <ssl.h>
#include <sslerr.h>
#include <stdio.h>
#include <jssutil.h>
#include <jss_exceptions.h>
#include <java_ids.h>
@ -686,13 +685,9 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketRead(JNIEnv *env, jobject self,
jbyte *buf = NULL;
jint size;
PRIntervalTime ivtimeout;
PRThread *me;
jint nread;
/* get the socket */
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
goto finish;
}
size = (*env)->GetArrayLength(env, bufBA);
if( off < 0 || len < 0 || (off+len) > size) {
JSS_throw(env, INDEX_OUT_OF_BOUNDS_EXCEPTION);
@ -707,42 +702,49 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketRead(JNIEnv *env, jobject self,
ivtimeout = (timeout > 0) ? PR_MillisecondsToInterval(timeout)
: PR_INTERVAL_NO_TIMEOUT;
for(;;) {
nread = PR_Recv(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
/* get the socket */
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
goto finish;
}
if( nread >= 0 ) {
/* either we read some bytes, or we hit EOF. Either way, we're
done */
break;
} else {
/* some error, but is it recoverable? */
PRErrorCode err = PR_GetError();
/* set the current thread doing the read */
me = PR_GetCurrentThread();
PR_Lock(sock->lock);
PR_ASSERT(sock->reader == NULL);
sock->reader = me;
PR_Unlock(sock->lock);
nread = PR_Recv(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
if( err == PR_PENDING_INTERRUPT_ERROR ||
err == PR_IO_PENDING_ERROR )
{
/* just try again */
} else {
PR_Lock(sock->lock);
PR_ASSERT(sock->reader == me);
sock->reader = NULL;
PR_Unlock(sock->lock);
if( nread < 0 ) {
PRErrorCode err = PR_GetError();
if( err == PR_PENDING_INTERRUPT_ERROR ) {
#ifdef WINNT
if (err == PR_IO_TIMEOUT_ERROR ) {
/*
* if timeout was set, and the PR_Accept() timed out,
* then cancel the I/O on the port, otherwise PR_Accept()
* will always return PR_IO_PENDING_ERROR on subsequent
* calls
*/
PR_NT_CancelIo(sock->fd);
JSSL_throwSSLSocketException(env, "Operation timed out");
goto finish;
}
/* Clean up after PR_interrupt called by abortReadWrite. */
PR_NT_CancelIo(sock->fd);
#endif
/* unrecoverable error */
JSSL_throwSSLSocketException(env,
"Error reading from socket");
goto finish;
}
JSSL_throwSSLSocketException(env, "Read operation interrupted");
} else if( err == PR_IO_TIMEOUT_ERROR ) {
#ifdef WINNT
/*
* if timeout was set, and the PR_Recv timed out,
* then cancel the I/O on the socket, otherwise PR_Recv()
* will always return PR_IO_PENDING_ERROR on subsequent
* calls
*/
PR_NT_CancelIo(sock->fd);
#endif
JSSL_throwSSLSocketException(env, "Operation timed out");
} else {
JSSL_throwSSLSocketException(env, "Error reading from socket");
}
goto finish;
}
if( nread == 0 ) {
@ -784,6 +786,8 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketWrite(JNIEnv *env, jobject self,
jbyte *buf = NULL;
jint size;
PRIntervalTime ivtimeout;
PRThread *me;
PRInt32 numwrit;
if( bufBA == NULL ) {
JSS_throw(env, NULL_POINTER_EXCEPTION);
@ -796,10 +800,6 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketWrite(JNIEnv *env, jobject self,
goto finish;
}
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
goto finish;
}
buf = (*env)->GetByteArrayElements(env, bufBA, NULL);
if( buf == NULL ) {
goto finish;
@ -808,40 +808,50 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketWrite(JNIEnv *env, jobject self,
ivtimeout = (timeout > 0) ? PR_MillisecondsToInterval(timeout)
: PR_INTERVAL_NO_TIMEOUT;
for(;;) {
PRInt32 numwrit;
numwrit = PR_Send(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
if( numwrit < 0 ) {
PRErrorCode err = PR_GetError();
if( err == PR_PENDING_INTERRUPT_ERROR ||
err == PR_IO_PENDING_ERROR)
{
/* just try again */
} else if( err == PR_IO_TIMEOUT_ERROR ) {
#ifdef WINNT
/*
* if timeout was set, and the PR_Accept() timed out,
* then cancel the I/O on the port, otherwise PR_Accept()
* will always return PR_IO_PENDING_ERROR on subsequent
* calls
*/
PR_NT_CancelIo(sock->fd);
#endif
JSSL_throwSSLSocketException(env, "Operation timed out");
goto finish;
} else {
JSSL_throwSSLSocketException(env,
"Failed to write to socket");
goto finish;
}
} else {
/* PR_Send is supposed to block until it sends everything */
PR_ASSERT(numwrit == len);
break;
}
/* get the socket */
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
goto finish;
}
/* set the current thread doing the write */
me = PR_GetCurrentThread();
PR_Lock(sock->lock);
PR_ASSERT(sock->writer == NULL);
sock->writer = me;
PR_Unlock(sock->lock);
numwrit = PR_Send(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
PR_Lock(sock->lock);
PR_ASSERT(sock->writer == me);
sock->writer = NULL;
PR_Unlock(sock->lock);
if( numwrit < 0 ) {
PRErrorCode err = PR_GetError();
if( err == PR_PENDING_INTERRUPT_ERROR ) {
#ifdef WINNT
/* clean up after PR_Interrupt called by abortReadWrite. */
PR_NT_CancelIo(sock->fd);
#endif
JSSL_throwSSLSocketException(env, "Write operation interrupted");
} else if( err == PR_IO_TIMEOUT_ERROR ) {
#ifdef WINNT
/*
* if timeout was set, and the PR_Send() timed out,
* then cancel the I/O on the socket, otherwise PR_Send()
* will always return PR_IO_PENDING_ERROR on subsequent
* calls
*/
PR_NT_CancelIo(sock->fd);
#endif
JSSL_throwSSLSocketException(env, "Operation timed out");
} else {
JSSL_throwSSLSocketException(env, "Failed to write to socket");
}
goto finish;
}
/* PR_Send is supposed to block until it sends everything */
PR_ASSERT(numwrit == len);
finish:
if( buf != NULL ) {
@ -850,6 +860,33 @@ finish:
EXCEPTION_CHECK(env, sock)
}
JNIEXPORT void JNICALL
Java_org_mozilla_jss_ssl_SSLSocket_abortReadWrite(
JNIEnv *env, jobject self)
{
JSSL_SocketData *sock = NULL;
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) goto finish;
/*
* The java layer prevents I/O once close has been
* called but if an I/O operation is in progress then abort it.
* For WINNT the read and write methods must check for the
* PR_PENDING_INTERRUPT_ERROR and call PR_NT_CancelIo.
*/
PR_Lock(sock->lock);
if ( sock->reader ) {
PR_Interrupt(sock->reader);
}
if ( sock->writer ) {
PR_Interrupt(sock->writer);
}
PR_Unlock(sock->lock);
finish:
EXCEPTION_CHECK(env, sock)
return;
}
JNIEXPORT void JNICALL
Java_org_mozilla_jss_ssl_SSLSocket_shutdownNative(
JNIEnv *env, jobject self, jint how)

View File

@ -49,6 +49,31 @@ import java.security.AccessController;
*/
public class SSLSocket extends java.net.Socket {
/*
* Locking strategy of SSLSocket
*
* isClosed, inRead, and inWrite must be accessed with the object
* locked.
*
* readLock must be locked throughout the read method. It is used
* to serialize read calls.
*
* writeLock must be locked throughout the write method. It is used
* to serialize write calls.
*/
private java.lang.Object readLock = new java.lang.Object();
private java.lang.Object writeLock = new java.lang.Object();
private boolean isClosed = false;
private boolean inRead = false;
private boolean inWrite = false;
private InetAddress inetAddress;
private int port;
private SocketProxy sockProxy = null;
private boolean open = false;
private boolean handshakeAsClient = true;
private SocketBase base = new SocketBase();
/**
* For sockets that get created by accept().
*/
@ -338,7 +363,7 @@ public class SSLSocket extends java.net.Socket {
}
private native void shutdownNative(int how) throws IOException;
private native void abortReadWrite() throws IOException;
/**
* Sets the SO_LINGER socket option.
* param linger The time (in seconds) to linger for.
@ -391,7 +416,36 @@ public class SSLSocket extends java.net.Socket {
* Closes this socket.
*/
public void close() throws IOException {
base.close();
synchronized (this) {
if( isClosed ) {
/* finalize calls close or user calls close more than once */
return;
}
isClosed = true;
if( sockProxy == null ) {
/* nothing to do */
return;
}
/*
* If a read or write is occuring, abort the I/O. Any
* further attempts to read/write will fail since isClosed
* is true
*/
if ( inRead || inWrite ) {
abortReadWrite();
}
}
/*
* Lock readLock and writeLock to ensure that read and write
* have been aborted.
*/
synchronized (readLock) {
synchronized (writeLock) {
base.close();
sockProxy = null;
base.setProxy(null);
}
}
}
private native void socketConnect(byte[] addr, String hostname, int port)
@ -620,12 +674,6 @@ public class SSLSocket extends java.net.Socket {
setSSLDefaultOption(SocketBase.SSL_NO_CACHE, !b);
}
private InetAddress inetAddress;
private int port;
private SocketProxy sockProxy;
private boolean open = false;
private boolean handshakeAsClient=true;
private SocketBase base = new SocketBase();
private static void setSSLDefaultOption(int option, boolean on)
throws SocketException
@ -663,11 +711,49 @@ public class SSLSocket extends java.net.Socket {
throws IOException;
int read(byte[] b, int off, int len) throws IOException {
return socketRead(b, off, len, base.getTimeout());
synchronized (readLock) {
synchronized (this) {
if ( isClosed ) { /* abort read if socket is closed */
throw new IOException(
"Socket has been closed, and cannot be reused.");
}
inRead = true;
}
int iRet;
try {
iRet = socketRead(b, off, len, base.getTimeout());
} catch (IOException ioe) {
throw new IOException(
"SocketException cannot read on socket");
} finally {
synchronized (this) {
inRead = false;
}
}
return iRet;
}
}
void write(byte[] b, int off, int len) throws IOException {
socketWrite(b, off, len, base.getTimeout());
synchronized (writeLock) {
synchronized (this) {
if ( isClosed ) { /* abort write if socket is closed */
throw new IOException(
"Socket has been closed, and cannot be reused.");
}
inWrite = true;
}
try {
socketWrite(b, off, len, base.getTimeout());
} catch (IOException ioe) {
throw new IOException(
"SocketException cannot write on socket");
} finally {
synchronized (this) {
inWrite = false;
}
}
}
}
private native int socketRead(byte[] b, int off, int len, int timeout)
@ -701,7 +787,9 @@ public class SSLSocket extends java.net.Socket {
*/
public native void redoHandshake(boolean flushCache) throws SocketException;
protected void finalize() throws Throwable { }
protected void finalize() throws Throwable {
close(); /* in case user did not call close */
}
public static class CipherPolicy {
private int _enum;

View File

@ -107,9 +107,10 @@ class SocketBase {
socketClose();
}
// This method is synchronized because there is a potential race
// condition in the native code.
native synchronized void socketClose() throws IOException;
// SSLServerSocket and SSLSocket close methods
// have their own synchronization control that
// protects SocketBase.socketClose.
native void socketClose() throws IOException;
private boolean requestingClientAuth = false;

View File

@ -149,9 +149,10 @@ Java_org_mozilla_jss_ssl_SocketBase_socketCreate(JNIEnv *env, jobject self,
jobject clientCertSelectionCallback, jobject javaSock, jstring host)
{
jbyteArray sdArray = NULL;
JSSL_SocketData *sockdata;
JSSL_SocketData *sockdata = NULL;
SECStatus status;
PRFileDesc *newFD;
PRFileDesc *tmpFD;
PRFilePrivate *priv = NULL;
if( javaSock == NULL ) {
@ -173,16 +174,18 @@ Java_org_mozilla_jss_ssl_SocketBase_socketCreate(JNIEnv *env, jobject self,
}
/* enable SSL on the socket */
newFD = SSL_ImportFD(NULL, newFD);
if( newFD == NULL ) {
tmpFD = SSL_ImportFD(NULL, newFD);
if( tmpFD == NULL ) {
JSSL_throwSSLSocketException(env, "SSL_ImportFD() returned NULL");
goto finish;
}
newFD = tmpFD;
sockdata = JSSL_CreateSocketData(env, sockObj, newFD, priv);
if( sockdata == NULL ) {
goto finish;
}
newFD = NULL;
if( host != NULL ) {
const char *chars;
@ -266,6 +269,9 @@ finish:
if( sockdata != NULL ) {
JSSL_DestroySocketData(env, sockdata);
}
if( newFD != NULL ) {
PR_Close(newFD);
}
} else {
PR_ASSERT( sdArray != NULL );
}
@ -280,6 +286,10 @@ JSSL_CreateSocketData(JNIEnv *env, jobject sockObj, PRFileDesc* newFD,
/* make a JSSL_SocketData structure */
sockdata = PR_Malloc( sizeof(JSSL_SocketData) );
if( sockdata == NULL ) {
JSS_throw(env, OUT_OF_MEMORY_ERROR);
goto finish;
}
sockdata->fd = newFD;
sockdata->socketObject = NULL;
sockdata->certApprovalCallback = NULL;
@ -287,7 +297,16 @@ JSSL_CreateSocketData(JNIEnv *env, jobject sockObj, PRFileDesc* newFD,
sockdata->clientCert = NULL;
sockdata->clientCertSlot = NULL;
sockdata->jsockPriv = priv;
sockdata->closed = PR_FALSE;
sockdata->lock = NULL;
sockdata->reader = NULL;
sockdata->writer = NULL;
sockdata->accepter = NULL;
sockdata->lock = PR_NewLock();
if( sockdata->lock == NULL ) {
JSS_throw(env, OUT_OF_MEMORY_ERROR);
goto finish;
}
/*
* Make a global ref to the socket. Since it is a weak reference, it will
@ -303,8 +322,6 @@ finish:
if( sockdata != NULL ) {
JSSL_DestroySocketData(env, sockdata);
sockdata = NULL;
} else {
PR_ASSERT( sockdata != NULL );
}
}
return sockdata;
@ -314,18 +331,13 @@ JNIEXPORT void JNICALL
Java_org_mozilla_jss_ssl_SocketProxy_releaseNativeResources
(JNIEnv *env, jobject this)
{
JSSL_SocketData *sock = NULL;
/* get the FD */
if( JSS_getPtrFromProxy(env, this, (void**)&sock) != PR_SUCCESS) {
/* exception was thrown */
goto finish;
}
JSSL_DestroySocketData(env, sock);
finish:
return;
/* SSLSocket.close and SSLServerSocket.close call */
/* SocketBase.close to destroy all native Resources */
/* attached to the socket. There is no native resource */
/* to release after close has been called. This method */
/* remains because SocketProxy extends org.mozilla.jss.util.NativeProxy*/
/* which defines releaseNativeResources as abstract and */
/* therefore must be implemented by SocketProxy */
}
void
@ -333,11 +345,7 @@ JSSL_DestroySocketData(JNIEnv *env, JSSL_SocketData *sd)
{
PR_ASSERT(sd != NULL);
if( !sd->closed ) {
PR_Close(sd->fd);
sd->closed = PR_TRUE;
/* this may have thrown an exception */
}
PR_Close(sd->fd);
if( sd->socketObject != NULL ) {
DELETE_WEAK_GLOBAL_REF(env, sd->socketObject );
@ -354,6 +362,9 @@ JSSL_DestroySocketData(JNIEnv *env, JSSL_SocketData *sd)
if( sd->clientCertSlot != NULL ) {
PK11_FreeSlot(sd->clientCertSlot);
}
if( sd->lock != NULL ) {
PR_DestroyLock(sd->lock);
}
PR_Free(sd);
}
@ -425,9 +436,8 @@ finish:
}
/*
* This method is synchronized because of a potential race condition.
* We want to avoid two threads simultaneously calling this code, in case
* one sets sd->fd to NULL and then the other calls PR_Close on the NULL.
* SSLServerSocket and SSLSocket have their own synchronization
* that protects SocketBase.socketClose.
*/
JNIEXPORT void JNICALL
Java_org_mozilla_jss_ssl_SocketBase_socketClose(JNIEnv *env, jobject self)
@ -440,11 +450,7 @@ Java_org_mozilla_jss_ssl_SocketBase_socketClose(JNIEnv *env, jobject self)
goto finish;
}
if( ! sock->closed ) {
PR_Close(sock->fd);
sock->closed = PR_TRUE;
/* this may have thrown an exception */
}
JSSL_DestroySocketData(env, sock);
finish:
EXCEPTION_CHECK(env, sock)

View File

@ -45,7 +45,10 @@ struct JSSL_SocketData {
CERTCertificate *clientCert;
PK11SlotInfo *clientCertSlot;
PRFilePrivate *jsockPriv;
PRBool closed;
PRLock *lock; /* protects reader, writer, and accepter */
PRThread *reader;
PRThread *writer;
PRThread *accepter;
};
typedef struct JSSL_SocketData JSSL_SocketData;