Skip to content

Commit

Permalink
Bug 1607449 - Lock cert->nssCertificate to prevent data race. r=jcj,k…
Browse files Browse the repository at this point in the history
…eeler

Differential Revision: https://phabricator.services.mozilla.com/D64233

--HG--
extra : moz-landing-system : lando
  • Loading branch information
Kevin Jacobs committed Nov 10, 2020
1 parent 6a2908b commit 43da06c
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 21 deletions.
49 changes: 34 additions & 15 deletions lib/certdb/certdb.c
Expand Up @@ -2908,16 +2908,27 @@ CERT_LockCertTrust(const CERTCertificate *cert)
PZ_Lock(certTrustLock);
}

static PZLock *certTempPermLock = NULL;
static PZLock *certTempPermCertLock = NULL;

/*
* Acquire the cert temp/perm lock
* Acquire the cert temp/perm/nssCert lock
*/
void
CERT_LockCertTempPerm(const CERTCertificate *cert)
{
PORT_Assert(certTempPermLock != NULL);
PZ_Lock(certTempPermLock);
PORT_Assert(certTempPermCertLock != NULL);
PZ_Lock(certTempPermCertLock);
}

/* Maybe[Lock, Unlock] variants are only to be used by
* CERT_DestroyCertificate, since an application could
* call this after NSS_Shutdown destroys cert locks. */
void
CERT_MaybeLockCertTempPerm(const CERTCertificate *cert)
{
if (certTempPermCertLock) {
PZ_Lock(certTempPermCertLock);
}
}

SECStatus
Expand All @@ -2941,10 +2952,10 @@ cert_InitLocks(void)
}
}

if (certTempPermLock == NULL) {
certTempPermLock = PZ_NewLock(nssILockCertDB);
PORT_Assert(certTempPermLock != NULL);
if (!certTempPermLock) {
if (certTempPermCertLock == NULL) {
certTempPermCertLock = PZ_NewLock(nssILockCertDB);
PORT_Assert(certTempPermCertLock != NULL);
if (!certTempPermCertLock) {
PZ_DestroyLock(certTrustLock);
PZ_DestroyLock(certRefCountLock);
certRefCountLock = NULL;
Expand Down Expand Up @@ -2977,10 +2988,10 @@ cert_DestroyLocks(void)
rv = SECFailure;
}

PORT_Assert(certTempPermLock != NULL);
if (certTempPermLock) {
PZ_DestroyLock(certTempPermLock);
certTempPermLock = NULL;
PORT_Assert(certTempPermCertLock != NULL);
if (certTempPermCertLock) {
PZ_DestroyLock(certTempPermCertLock);
certTempPermCertLock = NULL;
} else {
rv = SECFailure;
}
Expand All @@ -2999,16 +3010,24 @@ CERT_UnlockCertTrust(const CERTCertificate *cert)
}

/*
* Free the temp/perm lock
* Free the temp/perm/nssCert lock
*/
void
CERT_UnlockCertTempPerm(const CERTCertificate *cert)
{
PORT_Assert(certTempPermLock != NULL);
PRStatus prstat = PZ_Unlock(certTempPermLock);
PORT_Assert(certTempPermCertLock != NULL);
PRStatus prstat = PZ_Unlock(certTempPermCertLock);
PORT_AssertArg(prstat == PR_SUCCESS);
}

void
CERT_MaybeUnlockCertTempPerm(const CERTCertificate *cert)
{
if (certTempPermCertLock) {
PZ_Unlock(certTempPermCertLock);
}
}

/*
* Get the StatusConfig data for this handle
*/
Expand Down
17 changes: 15 additions & 2 deletions lib/certdb/stanpcertdb.c
Expand Up @@ -32,6 +32,9 @@
#include "dev.h"
#include "secmodi.h"

extern void CERT_MaybeLockCertTempPerm(const CERTCertificate *cert);
extern void CERT_MaybeUnlockCertTempPerm(const CERTCertificate *cert);

PRBool
SEC_CertNicknameConflict(const char *nickname, const SECItem *derSubject,
CERTCertDBHandle *handle)
Expand Down Expand Up @@ -311,7 +314,9 @@ __CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname,
nssPKIObject_AddInstance(&c->object, permInstance);
nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1);
/* reset the CERTCertificate fields */
CERT_LockCertTempPerm(cert);
cert->nssCertificate = NULL;
CERT_UnlockCertTempPerm(cert);
cert = STAN_GetCERTCertificateOrRelease(c); /* should return same pointer */
if (!cert) {
CERT_MapStanError();
Expand Down Expand Up @@ -808,9 +813,17 @@ CERT_DestroyCertificate(CERTCertificate *cert)
/* don't use STAN_GetNSSCertificate because we don't want to
* go to the trouble of translating the CERTCertificate into
* an NSSCertificate just to destroy it. If it hasn't been done
* yet, don't do it at all.
*/
* yet, don't do it at all
*
* cert->nssCertificate contains its own locks and refcount, but as it
* may be NULL, the pointer itself must be guarded by some other lock.
* Rather than creating a new global lock for only this purpose, share
* an existing global lock that happens to be taken near the write in
* fill_CERTCertificateFields(). The longer-term goal is to refactor
* all these global locks to be certificate-scoped. */
CERT_MaybeLockCertTempPerm(cert);
NSSCertificate *tmp = cert->nssCertificate;
CERT_MaybeUnlockCertTempPerm(cert);
if (tmp) {
/* delete the NSSCertificate */
NSSCertificate_Destroy(tmp);
Expand Down
7 changes: 5 additions & 2 deletions lib/pk11wrap/pk11cert.c
Expand Up @@ -1148,8 +1148,11 @@ PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert,
}

/* need to get the cert as a stan cert */
if (cert->nssCertificate) {
c = cert->nssCertificate;
CERT_LockCertTempPerm(cert);
NSSCertificate *nssCert = cert->nssCertificate;
CERT_UnlockCertTempPerm(cert);
if (nssCert) {
c = nssCert;
} else {
c = STAN_GetNSSCertificate(cert);
if (c == NULL) {
Expand Down
11 changes: 9 additions & 2 deletions lib/pki/pki3hack.c
Expand Up @@ -866,9 +866,9 @@ fill_CERTCertificateFields(NSSCertificate *c, CERTCertificate *cc, PRBool forced
CERT_LockCertTempPerm(cc);
cc->istemp = PR_FALSE; /* CERT_NewTemp will override this */
cc->isperm = PR_TRUE; /* by default */
CERT_UnlockCertTempPerm(cc);
/* pointer back */
cc->nssCertificate = c;
CERT_UnlockCertTempPerm(cc);
if (trust) {
/* force the cert type to be recomputed to include trust info */
PRUint32 nsCertType = cert_ComputeCertType(cc);
Expand Down Expand Up @@ -919,7 +919,10 @@ stan_GetCERTCertificate(NSSCertificate *c, PRBool forceUpdate)
nss_SetError(NSS_ERROR_INTERNAL_ERROR);
goto loser;
}
if (!cc->nssCertificate || forceUpdate) {
CERT_LockCertTempPerm(cc);
NSSCertificate *nssCert = cc->nssCertificate;
CERT_UnlockCertTempPerm(cc);
if (!nssCert || forceUpdate) {
fill_CERTCertificateFields(c, cc, forceUpdate);
} else if (CERT_GetCertTrust(cc, &certTrust) != SECSuccess) {
CERTCertTrust *trust;
Expand Down Expand Up @@ -1018,7 +1021,9 @@ STAN_GetNSSCertificate(CERTCertificate *cc)
nssCryptokiInstance *instance;
nssPKIObject *pkiob;
NSSArena *arena;
CERT_LockCertTempPerm(cc);
c = cc->nssCertificate;
CERT_UnlockCertTempPerm(cc);
if (c) {
return c;
}
Expand Down Expand Up @@ -1083,7 +1088,9 @@ STAN_GetNSSCertificate(CERTCertificate *cc)
nssPKIObject_AddInstance(&c->object, instance);
}
c->decoding = create_decoded_pkix_cert_from_nss3cert(NULL, cc);
CERT_LockCertTempPerm(cc);
cc->nssCertificate = c;
CERT_UnlockCertTempPerm(cc);
return c;
}

Expand Down

0 comments on commit 43da06c

Please sign in to comment.