Blame SOURCES/nss-softokn-3.67-read-write-db-locks.patch

0c41dc
diff -up ./lib/softoken/lgglue.c.read-write-db-locks ./lib/softoken/lgglue.c
0c41dc
--- ./lib/softoken/lgglue.c.read-write-db-locks	2023-06-04 10:42:53.000000000 +0200
0c41dc
+++ ./lib/softoken/lgglue.c	2023-06-21 10:59:44.940835332 +0200
0c41dc
@@ -184,13 +184,13 @@ sftkdb_encrypt_stub(PLArenaPool *arena,
5d145d
     }
5d145d
 
5d145d
     /* not a key handle */
5d145d
-    if (handle == NULL || handle->passwordLock == NULL) {
5d145d
+    if (handle == NULL || !sftkdb_passwordLockIsInited(handle)) {
5d145d
         return SECFailure;
5d145d
     }
5d145d
 
5d145d
-    PZ_Lock(handle->passwordLock);
5d145d
+    sftkdb_passwordReaderLock(handle);
5d145d
     if (handle->passwordKey.data == NULL) {
5d145d
-        PZ_Unlock(handle->passwordLock);
5d145d
+        sftkdb_passwordReaderUnlock(handle);
5d145d
         /* PORT_SetError */
5d145d
         return SECFailure;
5d145d
     }
0c41dc
@@ -208,7 +208,7 @@ sftkdb_encrypt_stub(PLArenaPool *arena,
5d145d
     rv = sftkdb_EncryptAttribute(arena, handle, sdb, key, iterationCount,
5d145d
                                  CK_INVALID_HANDLE, CKT_INVALID_TYPE,
5d145d
                                  plainText, cipherText);
5d145d
-    PZ_Unlock(handle->passwordLock);
5d145d
+    sftkdb_passwordReaderUnlock(handle);
5d145d
 
5d145d
     return rv;
5d145d
 }
0c41dc
@@ -235,13 +235,13 @@ sftkdb_decrypt_stub(SDB *sdb, SECItem *c
5d145d
     }
5d145d
 
5d145d
     /* not a key handle */
5d145d
-    if (handle == NULL || handle->passwordLock == NULL) {
5d145d
+    if (handle == NULL || !sftkdb_passwordLockIsInited(handle)) {
5d145d
         return SECFailure;
5d145d
     }
5d145d
 
5d145d
-    PZ_Lock(handle->passwordLock);
5d145d
+    sftkdb_passwordReaderLock(handle);
5d145d
     if (handle->passwordKey.data == NULL) {
5d145d
-        PZ_Unlock(handle->passwordLock);
5d145d
+        sftkdb_passwordReaderUnlock(handle);
5d145d
         /* PORT_SetError */
5d145d
         return SECFailure;
5d145d
     }
0c41dc
@@ -249,7 +249,7 @@ sftkdb_decrypt_stub(SDB *sdb, SECItem *c
5d145d
                                  CK_INVALID_HANDLE,
5d145d
                                  CKT_INVALID_TYPE,
5d145d
                                  cipherText, plainText);
5d145d
-    PZ_Unlock(handle->passwordLock);
5d145d
+    sftkdb_passwordReaderUnlock(handle);
5d145d
 
5d145d
     return rv;
5d145d
 }
0c41dc
diff -up ./lib/softoken/sftkdb.c.read-write-db-locks ./lib/softoken/sftkdb.c
0c41dc
--- ./lib/softoken/sftkdb.c.read-write-db-locks	2023-06-04 10:42:53.000000000 +0200
0c41dc
+++ ./lib/softoken/sftkdb.c	2023-06-21 12:54:41.007208477 +0200
0c41dc
@@ -413,9 +413,9 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *te
5d145d
 
5d145d
             cipherText.data = ntemplate[i].pValue;
5d145d
             cipherText.len = ntemplate[i].ulValueLen;
5d145d
-            PZ_Lock(handle->passwordLock);
5d145d
+            sftkdb_passwordReaderLock(handle);
5d145d
             if (handle->passwordKey.data == NULL) {
5d145d
-                PZ_Unlock(handle->passwordLock);
5d145d
+                sftkdb_passwordReaderUnlock(handle);
5d145d
                 template[i].ulValueLen = -1;
5d145d
                 crv = CKR_USER_NOT_LOGGED_IN;
5d145d
                 continue;
0c41dc
@@ -425,7 +425,7 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *te
5d145d
                                          objectID,
5d145d
                                          ntemplate[i].type,
5d145d
                                          &cipherText, &plainText);
5d145d
-            PZ_Unlock(handle->passwordLock);
5d145d
+            sftkdb_passwordReaderUnlock(handle);
5d145d
             if (rv != SECSuccess) {
5d145d
                 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
5d145d
                 template[i].ulValueLen = -1;
0c41dc
@@ -478,12 +478,12 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *te
5d145d
              * we do a second check holding the lock just in case the user
5d145d
              * loggout while we were trying to get the signature.
5d145d
              */
5d145d
-            PZ_Lock(keyHandle->passwordLock);
5d145d
+            sftkdb_passwordReaderLock(keyHandle);
5d145d
             if (keyHandle->passwordKey.data == NULL) {
5d145d
                 /* if we are no longer logged in, no use checking the other
5d145d
                  * Signatures either. */
5d145d
                 checkSig = PR_FALSE;
5d145d
-                PZ_Unlock(keyHandle->passwordLock);
5d145d
+                sftkdb_passwordReaderUnlock(keyHandle);
5d145d
                 continue;
5d145d
             }
5d145d
 
0c41dc
@@ -491,7 +491,7 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *te
5d145d
                                         &keyHandle->passwordKey,
5d145d
                                         objectID, ntemplate[i].type,
5d145d
                                         &plainText, &signText);
5d145d
-            PZ_Unlock(keyHandle->passwordLock);
5d145d
+            sftkdb_passwordReaderUnlock(keyHandle);
5d145d
             if (rv != SECSuccess) {
5d145d
                 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
5d145d
                 template[i].ulValueLen = -1;
0c41dc
@@ -590,9 +590,9 @@ sftk_signTemplate(PLArenaPool *arena, SF
5d145d
 
5d145d
             plainText.data = template[i].pValue;
5d145d
             plainText.len = template[i].ulValueLen;
5d145d
-            PZ_Lock(keyHandle->passwordLock);
5d145d
+            sftkdb_passwordReaderLock(keyHandle);
5d145d
             if (keyHandle->passwordKey.data == NULL) {
5d145d
-                PZ_Unlock(keyHandle->passwordLock);
5d145d
+                sftkdb_passwordReaderUnlock(keyHandle);
5d145d
                 crv = CKR_USER_NOT_LOGGED_IN;
5d145d
                 goto loser;
5d145d
             }
0c41dc
@@ -601,7 +601,7 @@ sftk_signTemplate(PLArenaPool *arena, SF
5d145d
                                       keyHandle->defaultIterationCount,
5d145d
                                       objectID, template[i].type,
5d145d
                                       &plainText, &signText);
5d145d
-            PZ_Unlock(keyHandle->passwordLock);
5d145d
+            sftkdb_passwordReaderUnlock(keyHandle);
5d145d
             if (rv != SECSuccess) {
5d145d
                 crv = CKR_GENERAL_ERROR; /* better error code here? */
5d145d
                 goto loser;
0c41dc
@@ -770,9 +770,9 @@ sftk_ExtractTemplate(PLArenaPool *arena,
5d145d
 
5d145d
                 plainText.data = tp->pValue;
5d145d
                 plainText.len = tp->ulValueLen;
5d145d
-                PZ_Lock(handle->passwordLock);
5d145d
+                sftkdb_passwordReaderLock(handle);
5d145d
                 if (handle->passwordKey.data == NULL) {
5d145d
-                    PZ_Unlock(handle->passwordLock);
5d145d
+                    sftkdb_passwordReaderUnlock(handle);
5d145d
                     *crv = CKR_USER_NOT_LOGGED_IN;
5d145d
                     break;
5d145d
                 }
0c41dc
@@ -782,7 +782,7 @@ sftk_ExtractTemplate(PLArenaPool *arena,
5d145d
                                              objectID,
5d145d
                                              tp->type,
5d145d
                                              &plainText, &cipherText);
5d145d
-                PZ_Unlock(handle->passwordLock);
5d145d
+                sftkdb_passwordReaderUnlock(handle);
5d145d
                 if (rv == SECSuccess) {
5d145d
                     tp->pValue = cipherText->data;
5d145d
                     tp->ulValueLen = cipherText->len;
0c41dc
@@ -1638,15 +1638,15 @@ sftkdb_CloseDB(SFTKDBHandle *handle)
5d145d
         }
5d145d
         (*handle->db->sdb_Close)(handle->db);
5d145d
     }
0c41dc
-    if (handle->passwordLock) {
0c41dc
-        PZ_Lock(handle->passwordLock);
0c41dc
+    if (sftkdb_passwordLockIsInited(handle)) {
0c41dc
+        sftkdb_passwordWriterLock(handle);
0c41dc
     }
5d145d
     if (handle->passwordKey.data) {
5d145d
         SECITEM_ZfreeItem(&handle->passwordKey, PR_FALSE);
5d145d
     }
5d145d
-    if (handle->passwordLock) {
0c41dc
-        PZ_Unlock(handle->passwordLock);
5d145d
-        SKIP_AFTER_FORK(PZ_DestroyLock(handle->passwordLock));
5d145d
+    if (sftkdb_passwordLockIsInited(handle)) {
0c41dc
+        sftkdb_passwordWriterUnlock(handle);
5d145d
+        SKIP_AFTER_FORK(sftkdb_passwordLockDestroy(handle));
5d145d
     }
5d145d
     if (handle->updatePasswordKey) {
5d145d
         SECITEM_ZfreeItem(handle->updatePasswordKey, PR_TRUE);
0c41dc
@@ -2704,8 +2704,10 @@ sftk_NewDBHandle(SDB *sdb, int type, PRB
5d145d
     handle->passwordKey.data = NULL;
5d145d
     handle->passwordKey.len = 0;
5d145d
     handle->passwordLock = NULL;
5d145d
+    handle->passwordWriterCond = NULL;
5d145d
+    handle->passwordReaderCond = NULL;
5d145d
     if (type == SFTK_KEYDB_TYPE) {
5d145d
-        handle->passwordLock = PZ_NewLock(nssILockAttribute);
5d145d
+        (void) sftkdb_passwordLockInit(handle);
5d145d
     }
5d145d
     sdb->app_private = handle;
5d145d
     return handle;
0c41dc
diff -up ./lib/softoken/sftkdbti.h.read-write-db-locks ./lib/softoken/sftkdbti.h
0c41dc
--- ./lib/softoken/sftkdbti.h.read-write-db-locks	2023-06-04 10:42:53.000000000 +0200
0c41dc
+++ ./lib/softoken/sftkdbti.h	2023-06-21 10:59:44.940835332 +0200
0c41dc
@@ -4,6 +4,7 @@
5d145d
 
5d145d
 #ifndef SFTKDBTI_H
5d145d
 #define SFTKDBTI_H 1
5d145d
+#include <prcvar.h>
5d145d
 
5d145d
 /*
5d145d
  * private defines
0c41dc
@@ -19,6 +20,11 @@ struct SFTKDBHandleStr {
5d145d
     SECItem *oldKey;
5d145d
     SECItem *updatePasswordKey;
5d145d
     PZLock *passwordLock;
5d145d
+    PRCondVar *passwordWriterCond;
5d145d
+    PRCondVar *passwordReaderCond;
5d145d
+    PRBool passwordWriterActive;
5d145d
+    int passwordWriters;
5d145d
+    int passwordReaders;
5d145d
     SFTKDBHandle *peerDB;
5d145d
     SDB *update;
5d145d
     char *updateID;
0c41dc
@@ -79,4 +85,13 @@ sftkdb_DestroyAttributeSignature(SFTKDBH
5d145d
                                  CK_OBJECT_HANDLE objectID,
5d145d
                                  CK_ATTRIBUTE_TYPE type);
5d145d
 
5d145d
+/* password lock functions */
5d145d
+SECStatus sftkdb_passwordLockInit(SFTKDBHandle *keydb);
5d145d
+void sftkdb_passwordLockDestroy(SFTKDBHandle *keydb);
5d145d
+PRBool sftkdb_passwordLockIsInited(SFTKDBHandle *keydb);
5d145d
+void sftkdb_passwordReaderLock(SFTKDBHandle *keydb);
5d145d
+void sftkdb_passwordWriterLock(SFTKDBHandle *keydb);
5d145d
+void sftkdb_passwordReaderUnlock(SFTKDBHandle *keydb);
5d145d
+void sftkdb_passwordWriterUnlock(SFTKDBHandle *keydb);
5d145d
+
5d145d
 #endif
0c41dc
diff -up ./lib/softoken/sftkpwd.c.read-write-db-locks ./lib/softoken/sftkpwd.c
0c41dc
--- ./lib/softoken/sftkpwd.c.read-write-db-locks	2023-06-04 10:42:53.000000000 +0200
0c41dc
+++ ./lib/softoken/sftkpwd.c	2023-06-21 10:59:44.940835332 +0200
0c41dc
@@ -632,6 +632,126 @@ loser:
5d145d
     return rv;
5d145d
 }
5d145d
 
5d145d
+SECStatus
5d145d
+sftkdb_passwordLockInit(SFTKDBHandle *keydb)
5d145d
+{
5d145d
+    keydb->passwordLock = PZ_NewLock(nssILockAttribute);
5d145d
+    if (keydb->passwordLock == NULL) {
5d145d
+        return SECFailure;
5d145d
+    }
5d145d
+    keydb->passwordWriterCond = PR_NewCondVar(keydb->passwordLock);
5d145d
+    if (keydb->passwordWriterCond == NULL) {
5d145d
+        PZ_DestroyLock(keydb->passwordLock);
5d145d
+        keydb->passwordLock = NULL;
5d145d
+        return SECFailure;
5d145d
+    }
5d145d
+    keydb->passwordReaderCond = PR_NewCondVar(keydb->passwordLock);
5d145d
+    if (keydb->passwordReaderCond == NULL) {
5d145d
+        PR_DestroyCondVar(keydb->passwordWriterCond);
5d145d
+        PZ_DestroyLock(keydb->passwordLock);
5d145d
+        keydb->passwordWriterCond = NULL;
5d145d
+        keydb->passwordLock = NULL;
5d145d
+        return SECFailure;
5d145d
+    }
5d145d
+    keydb->passwordWriters = 0;
5d145d
+    keydb->passwordReaders = 0;
5d145d
+    keydb->passwordWriterActive = PR_FALSE;
5d145d
+    return SECSuccess;
5d145d
+}
5d145d
+
5d145d
+void
5d145d
+sftkdb_passwordLockDestroy(SFTKDBHandle *keydb)
5d145d
+{
5d145d
+    PR_DestroyCondVar(keydb->passwordWriterCond);
5d145d
+    keydb->passwordWriterCond = NULL;
5d145d
+    PR_DestroyCondVar(keydb->passwordReaderCond);
5d145d
+    keydb->passwordReaderCond = NULL;
5d145d
+    PZ_DestroyLock(keydb->passwordLock);
5d145d
+    keydb->passwordLock = NULL;
5d145d
+}
5d145d
+
5d145d
+PRBool
5d145d
+sftkdb_passwordLockIsInited(SFTKDBHandle *keydb)
5d145d
+{
5d145d
+    return (keydb->passwordLock) && (keydb->passwordWriterCond)
5d145d
+            && (keydb->passwordReaderCond);
5d145d
+}
5d145d
+
5d145d
+/* we need reader/writer locks for the database because we have servers
5d145d
+ * that use the database key in their transactions on multi-cpu systems.
5d145d
+ * This means that all the other cpus doing private key operations become
5d145d
+ * effectively single threaded. We implement our specific reader/writer
5d145d
+ * locks because we want writer locks to interrupt reader streams to
5d145d
+ * prevent starvation on the writer side. */
5d145d
+void
5d145d
+sftkdb_passwordReaderLock(SFTKDBHandle *keydb)
5d145d
+{
5d145d
+    PZ_Lock(keydb->passwordLock);
5d145d
+    /* if there is a writer waiting or running wait until the
5d145d
+     * writer has completed before we continue. This prevents
5d145d
+     * writer starvation in a loaded system */
5d145d
+    while (keydb->passwordWriters) {
5d145d
+	PR_WaitCondVar(keydb->passwordReaderCond, PR_INTERVAL_NO_TIMEOUT);
5d145d
+    }
5d145d
+    /* allow multiple readers */
5d145d
+    keydb->passwordReaders++;
5d145d
+    PORT_Assert(keydb->passwordWriterActive == PR_FALSE);
5d145d
+    PZ_Unlock(keydb->passwordLock);
5d145d
+}
5d145d
+
5d145d
+/* We can only have 1 active writer or multiple active readers.
5d145d
+ * Readers will wait for an active writer. This means lots of write
5d145d
+ * operations can stall the readers. Write operations are rare, though and
5d145d
+ * only happen on database update, or on initial login.
5d145d
+ */
5d145d
+void
5d145d
+sftkdb_passwordWriterLock(SFTKDBHandle *keydb)
5d145d
+{
5d145d
+    PZ_Lock(keydb->passwordLock);
5d145d
+    keydb->passwordWriters++;
5d145d
+    /* if we have any readers or writers, wait for the writer condition */
5d145d
+    while (keydb->passwordReaders || keydb->passwordWriterActive) {
5d145d
+	PR_WaitCondVar(keydb->passwordWriterCond, PR_INTERVAL_NO_TIMEOUT);
5d145d
+    }
5d145d
+    /* only one writer allowed at a time */
5d145d
+    keydb->passwordWriterActive = PR_TRUE;
5d145d
+    PZ_Unlock(keydb->passwordLock);
5d145d
+}
5d145d
+
5d145d
+/* unlock, decrements the reader counter, and if we have not active readers,
5d145d
+ * notify any waiting writers. */
5d145d
+void
5d145d
+sftkdb_passwordReaderUnlock(SFTKDBHandle *keydb)
5d145d
+{
5d145d
+    PZ_Lock(keydb->passwordLock);
5d145d
+    PORT_Assert(keydb->passwordReaders);
5d145d
+    keydb->passwordReaders--;
5d145d
+    if (keydb->passwordReaders == 0) {
5d145d
+        if (keydb->passwordWriters) {
5d145d
+            PR_NotifyCondVar(keydb->passwordWriterCond);
5d145d
+        }
5d145d
+    }
5d145d
+    PZ_Unlock(keydb->passwordLock);
5d145d
+}
5d145d
+
5d145d
+/* unlock, if there are more writers, wake one of them up. If there or no more
5d145d
+ * writers but readers, wake up all the readers. */
5d145d
+void
5d145d
+sftkdb_passwordWriterUnlock(SFTKDBHandle *keydb)
5d145d
+{
5d145d
+    PZ_Lock(keydb->passwordLock);
5d145d
+    PORT_Assert(keydb->passwordWriters);
5d145d
+    PORT_Assert(keydb->passwordWriterActive);
5d145d
+    keydb->passwordWriterActive = PR_FALSE;
5d145d
+    keydb->passwordWriters--;
5d145d
+    if (keydb->passwordWriters) {
5d145d
+        PR_NotifyCondVar(keydb->passwordWriterCond);
5d145d
+    } else {
5d145d
+        PR_NotifyAllCondVar(keydb->passwordReaderCond);
5d145d
+    }
5d145d
+    PZ_Unlock(keydb->passwordLock);
5d145d
+}
5d145d
+
5d145d
 /*
5d145d
  * safely swith the passed in key for the one caches in the keydb handle
5d145d
  *
0c41dc
@@ -645,13 +765,13 @@ sftkdb_switchKeys(SFTKDBHandle *keydb, S
5d145d
     unsigned char *data;
5d145d
     int len;
5d145d
 
5d145d
-    if (keydb->passwordLock == NULL) {
5d145d
+    if (!sftkdb_passwordLockIsInited(keydb)) {
5d145d
         PORT_Assert(keydb->type != SFTK_KEYDB_TYPE);
5d145d
         return;
5d145d
     }
5d145d
 
5d145d
     /* an atomic pointer set would be nice */
5d145d
-    SKIP_AFTER_FORK(PZ_Lock(keydb->passwordLock));
5d145d
+    SKIP_AFTER_FORK(sftkdb_passwordWriterLock(keydb));
5d145d
     data = keydb->passwordKey.data;
5d145d
     len = keydb->passwordKey.len;
5d145d
     keydb->passwordKey.data = passKey->data;
0c41dc
@@ -659,7 +779,7 @@ sftkdb_switchKeys(SFTKDBHandle *keydb, S
5d145d
     keydb->defaultIterationCount = iterationCount;
5d145d
     passKey->data = data;
5d145d
     passKey->len = len;
5d145d
-    SKIP_AFTER_FORK(PZ_Unlock(keydb->passwordLock));
5d145d
+    SKIP_AFTER_FORK(sftkdb_passwordWriterUnlock(keydb));
5d145d
 }
5d145d
 
5d145d
 /*
0c41dc
@@ -705,11 +825,11 @@ sftkdb_GetUpdatePasswordKey(SFTKDBHandle
5d145d
         return NULL;
5d145d
     }
5d145d
 
5d145d
-    PZ_Lock(handle->passwordLock);
5d145d
+    sftkdb_passwordReaderLock(handle);
5d145d
     if (handle->updatePasswordKey) {
5d145d
         key = SECITEM_DupItem(handle->updatePasswordKey);
5d145d
     }
5d145d
-    PZ_Unlock(handle->passwordLock);
5d145d
+    sftkdb_passwordReaderUnlock(handle);
5d145d
 
5d145d
     return key;
5d145d
 }
0c41dc
@@ -732,12 +852,12 @@ sftkdb_FreeUpdatePasswordKey(SFTKDBHandl
5d145d
         return;
5d145d
     }
5d145d
 
5d145d
-    PZ_Lock(handle->passwordLock);
5d145d
+    sftkdb_passwordWriterLock(handle);
5d145d
     if (handle->updatePasswordKey) {
5d145d
         key = handle->updatePasswordKey;
5d145d
         handle->updatePasswordKey = NULL;
5d145d
     }
5d145d
-    PZ_Unlock(handle->passwordLock);
5d145d
+    sftkdb_passwordWriterUnlock(handle);
5d145d
 
5d145d
     if (key) {
5d145d
         SECITEM_ZfreeItem(key, PR_TRUE);
0c41dc
@@ -1004,7 +1124,7 @@ sftkdb_finishPasswordCheck(SFTKDBHandle
5d145d
          *         update, as we now have both required passwords.
5d145d
          *
5d145d
          */
5d145d
-        PZ_Lock(keydb->passwordLock);
5d145d
+        sftkdb_passwordWriterLock(keydb);
5d145d
         if (sftkdb_NeedUpdateDBPassword(keydb)) {
5d145d
             /* Squirrel this special key away.
5d145d
              * This has the side effect of turning sftkdb_NeedLegacyPW off,
0c41dc
@@ -1012,7 +1132,7 @@ sftkdb_finishPasswordCheck(SFTKDBHandle
5d145d
              * SFTK_GET_PW_DB (thus effecting both sftkdb_CheckPassword()
5d145d
              * and sftkdb_HasPasswordSet()) */
5d145d
             keydb->updatePasswordKey = SECITEM_DupItem(key);
5d145d
-            PZ_Unlock(keydb->passwordLock);
5d145d
+            sftkdb_passwordWriterUnlock(keydb);
5d145d
             if (keydb->updatePasswordKey == NULL) {
5d145d
                 /* PORT_Error set by SECITEM_DupItem */
5d145d
                 rv = SECFailure;
0c41dc
@@ -1077,7 +1197,7 @@ sftkdb_finishPasswordCheck(SFTKDBHandle
5d145d
                  * update case. */
5d145d
             }
5d145d
         } else {
5d145d
-            PZ_Unlock(keydb->passwordLock);
5d145d
+            sftkdb_passwordWriterUnlock(keydb);
5d145d
         }
5d145d
         /* load the keys, so the keydb can parse it's key set */
5d145d
         sftkdb_switchKeys(keydb, key, iterationCount);