diff options
Diffstat (limited to 'security/nss/lib/dev/devslot.c')
-rw-r--r-- | security/nss/lib/dev/devslot.c | 105 |
1 files changed, 80 insertions, 25 deletions
diff --git a/security/nss/lib/dev/devslot.c b/security/nss/lib/dev/devslot.c index 9f0bd8226..ebd6e6aa5 100644 --- a/security/nss/lib/dev/devslot.c +++ b/security/nss/lib/dev/devslot.c @@ -33,6 +33,8 @@ nssSlot_Destroy( if (PR_ATOMIC_DECREMENT(&slot->base.refCount) == 0) { PK11_FreeSlot(slot->pk11slot); PZ_DestroyLock(slot->base.lock); + PZ_DestroyCondVar(slot->isPresentCondition); + PZ_DestroyLock(slot->isPresentLock); return nssArena_Destroy(slot->base.arena); } } @@ -88,20 +90,28 @@ NSS_IMPLEMENT void nssSlot_ResetDelay( NSSSlot *slot) { - slot->lastTokenPing = 0; + PZ_Lock(slot->isPresentLock); + slot->lastTokenPingState = nssSlotLastPingState_Reset; + PZ_Unlock(slot->isPresentLock); } static PRBool -within_token_delay_period(const NSSSlot *slot) +token_status_checked(const NSSSlot *slot) { - PRIntervalTime time, lastTime; + PRIntervalTime time; + int lastPingState = slot->lastTokenPingState; + /* When called from the same thread, that means + * nssSlot_IsTokenPresent() is called recursively through + * nssSlot_Refresh(). Return immediately in that case. */ + if (slot->isPresentThread == PR_GetCurrentThread()) { + return PR_TRUE; + } /* Set the delay time for checking the token presence */ if (s_token_delay_time == 0) { s_token_delay_time = PR_SecondsToInterval(NSSSLOT_TOKEN_DELAY_TIME); } time = PR_IntervalNow(); - lastTime = slot->lastTokenPing; - if ((lastTime) && ((time - lastTime) < s_token_delay_time)) { + if ((lastPingState == nssSlotLastPingState_Valid) && ((time - slot->lastTokenPingTime) < s_token_delay_time)) { return PR_TRUE; } return PR_FALSE; @@ -117,35 +127,63 @@ nssSlot_IsTokenPresent( nssSession *session; CK_SLOT_INFO slotInfo; void *epv; + PRBool isPresent = PR_FALSE; + /* permanent slots are always present unless they're disabled */ if (nssSlot_IsPermanent(slot)) { return !PK11_IsDisabled(slot->pk11slot); } + /* avoid repeated calls to check token status within set interval */ - if (within_token_delay_period(slot)) { - return ((slot->ckFlags & CKF_TOKEN_PRESENT) != 0); + PZ_Lock(slot->isPresentLock); + if (token_status_checked(slot)) { + CK_FLAGS ckFlags = slot->ckFlags; + PZ_Unlock(slot->isPresentLock); + return ((ckFlags & CKF_TOKEN_PRESENT) != 0); } + PZ_Unlock(slot->isPresentLock); - /* First obtain the slot info */ + /* First obtain the slot epv before we set up the condition + * variable, so we can just return if we couldn't get it. */ epv = slot->epv; if (!epv) { return PR_FALSE; } + + /* set up condition so only one thread is active in this part of the code at a time */ + PZ_Lock(slot->isPresentLock); + while (slot->isPresentThread) { + PR_WaitCondVar(slot->isPresentCondition, PR_INTERVAL_NO_TIMEOUT); + } + /* if we were one of multiple threads here, the first thread will have + * given us the answer, no need to make more queries of the token. */ + if (token_status_checked(slot)) { + CK_FLAGS ckFlags = slot->ckFlags; + PZ_Unlock(slot->isPresentLock); + return ((ckFlags & CKF_TOKEN_PRESENT) != 0); + } + /* this is the winning thread, block all others until we've determined + * if the token is present and that it needs initialization. */ + slot->lastTokenPingState = nssSlotLastPingState_Update; + slot->isPresentThread = PR_GetCurrentThread(); + + PZ_Unlock(slot->isPresentLock); + nssSlot_EnterMonitor(slot); ckrv = CKAPI(epv)->C_GetSlotInfo(slot->slotID, &slotInfo); nssSlot_ExitMonitor(slot); if (ckrv != CKR_OK) { slot->token->base.name[0] = 0; /* XXX */ - slot->lastTokenPing = PR_IntervalNow(); - return PR_FALSE; + isPresent = PR_FALSE; + goto done; } slot->ckFlags = slotInfo.flags; /* check for the presence of the token */ if ((slot->ckFlags & CKF_TOKEN_PRESENT) == 0) { if (!slot->token) { /* token was never present */ - slot->lastTokenPing = PR_IntervalNow(); - return PR_FALSE; + isPresent = PR_FALSE; + goto done; } session = nssToken_GetDefaultSession(slot->token); if (session) { @@ -167,15 +205,15 @@ nssSlot_IsTokenPresent( slot->token->base.name[0] = 0; /* XXX */ /* clear the token cache */ nssToken_Remove(slot->token); - slot->lastTokenPing = PR_IntervalNow(); - return PR_FALSE; + isPresent = PR_FALSE; + goto done; } /* token is present, use the session info to determine if the card * has been removed and reinserted. */ session = nssToken_GetDefaultSession(slot->token); if (session) { - PRBool isPresent = PR_FALSE; + PRBool tokenRemoved; nssSession_EnterMonitor(session); if (session->handle != CK_INVALID_SESSION) { CK_SESSION_INFO sessionInfo; @@ -187,12 +225,12 @@ nssSlot_IsTokenPresent( session->handle = CK_INVALID_SESSION; } } - isPresent = session->handle != CK_INVALID_SESSION; + tokenRemoved = (session->handle == CK_INVALID_SESSION); nssSession_ExitMonitor(session); /* token not removed, finished */ - if (isPresent) { - slot->lastTokenPing = PR_IntervalNow(); - return PR_TRUE; + if (!tokenRemoved) { + isPresent = PR_TRUE; + goto done; } } /* the token has been removed, and reinserted, or the slot contains @@ -203,15 +241,32 @@ nssSlot_IsTokenPresent( nssToken_Remove(slot->token); /* token has been removed, need to refresh with new session */ nssrv = nssSlot_Refresh(slot); + isPresent = PR_TRUE; if (nssrv != PR_SUCCESS) { slot->token->base.name[0] = 0; /* XXX */ slot->ckFlags &= ~CKF_TOKEN_PRESENT; - /* TODO: insert a barrier here to avoid reordering of the assingments */ - slot->lastTokenPing = PR_IntervalNow(); - return PR_FALSE; + isPresent = PR_FALSE; + } +done: + /* Once we've set up the condition variable, + * Before returning, it's necessary to: + * 1) Set the lastTokenPingTime so that any other threads waiting on this + * initialization and any future calls within the initialization window + * return the just-computed status. + * 2) Indicate we're complete, waking up all other threads that may still + * be waiting on initialization can progress. + */ + PZ_Lock(slot->isPresentLock); + /* don't update the time if we were reset while we were + * getting the token state */ + if (slot->lastTokenPingState == nssSlotLastPingState_Update) { + slot->lastTokenPingTime = PR_IntervalNow(); + slot->lastTokenPingState = nssSlotLastPingState_Valid; } - slot->lastTokenPing = PR_IntervalNow(); - return PR_TRUE; + slot->isPresentThread = NULL; + PR_NotifyAllCondVar(slot->isPresentCondition); + PZ_Unlock(slot->isPresentLock); + return isPresent; } NSS_IMPLEMENT void * @@ -229,7 +284,7 @@ nssSlot_GetToken( if (nssSlot_IsTokenPresent(slot)) { /* Even if a token should be present, check `slot->token` too as it - * might be gone already. This would happen mostly on shutdown. */ + * might be gone already. This would happen mostly on shutdown. */ nssSlot_EnterMonitor(slot); if (slot->token) rvToken = nssToken_AddRef(slot->token); |