Blame SOURCES/nss-3.16-token-init-race.patch

5f1c2b
diff -up nss/lib/pk11wrap/dev3hack.c.init-token-race nss/lib/pk11wrap/dev3hack.c
5f1c2b
--- nss/lib/pk11wrap/dev3hack.c.init-token-race	2017-01-13 17:58:55.485868744 +0100
5f1c2b
+++ nss/lib/pk11wrap/dev3hack.c	2017-01-13 18:02:27.126675831 +0100
5f1c2b
@@ -231,6 +231,16 @@ nssSlot_Refresh(NSSSlot *slot)
1b6f66
     if (slot->token && slot->token->base.name[0] == 0) {
5f1c2b
         doit = PR_TRUE;
1b6f66
     }
1b6f66
+    /* invalidate the session in the nss3slot if we haven't done an init
1b6f66
+     * token since we noticed that the token->default session is invalid.
1b6f66
+     * This works because the monitor lock and the token session lock are the
1b6f66
+     * same locks */
1b6f66
+    PK11_EnterSlotMonitor(nss3slot);
1b6f66
+    if ((slot->token == NULL) || (slot->token->defaultSession == NULL) || 
1b6f66
+		(slot->token->defaultSession->handle == CK_INVALID_SESSION)) {
1b6f66
+	nss3slot->session = CK_INVALID_SESSION;
1b6f66
+    }
1b6f66
+    PK11_ExitSlotMonitor(nss3slot);
1b6f66
     if (PK11_InitToken(nss3slot, PR_FALSE) != SECSuccess) {
5f1c2b
         return PR_FAILURE;
1b6f66
     }
5f1c2b
@@ -238,7 +248,8 @@ nssSlot_Refresh(NSSSlot *slot)
5f1c2b
         nssTrustDomain_UpdateCachedTokenCerts(slot->token->trustDomain,
5f1c2b
                                               slot->token);
1b6f66
     }
1b6f66
-    return nssToken_Refresh(slot->token);
1b6f66
+    /* no need to call nssToken_Refresh since PK11_Init has already done so */
1b6f66
+    return PR_SUCCESS;
1b6f66
 }
1b6f66
 
1b6f66
 NSS_IMPLEMENT PRStatus
5f1c2b
diff -up nss/lib/pk11wrap/pk11auth.c.init-token-race nss/lib/pk11wrap/pk11auth.c
5f1c2b
--- nss/lib/pk11wrap/pk11auth.c.init-token-race	2017-01-13 17:58:55.485868744 +0100
5f1c2b
+++ nss/lib/pk11wrap/pk11auth.c	2017-01-13 18:05:07.650739842 +0100
5f1c2b
@@ -73,8 +73,6 @@ pk11_CheckPassword(PK11SlotInfo *slot, C
5f1c2b
                                          (unsigned char *)pw, len);
5f1c2b
         slot->lastLoginCheck = 0;
5f1c2b
         mustRetry = PR_FALSE;
5f1c2b
-        if (!alreadyLocked)
5f1c2b
-            PK11_ExitSlotMonitor(slot);
5f1c2b
         switch (crv) {
5f1c2b
             /* if we're already logged in, we're good to go */
5f1c2b
             case CKR_OK:
5f1c2b
@@ -101,7 +99,16 @@ pk11_CheckPassword(PK11SlotInfo *slot, C
5f1c2b
                     break;
5f1c2b
                 }
5f1c2b
                 if (retry++ == 0) {
5f1c2b
+		    /* we already know the this session is invalid */
5f1c2b
+		    slot->session = CK_INVALID_SESSION; 
5f1c2b
+		    /* can't enter PK11_InitToken holding the lock
5f1c2b
+		     * This is safe because the only places that tries to
5f1c2b
+		     * hold the slot monitor over this call pass their own
5f1c2b
+		     * session, which would have failed above.
5f1c2b
+		     * (session != slot->session) */
5f1c2b
+		    PK11_ExitSlotMonitor(slot);
5f1c2b
                     rv = PK11_InitToken(slot, PR_FALSE);
5f1c2b
+		    PK11_EnterSlotMonitor(slot);
5f1c2b
                     if (rv == SECSuccess) {
5f1c2b
                         if (slot->session != CK_INVALID_SESSION) {
5f1c2b
                             session = slot->session; /* we should have
5f1c2b
@@ -119,6 +126,8 @@ pk11_CheckPassword(PK11SlotInfo *slot, C
5f1c2b
                 PORT_SetError(PK11_MapError(crv));
5f1c2b
                 rv = SECFailure; /* some failure we can't fix by retrying */
5f1c2b
         }
5f1c2b
+	if (!alreadyLocked)
5f1c2b
+	    PK11_ExitSlotMonitor(slot);
1b6f66
     } while (mustRetry);
1b6f66
     return rv;
1b6f66
 }
5f1c2b
@@ -465,14 +474,18 @@ done:
1b6f66
     slot->lastLoginCheck = 0;
5f1c2b
     PK11_RestoreROSession(slot, rwsession);
1b6f66
     if (rv == SECSuccess) {
1b6f66
+	PK11_EnterSlotMonitor(slot);
1b6f66
         /* update our view of the world */
1b6f66
+	if (slot->session != CK_INVALID_SESSION) {
1b6f66
+		PK11_GETTAB(slot)->C_CloseSession(slot->session);
1b6f66
+		slot->session = CK_INVALID_SESSION;
1b6f66
+	}
1b6f66
+	PK11_ExitSlotMonitor(slot);
5f1c2b
         PK11_InitToken(slot, PR_TRUE);
5f1c2b
         if (slot->needLogin) {
5f1c2b
-            PK11_EnterSlotMonitor(slot);
5f1c2b
             PK11_GETTAB(slot)->C_Login(slot->session, CKU_USER,
5f1c2b
                                        (unsigned char *)userpw, len);
5f1c2b
             slot->lastLoginCheck = 0;
5f1c2b
-            PK11_ExitSlotMonitor(slot);
5f1c2b
         }
1b6f66
     }
1b6f66
     return rv;
5f1c2b
@@ -520,7 +533,7 @@ PK11_ChangePW(PK11SlotInfo *slot, const
5f1c2b
     PK11_RestoreROSession(slot, rwsession);
1b6f66
 
1b6f66
     /* update our view of the world */
5f1c2b
-    PK11_InitToken(slot, PR_TRUE);
1b6f66
+    /* PK11_InitToken(slot,PR_TRUE); */
1b6f66
     return rv;
1b6f66
 }
1b6f66
 
5f1c2b
diff -up nss/lib/pk11wrap/pk11slot.c.init-token-race nss/lib/pk11wrap/pk11slot.c
5f1c2b
--- nss/lib/pk11wrap/pk11slot.c.init-token-race	2017-01-13 17:58:55.486868720 +0100
5f1c2b
+++ nss/lib/pk11wrap/pk11slot.c	2017-01-13 18:12:50.869381900 +0100
5f1c2b
@@ -1085,6 +1085,7 @@ PK11_ReadMechanismList(PK11SlotInfo *slo
1b6f66
     CK_ULONG count;
1b6f66
     CK_RV crv;
1b6f66
     PRUint32 i;
1b6f66
+    char mechanismBits[sizeof(slot->mechanismBits)];
1b6f66
 
1b6f66
     if (slot->mechanismList) {
5f1c2b
         PORT_Free(slot->mechanismList);
5f1c2b
@@ -1092,12 +1093,8 @@ PK11_ReadMechanismList(PK11SlotInfo *slo
1b6f66
     }
1b6f66
     slot->mechanismCount = 0;
1b6f66
 
5f1c2b
-    if (!slot->isThreadSafe)
5f1c2b
-        PK11_EnterSlotMonitor(slot);
5f1c2b
     crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID, NULL, &count);
1b6f66
     if (crv != CKR_OK) {
5f1c2b
-        if (!slot->isThreadSafe)
5f1c2b
-            PK11_ExitSlotMonitor(slot);
5f1c2b
         PORT_SetError(PK11_MapError(crv));
5f1c2b
         return SECFailure;
1b6f66
     }
5f1c2b
@@ -1105,14 +1102,10 @@ PK11_ReadMechanismList(PK11SlotInfo *slo
1b6f66
     slot->mechanismList = (CK_MECHANISM_TYPE *)
5f1c2b
         PORT_Alloc(count * sizeof(CK_MECHANISM_TYPE));
1b6f66
     if (slot->mechanismList == NULL) {
5f1c2b
-        if (!slot->isThreadSafe)
5f1c2b
-            PK11_ExitSlotMonitor(slot);
5f1c2b
         return SECFailure;
1b6f66
     }
1b6f66
     crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID,
5f1c2b
                                                 slot->mechanismList, &count);
5f1c2b
-    if (!slot->isThreadSafe)
5f1c2b
-        PK11_ExitSlotMonitor(slot);
1b6f66
     if (crv != CKR_OK) {
5f1c2b
         PORT_Free(slot->mechanismList);
5f1c2b
         slot->mechanismList = NULL;
5f1c2b
@@ -1120,14 +1113,16 @@ PK11_ReadMechanismList(PK11SlotInfo *slo
5f1c2b
         return SECSuccess;
1b6f66
     }
1b6f66
     slot->mechanismCount = count;
1b6f66
-    PORT_Memset(slot->mechanismBits, 0, sizeof(slot->mechanismBits));
1b6f66
+    PORT_Memset(mechanismBits, 0, sizeof(slot->mechanismBits));
1b6f66
 
5f1c2b
     for (i = 0; i < count; i++) {
5f1c2b
         CK_MECHANISM_TYPE mech = slot->mechanismList[i];
5f1c2b
         if (mech < 0x7ff) {
5f1c2b
-            slot->mechanismBits[mech & 0xff] |= 1 << (mech >> 8);
1b6f66
+	    mechanismBits[mech & 0xff] |= 1 << (mech >> 8);
5f1c2b
         }
1b6f66
     }
1b6f66
+    PORT_Memcpy(slot->mechanismBits, mechanismBits, 
1b6f66
+					sizeof(slot->mechanismBits));
1b6f66
     return SECSuccess;
1b6f66
 }
1b6f66
 
5f1c2b
@@ -1144,14 +1139,20 @@ PK11_InitToken(PK11SlotInfo *slot, PRBoo
1b6f66
     CK_RV crv;
1b6f66
     SECStatus rv;
1b6f66
     PRStatus status;
1b6f66
+    CK_SESSION_HANDLE session;
1b6f66
+ 
1b6f66
+    PK11_EnterSlotMonitor(slot);
1b6f66
+    if (slot->session != CK_INVALID_SESSION) {
1b6f66
+	/* The reason for doing an InitToken has already been satisfied by
1b6f66
+         * another thread. Just return */
1b6f66
+	PK11_ExitSlotMonitor(slot);
1b6f66
+	return SECSuccess;
1b6f66
+    }
1b6f66
 
1b6f66
     /* set the slot flags to the current token values */
5f1c2b
-    if (!slot->isThreadSafe)
5f1c2b
-        PK11_EnterSlotMonitor(slot);
5f1c2b
     crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID, &tokenInfo);
5f1c2b
-    if (!slot->isThreadSafe)
5f1c2b
-        PK11_ExitSlotMonitor(slot);
1b6f66
     if (crv != CKR_OK) {
1b6f66
+	PK11_ExitSlotMonitor(slot);
5f1c2b
         PORT_SetError(PK11_MapError(crv));
5f1c2b
         return SECFailure;
1b6f66
     }
5f1c2b
@@ -1186,8 +1187,10 @@ PK11_InitToken(PK11SlotInfo *slot, PRBoo
5f1c2b
     slot->defRWSession = (PRBool)((!slot->readOnly) &&
5f1c2b
                                   (tokenInfo.ulMaxSessionCount == 1));
1b6f66
     rv = PK11_ReadMechanismList(slot);
5f1c2b
-    if (rv != SECSuccess)
5f1c2b
-        return rv;
1b6f66
+    if (rv != SECSuccess)  {
1b6f66
+	PK11_ExitSlotMonitor(slot);
1b6f66
+ 	return rv;
1b6f66
+    }
1b6f66
 
1b6f66
     slot->hasRSAInfo = PR_FALSE;
1b6f66
     slot->RSAInfoFlags = 0;
5f1c2b
@@ -1202,56 +1205,23 @@ PK11_InitToken(PK11SlotInfo *slot, PRBoo
5f1c2b
         slot->maxKeyCount = tokenInfo.ulMaxSessionCount / 2;
1b6f66
     }
1b6f66
 
1b6f66
-    /* Make sure our session handle is valid */
1b6f66
-    if (slot->session == CK_INVALID_SESSION) {
5f1c2b
-        /* we know we don't have a valid session, go get one */
5f1c2b
-        CK_SESSION_HANDLE session;
1b6f66
-
5f1c2b
-        /* session should be Readonly, serial */
5f1c2b
-        if (!slot->isThreadSafe)
5f1c2b
-            PK11_EnterSlotMonitor(slot);
5f1c2b
-        crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
1b6f66
+    /* we know we don't have a valid session, go get one */
1b6f66
+    /* session should be Readonly, serial */
1b6f66
+    crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
5f1c2b
                                                (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION,
5f1c2b
                                                slot, pk11_notify, &session);
5f1c2b
-        if (!slot->isThreadSafe)
5f1c2b
-            PK11_ExitSlotMonitor(slot);
5f1c2b
-        if (crv != CKR_OK) {
5f1c2b
-            PORT_SetError(PK11_MapError(crv));
5f1c2b
-            return SECFailure;
5f1c2b
-        }
5f1c2b
-        slot->session = session;
1b6f66
-    } else {
5f1c2b
-        /* The session we have may be defunct (the token associated with it)
5f1c2b
-         * has been removed   */
5f1c2b
-        CK_SESSION_INFO sessionInfo;
1b6f66
-
5f1c2b
-        if (!slot->isThreadSafe)
5f1c2b
-            PK11_EnterSlotMonitor(slot);
5f1c2b
-        crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo);
1b6f66
-        if (crv == CKR_DEVICE_ERROR) {
5f1c2b
-            PK11_GETTAB(slot)
5f1c2b
-                ->C_CloseSession(slot->session);
5f1c2b
-            crv = CKR_SESSION_CLOSED;
5f1c2b
-        }
5f1c2b
-        if ((crv == CKR_SESSION_CLOSED) || (crv == CKR_SESSION_HANDLE_INVALID)) {
5f1c2b
-            crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
5f1c2b
-                                                   (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION,
5f1c2b
-                                                   slot, pk11_notify, &slot->session);
5f1c2b
-            if (crv != CKR_OK) {
5f1c2b
-                PORT_SetError(PK11_MapError(crv));
5f1c2b
-                slot->session = CK_INVALID_SESSION;
5f1c2b
-                if (!slot->isThreadSafe)
5f1c2b
-                    PK11_ExitSlotMonitor(slot);
5f1c2b
-                return SECFailure;
5f1c2b
-            }
5f1c2b
-        }
5f1c2b
-        if (!slot->isThreadSafe)
5f1c2b
-            PK11_ExitSlotMonitor(slot);
1b6f66
+    if (crv != CKR_OK) {
1b6f66
+	PK11_ExitSlotMonitor(slot);
1b6f66
+	PORT_SetError(PK11_MapError(crv));
1b6f66
+	return SECFailure;
1b6f66
     }
1b6f66
+    slot->session = session;
1b6f66
 
1b6f66
     status = nssToken_Refresh(slot->nssToken);
1b6f66
-    if (status != PR_SUCCESS)
1b6f66
+    if (status != PR_SUCCESS) {
1b6f66
+	PK11_ExitSlotMonitor(slot);
5f1c2b
         return SECFailure;
1b6f66
+     }
1b6f66
 
1b6f66
     if (!(slot->isInternal) && (slot->hasRandom)) {
5f1c2b
         /* if this slot has a random number generater, use it to add entropy
5f1c2b
@@ -1264,28 +1234,20 @@ PK11_InitToken(PK11SlotInfo *slot, PRBoo
5f1c2b
             /* if this slot can issue random numbers, get some entropy from
5f1c2b
              * that random number generater and give it to our internal token.
5f1c2b
              */
5f1c2b
-            PK11_EnterSlotMonitor(slot);
5f1c2b
             crv = PK11_GETTAB(slot)->C_GenerateRandom(slot->session, random_bytes, sizeof(random_bytes));
5f1c2b
-            PK11_ExitSlotMonitor(slot);
5f1c2b
             if (crv == CKR_OK) {
5f1c2b
-                PK11_EnterSlotMonitor(int_slot);
5f1c2b
                 PK11_GETTAB(int_slot)
5f1c2b
                     ->C_SeedRandom(int_slot->session,
5f1c2b
                                    random_bytes, sizeof(random_bytes));
5f1c2b
-                PK11_ExitSlotMonitor(int_slot);
5f1c2b
             }
1b6f66
 
5f1c2b
             /* Now return the favor and send entropy to the token's random
5f1c2b
              * number generater */
5f1c2b
-            PK11_EnterSlotMonitor(int_slot);
5f1c2b
             crv = PK11_GETTAB(int_slot)->C_GenerateRandom(int_slot->session,
5f1c2b
                                                           random_bytes, sizeof(random_bytes));
5f1c2b
-            PK11_ExitSlotMonitor(int_slot);
5f1c2b
             if (crv == CKR_OK) {
5f1c2b
-                PK11_EnterSlotMonitor(slot);
5f1c2b
                 crv = PK11_GETTAB(slot)->C_SeedRandom(slot->session,
5f1c2b
                                                       random_bytes, sizeof(random_bytes));
5f1c2b
-                PK11_ExitSlotMonitor(slot);
5f1c2b
             }
5f1c2b
             PK11_FreeSlot(int_slot);
5f1c2b
         }
5f1c2b
@@ -1318,6 +1280,7 @@ PK11_InitToken(PK11SlotInfo *slot, PRBoo
5f1c2b
                 ->C_CloseSession(session);
5f1c2b
         }
1b6f66
     }
1b6f66
+    PK11_ExitSlotMonitor(slot);
5f1c2b
 
1b6f66
     return SECSuccess;
1b6f66
 }
5f1c2b
@@ -1433,6 +1396,8 @@ PK11_InitSlot(SECMODModule *mod, CK_SLOT
1b6f66
     }
1b6f66
     /* if the token is present, initialize it */
1b6f66
     if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) {
1b6f66
+	/* session was initialized to CK_INVALID_SESSION when the slot
1b6f66
+  	 * was created */
5f1c2b
         rv = PK11_InitToken(slot, PR_TRUE);
5f1c2b
         /* the only hard failures are on permanent devices, or function
5f1c2b
          * verify failures... function verify failures are already handled
5f1c2b
@@ -1888,10 +1853,14 @@ PK11_DoesMechanism(PK11SlotInfo *slot, C
5f1c2b
         return (slot->mechanismBits[type & 0xff] & (1 << (type >> 8))) ? PR_TRUE : PR_FALSE;
1b6f66
     }
5f1c2b
 
1b6f66
+    PK11_EnterSlotMonitor(slot);  
5f1c2b
     for (i = 0; i < (int)slot->mechanismCount; i++) {
5f1c2b
-        if (slot->mechanismList[i] == type)
5f1c2b
-            return PR_TRUE;
1b6f66
+	if (slot->mechanismList[i] == type) {
1b6f66
+	    PK11_ExitSlotMonitor(slot);
1b6f66
+	    return PR_TRUE;
1b6f66
+	}
1b6f66
     }
1b6f66
+    PK11_ExitSlotMonitor(slot);
1b6f66
     return PR_FALSE;
1b6f66
 }
1b6f66
 
5f1c2b
diff -up nss/lib/pk11wrap/pk11util.c.init-token-race nss/lib/pk11wrap/pk11util.c
5f1c2b
--- nss/lib/pk11wrap/pk11util.c.init-token-race	2017-01-13 17:58:55.487868695 +0100
5f1c2b
+++ nss/lib/pk11wrap/pk11util.c	2017-01-13 18:01:21.280291292 +0100
5f1c2b
@@ -1624,6 +1624,11 @@ SECMOD_RestartModules(PRBool force)
1b6f66
              * older modules require it, and it doesn't hurt (compliant modules
1b6f66
              * will return CKR_NOT_INITIALIZED */
5f1c2b
             (void)PK11_GETTAB(mod)->C_Finalize(NULL);
1b6f66
+	    /* finalize clears the session, mark them dead in the 
1b6f66
+	     * slot as well */
1b6f66
+	    for (i=0; i < mod->slotCount; i++) {
1b6f66
+		mod->slots[i]->session = CK_INVALID_SESSION;
1b6f66
+	    }
5f1c2b
             /* now initialize the module, this function reinitializes
5f1c2b
              * a module in place, preserving existing slots (even if they
5f1c2b
              * no longer exist) */
5f1c2b
@@ -1643,17 +1648,18 @@ SECMOD_RestartModules(PRBool force)
5f1c2b
                 /* get new token sessions, bump the series up so that
5f1c2b
                  * we refresh other old sessions. This will tell much of
5f1c2b
                  * NSS to flush cached handles it may hold as well */
5f1c2b
-                rv = PK11_InitToken(mod->slots[i], PR_TRUE);
1b6f66
+		PK11SlotInfo *slot = mod->slots[i];
1b6f66
+		rv = PK11_InitToken(slot,PR_TRUE);
5f1c2b
                 /* PK11_InitToken could fail if the slot isn't present.
5f1c2b
                  * If it is present, though, something is wrong and we should
5f1c2b
                  * disable the slot and let the caller know. */
5f1c2b
-                if (rv != SECSuccess && PK11_IsPresent(mod->slots[i])) {
1b6f66
+		if (rv != SECSuccess && PK11_IsPresent(slot)) {
5f1c2b
                     /* save the last error code */
5f1c2b
                     lastError = PORT_GetError();
5f1c2b
                     rrv = rv;
5f1c2b
                     /* disable the token */
5f1c2b
-                    mod->slots[i]->disabled = PR_TRUE;
5f1c2b
-                    mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
1b6f66
+		    slot->disabled = PR_TRUE;
1b6f66
+		    slot->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
5f1c2b
                 }
5f1c2b
             }
5f1c2b
         }