Skip to content

Commit

Permalink
8240256: Better resource cleaning for SunPKCS11 Provider
Browse files Browse the repository at this point in the history
Reviewed-by: valeriep
  • Loading branch information
coffeys committed Jun 3, 2021
1 parent 06f87cf commit bdeaeb4
Show file tree
Hide file tree
Showing 11 changed files with 505 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,15 @@ private static void debug(Object o) {
// how often to test for token insertion, if no token is present
private int insertionCheckInterval = 2000;

// flag inidicating whether to omit the call to C_Initialize()
// short ms value to indicate how often native cleaner thread is called
private int resourceCleanerShortInterval = 2_000;
// long ms value to indicate how often native cleaner thread is called
private int resourceCleanerLongInterval = 60_000;

// should Token be destroyed after logout()
private boolean destroyTokenAfterLogout;

// flag indicating whether to omit the call to C_Initialize()
// should be used only if we are running within a process that
// has already called it (e.g. Plugin inside of Mozilla/NSS)
private boolean omitInitialize = false;
Expand Down Expand Up @@ -278,6 +286,18 @@ boolean getExplicitCancel() {
return explicitCancel;
}

boolean getDestroyTokenAfterLogout() {
return destroyTokenAfterLogout;
}

int getResourceCleanerShortInterval() {
return resourceCleanerShortInterval;
}

int getResourceCleanerLongInterval() {
return resourceCleanerLongInterval;
}

int getInsertionCheckInterval() {
return insertionCheckInterval;
}
Expand Down Expand Up @@ -412,6 +432,18 @@ private void parse() throws IOException {
if (insertionCheckInterval < 100) {
throw excLine(word + " must be at least 100 ms");
}
} else if (word.equals("cleaner.shortInterval")) {
resourceCleanerShortInterval = parseIntegerEntry(word);
if (resourceCleanerShortInterval < 1_000) {
throw excLine(word + " must be at least 1000 ms");
}
} else if (word.equals("cleaner.longInterval")) {
resourceCleanerLongInterval = parseIntegerEntry(word);
if (resourceCleanerLongInterval < 1_000) {
throw excLine(word + " must be at least 1000 ms");
}
} else if (word.equals("destroyTokenAfterLogout")) {
destroyTokenAfterLogout = parseBooleanEntry(word);
} else if (word.equals("showInfo")) {
showInfo = parseBooleanEntry(word);
} else if (word.equals("keyStoreCompatibilityMode")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -100,4 +100,8 @@ synchronized void put(Key key, P11Key p11Key) {
map.put(key, p11Key);
}

synchronized void clear() {
strongCache.clear();
cacheReference = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import static sun.security.pkcs11.TemplateManager.O_GENERATE;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;

import sun.security.util.Debug;
import sun.security.util.DerValue;
import sun.security.util.Length;
import sun.security.util.ECUtil;
Expand Down Expand Up @@ -142,8 +141,8 @@ abstract class P11Key implements Key, Length {
&& tokenLabel[2] == 'S');
boolean extractKeyInfo = (!DISABLE_NATIVE_KEYS_EXTRACTION && isNSS &&
extractable && !tokenObject);
this.keyIDHolder = new NativeKeyHolder(this, keyID, session, extractKeyInfo,
tokenObject);
this.keyIDHolder = new NativeKeyHolder(this, keyID, session,
extractKeyInfo, tokenObject);
}

public long getKeyID() {
Expand All @@ -166,6 +165,18 @@ public final byte[] getEncoded() {
return (b == null) ? null : b.clone();
}

// Called by the NativeResourceCleaner at specified intervals
// See NativeResourceCleaner for more information
static boolean drainRefQueue() {
boolean found = false;
SessionKeyRef next;
while ((next = (SessionKeyRef) SessionKeyRef.refQueue.poll()) != null) {
found = true;
next.dispose();
}
return found;
}

abstract byte[] getEncodedInternal();

public boolean equals(Object obj) {
Expand Down Expand Up @@ -882,7 +893,7 @@ public DHParameterSpec getParams() {
return params;
}
public int hashCode() {
if (token.isValid() == false) {
if (!token.isValid()) {
return 0;
}
fetchValues();
Expand All @@ -891,7 +902,7 @@ public int hashCode() {
public boolean equals(Object obj) {
if (this == obj) return true;
// equals() should never throw exceptions
if (token.isValid() == false) {
if (!token.isValid()) {
return false;
}
if (!(obj instanceof DHPrivateKey)) {
Expand Down Expand Up @@ -1129,7 +1140,6 @@ public String toString() {
}
}
}

final class NativeKeyHolder {

private static long nativeKeyWrapperKeyID = 0;
Expand Down Expand Up @@ -1254,6 +1264,7 @@ static void decWrapperKeyRef() {
this.ref = new SessionKeyRef(p11Key, keyID, wrapperKeyUsed,
keySession);
}

this.nativeKeyInfo = ((ki == null || ki.length == 0)? null : ki);
}

Expand Down Expand Up @@ -1327,24 +1338,9 @@ void releaseKeyID() {
* still use these keys during finalization such as SSLSocket.
*/
final class SessionKeyRef extends PhantomReference<P11Key> {
private static ReferenceQueue<P11Key> refQueue =
new ReferenceQueue<P11Key>();
static ReferenceQueue<P11Key> refQueue = new ReferenceQueue<>();
private static Set<SessionKeyRef> refSet =
Collections.synchronizedSet(new HashSet<SessionKeyRef>());

static ReferenceQueue<P11Key> referenceQueue() {
return refQueue;
}

private static void drainRefQueueBounded() {
while (true) {
SessionKeyRef next = (SessionKeyRef) refQueue.poll();
if (next == null) {
break;
}
next.dispose();
}
}
Collections.synchronizedSet(new HashSet<>());

// handle to the native key and the session it is generated under
private long keyID;
Expand All @@ -1355,13 +1351,13 @@ private static void drainRefQueueBounded() {
Session session) {
super(p11Key, refQueue);
if (session == null) {
throw new ProviderException("key must be associated with a session");
throw new ProviderException
("key must be associated with a session");
}
registerNativeKey(keyID, session);
this.wrapperKeyUsed = wrapperKeyUsed;

refSet.add(this);
drainRefQueueBounded();
}

void registerNativeKey(long newKeyID, Session newSession) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -82,10 +82,6 @@ boolean isLive(long currentTime) {
return currentTime - lastAccess < MAX_IDLE_TIME;
}

long idInternal() {
return id;
}

long id() {
if (token.isPresent(this.id) == false) {
throw new ProviderException("Token has been removed");
Expand All @@ -112,15 +108,40 @@ boolean hasObjects() {
return createdObjects.get() != 0;
}

// regular close which will not close sessions when there are objects(keys)
// still associated with them
void close() {
if (hasObjects()) {
close(true);
}

// forced close which will close sessions regardless if there are objects
// associated with them. Note that closing the sessions this way may
// lead to those associated objects(keys) un-usable. Thus should only be
// used for scenarios such as the token is about to be removed, etc.
void kill() {
close(false);
}

private void close(boolean checkObjCtr) {
if (hasObjects() && checkObjCtr) {
throw new ProviderException(
"Internal error: close session with active objects");
"Internal error: close session with active objects");
}
sessionRef.dispose();
}
}

// Called by the NativeResourceCleaner at specified intervals
// See NativeResourceCleaner for more information
static boolean drainRefQueue() {
boolean found = false;
SessionRef next;
while ((next = (SessionRef) SessionRef.refQueue.poll())!= null) {
found = true;
next.dispose();
}
return found;
}
}
/*
* NOTE: Use PhantomReference here and not WeakReference
* otherwise the sessions maybe closed before other objects
Expand All @@ -129,27 +150,10 @@ void close() {
final class SessionRef extends PhantomReference<Session>
implements Comparable<SessionRef> {

private static ReferenceQueue<Session> refQueue =
new ReferenceQueue<Session>();
static ReferenceQueue<Session> refQueue = new ReferenceQueue<>();

private static Set<SessionRef> refList =
Collections.synchronizedSortedSet(new TreeSet<SessionRef>());

static ReferenceQueue<Session> referenceQueue() {
return refQueue;
}

static int totalCount() {
return refList.size();
}

private static void drainRefQueueBounded() {
while (true) {
SessionRef next = (SessionRef) refQueue.poll();
if (next == null) break;
next.dispose();
}
}
Collections.synchronizedSortedSet(new TreeSet<>());

// handle to the native session
private long id;
Expand All @@ -160,8 +164,6 @@ private static void drainRefQueueBounded() {
this.id = id;
this.token = token;
refList.add(this);
// TBD: run at some interval and not every time?
drainRefQueueBounded();
}

void dispose() {
Expand All @@ -170,9 +172,7 @@ void dispose() {
if (token.isPresent(id)) {
token.p11.C_CloseSession(id);
}
} catch (PKCS11Exception e1) {
// ignore
} catch (ProviderException e2) {
} catch (PKCS11Exception | ProviderException e1) {
// ignore
} finally {
this.clear();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -26,11 +26,9 @@
package sun.security.pkcs11;

import java.util.*;

import java.security.ProviderException;

import sun.security.util.Debug;

import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;

Expand Down Expand Up @@ -171,15 +169,16 @@ Session killSession(Session session) {
System.out.println("Killing session (" + location + ") active: "
+ activeSessions.get());
}
closeSession(session);

session.kill();
activeSessions.decrementAndGet();
return null;
}

Session releaseSession(Session session) {
if ((session == null) || (token.isValid() == false)) {
return null;
}

if (session.hasObjects()) {
objSessions.release(session);
} else {
Expand All @@ -188,6 +187,11 @@ Session releaseSession(Session session) {
return null;
}

void clearPools() {
objSessions.closeAll();
opSessions.closeAll();
}

void demoteObjSession(Session session) {
if (token.isValid() == false) {
return;
Expand All @@ -196,6 +200,7 @@ void demoteObjSession(Session session) {
System.out.println("Demoting session, active: " +
activeSessions.get());
}

boolean present = objSessions.remove(session);
if (present == false) {
// session is currently in use
Expand Down Expand Up @@ -238,6 +243,7 @@ public static final class Pool {
private final SessionManager mgr;
private final AbstractQueue<Session> pool;
private final int SESSION_MAX = 5;
private volatile boolean closed = false;

// Object session pools can contain unlimited sessions.
// Operation session pools are limited and enforced by the queue.
Expand All @@ -260,14 +266,17 @@ Session poll() {

void release(Session session) {
// Object session pools never return false, only Operation ones
if (!pool.offer(session)) {
if (closed || !pool.offer(session)) {
mgr.closeSession(session);
free();
}
}

// Free any old operation session if this queue is full
void free() {
// quick return path
if (pool.size() == 0) return;

int n = SESSION_MAX;
int i = 0;
Session oldestSession;
Expand All @@ -291,6 +300,14 @@ void free() {
}
}

// empty out all sessions inside 'pool' and close them.
// however the Pool can still accept sessions
void closeAll() {
closed = true;
Session s;
while ((s = pool.poll()) != null) {
mgr.killSession(s);
}
}
}

}
Loading

1 comment on commit bdeaeb4

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.