Guard against possible segfault when OpenSslContext is gc'ed and user still hold reference to OpenSslSessionContext / OpenSslSessionStats

Motivation:

When the OpenSslContext is gc'ed and the user still hold a reference to OpenSslSessionContext / OpenSslSessionStats it is possible to produce a segfault when calling
a method on any of these that tries to pass down the ctx pointer to the native methods. This is because the OpenSslContext finalizer will free the native pointer.

Modifications:

Change OpenSslSessionContext / OpenSslSessionContext to store a reference to OpenSslContext and so prevent the GC to collect it as long as the user has a reference to OpenSslSessionContext / OpenSslSessionContext.

Result:

No more sefault possible.
This commit is contained in:
Norman Maurer 2016-06-13 11:34:14 +02:00
parent 4a1e0ceb4d
commit 65d1fb474d
6 changed files with 41 additions and 30 deletions

View File

@ -264,7 +264,7 @@ public final class OpenSslClientContext extends OpenSslContext {
throw new SSLException("unable to setup trustmanager", e); throw new SSLException("unable to setup trustmanager", e);
} }
} }
sessionContext = new OpenSslClientSessionContext(ctx); sessionContext = new OpenSslClientSessionContext(this);
success = true; success = true;
} finally { } finally {
if (!success) { if (!success) {
@ -280,7 +280,7 @@ public final class OpenSslClientContext extends OpenSslContext {
// No cache is currently supported for client side mode. // No cache is currently supported for client side mode.
private static final class OpenSslClientSessionContext extends OpenSslSessionContext { private static final class OpenSslClientSessionContext extends OpenSslSessionContext {
private OpenSslClientSessionContext(long context) { private OpenSslClientSessionContext(OpenSslContext context) {
super(context); super(context);
} }

View File

@ -365,7 +365,10 @@ public abstract class OpenSslContext extends SslContext {
return ctx; return ctx;
} }
protected final void destroy() { // IMPORTANT: This method must only be called from either the constructor or the finalizer as a user MUST never
// get access to an OpenSslSessionContext after this method was called to prevent the user from
// producing a segfault.
final void destroy() {
synchronized (OpenSslContext.class) { synchronized (OpenSslContext.class) {
if (ctx != 0) { if (ctx != 0) {
SSLContext.free(ctx); SSLContext.free(ctx);

View File

@ -433,7 +433,7 @@ public final class OpenSslServerContext extends OpenSslContext {
throw new SSLException("unable to setup trustmanager", e); throw new SSLException("unable to setup trustmanager", e);
} }
} }
sessionContext = new OpenSslServerSessionContext(ctx); sessionContext = new OpenSslServerSessionContext(this);
sessionContext.setSessionIdContext(ID); sessionContext.setSessionIdContext(ID);
success = true; success = true;
} finally { } finally {

View File

@ -23,7 +23,7 @@ import org.apache.tomcat.jni.SSLContext;
* {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side. * {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side.
*/ */
public final class OpenSslServerSessionContext extends OpenSslSessionContext { public final class OpenSslServerSessionContext extends OpenSslSessionContext {
OpenSslServerSessionContext(long context) { OpenSslServerSessionContext(OpenSslContext context) {
super(context); super(context);
} }
@ -32,12 +32,12 @@ public final class OpenSslServerSessionContext extends OpenSslSessionContext {
if (seconds < 0) { if (seconds < 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
SSLContext.setSessionCacheTimeout(context, seconds); SSLContext.setSessionCacheTimeout(context.ctx, seconds);
} }
@Override @Override
public int getSessionTimeout() { public int getSessionTimeout() {
return (int) SSLContext.getSessionCacheTimeout(context); return (int) SSLContext.getSessionCacheTimeout(context.ctx);
} }
@Override @Override
@ -45,23 +45,23 @@ public final class OpenSslServerSessionContext extends OpenSslSessionContext {
if (size < 0) { if (size < 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
SSLContext.setSessionCacheSize(context, size); SSLContext.setSessionCacheSize(context.ctx, size);
} }
@Override @Override
public int getSessionCacheSize() { public int getSessionCacheSize() {
return (int) SSLContext.getSessionCacheSize(context); return (int) SSLContext.getSessionCacheSize(context.ctx);
} }
@Override @Override
public void setSessionCacheEnabled(boolean enabled) { public void setSessionCacheEnabled(boolean enabled) {
long mode = enabled ? SSL.SSL_SESS_CACHE_SERVER : SSL.SSL_SESS_CACHE_OFF; long mode = enabled ? SSL.SSL_SESS_CACHE_SERVER : SSL.SSL_SESS_CACHE_OFF;
SSLContext.setSessionCacheMode(context, mode); SSLContext.setSessionCacheMode(context.ctx, mode);
} }
@Override @Override
public boolean isSessionCacheEnabled() { public boolean isSessionCacheEnabled() {
return SSLContext.getSessionCacheMode(context) == SSL.SSL_SESS_CACHE_SERVER; return SSLContext.getSessionCacheMode(context.ctx) == SSL.SSL_SESS_CACHE_SERVER;
} }
/** /**
@ -74,6 +74,6 @@ public final class OpenSslServerSessionContext extends OpenSslSessionContext {
* @return {@code true} if success, {@code false} otherwise. * @return {@code true} if success, {@code false} otherwise.
*/ */
public boolean setSessionIdContext(byte[] sidCtx) { public boolean setSessionIdContext(byte[] sidCtx) {
return SSLContext.setSessionIdContext(context, sidCtx); return SSLContext.setSessionIdContext(context.ctx, sidCtx);
} }
} }

View File

@ -30,9 +30,13 @@ public abstract class OpenSslSessionContext implements SSLSessionContext {
private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration(); private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
private final OpenSslSessionStats stats; private final OpenSslSessionStats stats;
final long context; final OpenSslContext context;
OpenSslSessionContext(long context) { // IMPORTANT: We take the OpenSslContext and not just the long (which points the native instance) to prevent
// the GC to collect OpenSslContext as this would also free the pointer and so could result in a
// segfault when the user calls any of the methods here that try to pass the pointer down to the native
// level.
OpenSslSessionContext(OpenSslContext context) {
this.context = context; this.context = context;
stats = new OpenSslSessionStats(context); stats = new OpenSslSessionStats(context);
} }
@ -59,7 +63,7 @@ public abstract class OpenSslSessionContext implements SSLSessionContext {
if (keys == null) { if (keys == null) {
throw new NullPointerException("keys"); throw new NullPointerException("keys");
} }
SSLContext.setSessionTicketKeys(context, keys); SSLContext.setSessionTicketKeys(context.ctx, keys);
} }
/** /**
@ -73,7 +77,7 @@ public abstract class OpenSslSessionContext implements SSLSessionContext {
for (int i = 0; i < ticketKeys.length; i++) { for (int i = 0; i < ticketKeys.length; i++) {
ticketKeys[i] = keys[i].key; ticketKeys[i] = keys[i].key;
} }
SSLContext.setSessionTicketKeys(context, ticketKeys); SSLContext.setSessionTicketKeys(context.ctx, ticketKeys);
} }
/** /**

View File

@ -25,9 +25,13 @@ import org.apache.tomcat.jni.SSLContext;
*/ */
public final class OpenSslSessionStats { public final class OpenSslSessionStats {
private final long context; private final OpenSslContext context;
OpenSslSessionStats(long context) { // IMPORTANT: We take the OpenSslContext and not just the long (which points the native instance) to prevent
// the GC to collect OpenSslContext as this would also free the pointer and so could result in a
// segfault when the user calls any of the methods here that try to pass the pointer down to the native
// level.
OpenSslSessionStats(OpenSslContext context) {
this.context = context; this.context = context;
} }
@ -35,49 +39,49 @@ public final class OpenSslSessionStats {
* Returns the current number of sessions in the internal session cache. * Returns the current number of sessions in the internal session cache.
*/ */
public long number() { public long number() {
return SSLContext.sessionNumber(context); return SSLContext.sessionNumber(context.ctx);
} }
/** /**
* Returns the number of started SSL/TLS handshakes in client mode. * Returns the number of started SSL/TLS handshakes in client mode.
*/ */
public long connect() { public long connect() {
return SSLContext.sessionConnect(context); return SSLContext.sessionConnect(context.ctx);
} }
/** /**
* Returns the number of successfully established SSL/TLS sessions in client mode. * Returns the number of successfully established SSL/TLS sessions in client mode.
*/ */
public long connectGood() { public long connectGood() {
return SSLContext.sessionConnectGood(context); return SSLContext.sessionConnectGood(context.ctx);
} }
/** /**
* Returns the number of start renegotiations in client mode. * Returns the number of start renegotiations in client mode.
*/ */
public long connectRenegotiate() { public long connectRenegotiate() {
return SSLContext.sessionConnectRenegotiate(context); return SSLContext.sessionConnectRenegotiate(context.ctx);
} }
/** /**
* Returns the number of started SSL/TLS handshakes in server mode. * Returns the number of started SSL/TLS handshakes in server mode.
*/ */
public long accept() { public long accept() {
return SSLContext.sessionAccept(context); return SSLContext.sessionAccept(context.ctx);
} }
/** /**
* Returns the number of successfully established SSL/TLS sessions in server mode. * Returns the number of successfully established SSL/TLS sessions in server mode.
*/ */
public long acceptGood() { public long acceptGood() {
return SSLContext.sessionAcceptGood(context); return SSLContext.sessionAcceptGood(context.ctx);
} }
/** /**
* Returns the number of start renegotiations in server mode. * Returns the number of start renegotiations in server mode.
*/ */
public long acceptRenegotiate() { public long acceptRenegotiate() {
return SSLContext.sessionAcceptRenegotiate(context); return SSLContext.sessionAcceptRenegotiate(context.ctx);
} }
/** /**
@ -86,14 +90,14 @@ public final class OpenSslSessionStats {
* external cache is counted as a hit. * external cache is counted as a hit.
*/ */
public long hits() { public long hits() {
return SSLContext.sessionHits(context); return SSLContext.sessionHits(context.ctx);
} }
/** /**
* Returns the number of successfully retrieved sessions from the external session cache in server mode. * Returns the number of successfully retrieved sessions from the external session cache in server mode.
*/ */
public long cbHits() { public long cbHits() {
return SSLContext.sessionCbHits(context); return SSLContext.sessionCbHits(context.ctx);
} }
/** /**
@ -101,7 +105,7 @@ public final class OpenSslSessionStats {
* in server mode. * in server mode.
*/ */
public long misses() { public long misses() {
return SSLContext.sessionMisses(context); return SSLContext.sessionMisses(context.ctx);
} }
/** /**
@ -110,13 +114,13 @@ public final class OpenSslSessionStats {
* count. * count.
*/ */
public long timeouts() { public long timeouts() {
return SSLContext.sessionTimeouts(context); return SSLContext.sessionTimeouts(context.ctx);
} }
/** /**
* Returns the number of sessions that were removed because the maximum session cache size was exceeded. * Returns the number of sessions that were removed because the maximum session cache size was exceeded.
*/ */
public long cacheFull() { public long cacheFull() {
return SSLContext.sessionCacheFull(context); return SSLContext.sessionCacheFull(context.ctx);
} }
} }