diff options
Diffstat (limited to 'lib/ssl/sslmutex.c')
-rw-r--r-- | lib/ssl/sslmutex.c | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/lib/ssl/sslmutex.c b/lib/ssl/sslmutex.c new file mode 100644 index 000000000..a9f60d9b4 --- /dev/null +++ b/lib/ssl/sslmutex.c @@ -0,0 +1,641 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* $Id$ */ + +#include "seccomon.h" +/* This ifdef should match the one in sslsnce.c */ +#if defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS) + +#include "sslmutex.h" +#include "prerr.h" + +static SECStatus single_process_sslMutex_Init(sslMutex* pMutex) +{ + PR_ASSERT(pMutex != 0 && pMutex->u.sslLock == 0 ); + + pMutex->u.sslLock = PR_NewLock(); + if (!pMutex->u.sslLock) { + return SECFailure; + } + return SECSuccess; +} + +static SECStatus single_process_sslMutex_Destroy(sslMutex* pMutex) +{ + PR_ASSERT(pMutex != 0); + PR_ASSERT(pMutex->u.sslLock!= 0); + if (!pMutex->u.sslLock) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + PR_DestroyLock(pMutex->u.sslLock); + return SECSuccess; +} + +static SECStatus single_process_sslMutex_Unlock(sslMutex* pMutex) +{ + PR_ASSERT(pMutex != 0 ); + PR_ASSERT(pMutex->u.sslLock !=0); + if (!pMutex->u.sslLock) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + PR_Unlock(pMutex->u.sslLock); + return SECSuccess; +} + +static SECStatus single_process_sslMutex_Lock(sslMutex* pMutex) +{ + PR_ASSERT(pMutex != 0); + PR_ASSERT(pMutex->u.sslLock != 0 ); + if (!pMutex->u.sslLock) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + PR_Lock(pMutex->u.sslLock); + return SECSuccess; +} + +#if defined(LINUX) || defined(AIX) || defined(BEOS) || defined(BSDI) || (defined(NETBSD) && __NetBSD_Version__ < 500000000) || defined(OPENBSD) + +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include "unix_err.h" +#include "pratom.h" + +#define SSL_MUTEX_MAGIC 0xfeedfd +#define NONBLOCKING_POSTS 1 /* maybe this is faster */ + +#if NONBLOCKING_POSTS + +#ifndef FNONBLOCK +#define FNONBLOCK O_NONBLOCK +#endif + +static int +setNonBlocking(int fd, int nonBlocking) +{ + int flags; + int err; + + flags = fcntl(fd, F_GETFL, 0); + if (0 > flags) + return flags; + if (nonBlocking) + flags |= FNONBLOCK; + else + flags &= ~FNONBLOCK; + err = fcntl(fd, F_SETFL, flags); + return err; +} +#endif + +SECStatus +sslMutex_Init(sslMutex *pMutex, int shared) +{ + int err; + PR_ASSERT(pMutex); + pMutex->isMultiProcess = (PRBool)(shared != 0); + if (!shared) { + return single_process_sslMutex_Init(pMutex); + } + pMutex->u.pipeStr.mPipes[0] = -1; + pMutex->u.pipeStr.mPipes[1] = -1; + pMutex->u.pipeStr.mPipes[2] = -1; + pMutex->u.pipeStr.nWaiters = 0; + + err = pipe(pMutex->u.pipeStr.mPipes); + if (err) { + nss_MD_unix_map_default_error(errno); + return err; + } +#if NONBLOCKING_POSTS + err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1); + if (err) + goto loser; +#endif + + pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC; + +#if defined(LINUX) && defined(i386) + /* Pipe starts out empty */ + return SECSuccess; +#else + /* Pipe starts with one byte. */ + return sslMutex_Unlock(pMutex); +#endif + +loser: + nss_MD_unix_map_default_error(errno); + close(pMutex->u.pipeStr.mPipes[0]); + close(pMutex->u.pipeStr.mPipes[1]); + return SECFailure; +} + +SECStatus +sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal) +{ + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Destroy(pMutex); + } + if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + close(pMutex->u.pipeStr.mPipes[0]); + close(pMutex->u.pipeStr.mPipes[1]); + + if (processLocal) { + return SECSuccess; + } + + pMutex->u.pipeStr.mPipes[0] = -1; + pMutex->u.pipeStr.mPipes[1] = -1; + pMutex->u.pipeStr.mPipes[2] = -1; + pMutex->u.pipeStr.nWaiters = 0; + + return SECSuccess; +} + +#if defined(LINUX) && defined(i386) +/* No memory barrier needed for this platform */ + +/* nWaiters includes the holder of the lock (if any) and the number +** threads waiting for it. After incrementing nWaiters, if the count +** is exactly 1, then you have the lock and may proceed. If the +** count is greater than 1, then you must wait on the pipe. +*/ + + +SECStatus +sslMutex_Unlock(sslMutex *pMutex) +{ + PRInt32 newValue; + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Unlock(pMutex); + } + + if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + /* Do Memory Barrier here. */ + newValue = PR_ATOMIC_DECREMENT(&pMutex->u.pipeStr.nWaiters); + if (newValue > 0) { + int cc; + char c = 1; + do { + cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1); + } while (cc < 0 && (errno == EINTR || errno == EAGAIN)); + if (cc != 1) { + if (cc < 0) + nss_MD_unix_map_default_error(errno); + else + PORT_SetError(PR_UNKNOWN_ERROR); + return SECFailure; + } + } + return SECSuccess; +} + +SECStatus +sslMutex_Lock(sslMutex *pMutex) +{ + PRInt32 newValue; + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Lock(pMutex); + } + + if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + newValue = PR_ATOMIC_INCREMENT(&pMutex->u.pipeStr.nWaiters); + /* Do Memory Barrier here. */ + if (newValue > 1) { + int cc; + char c; + do { + cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1); + } while (cc < 0 && errno == EINTR); + if (cc != 1) { + if (cc < 0) + nss_MD_unix_map_default_error(errno); + else + PORT_SetError(PR_UNKNOWN_ERROR); + return SECFailure; + } + } + return SECSuccess; +} + +#else + +/* Using Atomic operations requires the use of a memory barrier instruction +** on PowerPC, Sparc, and Alpha. NSPR's PR_Atomic functions do not perform +** them, and NSPR does not provide a function that does them (e.g. PR_Barrier). +** So, we don't use them on those platforms. +*/ + +SECStatus +sslMutex_Unlock(sslMutex *pMutex) +{ + int cc; + char c = 1; + + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Unlock(pMutex); + } + + if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + do { + cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1); + } while (cc < 0 && (errno == EINTR || errno == EAGAIN)); + if (cc != 1) { + if (cc < 0) + nss_MD_unix_map_default_error(errno); + else + PORT_SetError(PR_UNKNOWN_ERROR); + return SECFailure; + } + + return SECSuccess; +} + +SECStatus +sslMutex_Lock(sslMutex *pMutex) +{ + int cc; + char c; + + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Lock(pMutex); + } + + if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + + do { + cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1); + } while (cc < 0 && errno == EINTR); + if (cc != 1) { + if (cc < 0) + nss_MD_unix_map_default_error(errno); + else + PORT_SetError(PR_UNKNOWN_ERROR); + return SECFailure; + } + + return SECSuccess; +} + +#endif + +#elif defined(WIN32) + +#include "win32err.h" + +/* on Windows, we need to find the optimal type of locking mechanism to use + for the sslMutex. + + There are 3 cases : + 1) single-process, use a PRLock, as for all other platforms + 2) Win95 multi-process, use a Win32 mutex + 3) on WINNT multi-process, use a PRLock + a Win32 mutex + +*/ + +#ifdef WINNT + +SECStatus sslMutex_2LevelInit(sslMutex *sem) +{ + /* the following adds a PRLock to sslMutex . This is done in each + process of a multi-process server and is only needed on WINNT, if + using fibers. We can't tell if native threads or fibers are used, so + we always do it on WINNT + */ + PR_ASSERT(sem); + if (sem) { + /* we need to reset the sslLock in the children or the single_process init + function below will assert */ + sem->u.sslLock = NULL; + } + return single_process_sslMutex_Init(sem); +} + +static SECStatus sslMutex_2LevelDestroy(sslMutex *sem) +{ + return single_process_sslMutex_Destroy(sem); +} + +#endif + +SECStatus +sslMutex_Init(sslMutex *pMutex, int shared) +{ +#ifdef WINNT + SECStatus retvalue; +#endif + HANDLE hMutex; + SECURITY_ATTRIBUTES attributes = + { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + + PR_ASSERT(pMutex != 0 && (pMutex->u.sslMutx == 0 || + pMutex->u.sslMutx == INVALID_HANDLE_VALUE) ); + + pMutex->isMultiProcess = (PRBool)(shared != 0); + + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Init(pMutex); + } + +#ifdef WINNT + /* we need a lock on WINNT for fibers in the parent process */ + retvalue = sslMutex_2LevelInit(pMutex); + if (SECSuccess != retvalue) + return SECFailure; +#endif + + if (!pMutex || ((hMutex = pMutex->u.sslMutx) != 0 && + hMutex != INVALID_HANDLE_VALUE)) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + attributes.bInheritHandle = (shared ? TRUE : FALSE); + hMutex = CreateMutex(&attributes, FALSE, NULL); + if (hMutex == NULL) { + hMutex = INVALID_HANDLE_VALUE; + nss_MD_win32_map_default_error(GetLastError()); + return SECFailure; + } + pMutex->u.sslMutx = hMutex; + return SECSuccess; +} + +SECStatus +sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal) +{ + HANDLE hMutex; + int rv; + int retvalue = SECSuccess; + + PR_ASSERT(pMutex != 0); + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Destroy(pMutex); + } + + /* multi-process mode */ +#ifdef WINNT + /* on NT, get rid of the PRLock used for fibers within a process */ + retvalue = sslMutex_2LevelDestroy(pMutex); +#endif + + PR_ASSERT( pMutex->u.sslMutx != 0 && + pMutex->u.sslMutx != INVALID_HANDLE_VALUE); + if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 + || hMutex == INVALID_HANDLE_VALUE) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + + rv = CloseHandle(hMutex); /* ignore error */ + if (!processLocal && rv) { + pMutex->u.sslMutx = hMutex = INVALID_HANDLE_VALUE; + } + if (!rv) { + nss_MD_win32_map_default_error(GetLastError()); + retvalue = SECFailure; + } + return retvalue; +} + +int +sslMutex_Unlock(sslMutex *pMutex) +{ + BOOL success = FALSE; + HANDLE hMutex; + + PR_ASSERT(pMutex != 0 ); + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Unlock(pMutex); + } + + PR_ASSERT(pMutex->u.sslMutx != 0 && + pMutex->u.sslMutx != INVALID_HANDLE_VALUE); + if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 || + hMutex == INVALID_HANDLE_VALUE) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + success = ReleaseMutex(hMutex); + if (!success) { + nss_MD_win32_map_default_error(GetLastError()); + return SECFailure; + } +#ifdef WINNT + return single_process_sslMutex_Unlock(pMutex); + /* release PRLock for other fibers in the process */ +#else + return SECSuccess; +#endif +} + +int +sslMutex_Lock(sslMutex *pMutex) +{ + HANDLE hMutex; + DWORD event; + DWORD lastError; + SECStatus rv; + SECStatus retvalue = SECSuccess; + PR_ASSERT(pMutex != 0); + + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Lock(pMutex); + } +#ifdef WINNT + /* lock first to preserve from other threads/fibers + in the same process */ + retvalue = single_process_sslMutex_Lock(pMutex); +#endif + PR_ASSERT(pMutex->u.sslMutx != 0 && + pMutex->u.sslMutx != INVALID_HANDLE_VALUE); + if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 || + hMutex == INVALID_HANDLE_VALUE) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; /* what else ? */ + } + /* acquire the mutex to be the only owner accross all other processes */ + event = WaitForSingleObject(hMutex, INFINITE); + switch (event) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + rv = SECSuccess; + break; + + case WAIT_TIMEOUT: +#if defined(WAIT_IO_COMPLETION) + case WAIT_IO_COMPLETION: +#endif + default: /* should never happen. nothing we can do. */ + PR_ASSERT(!("WaitForSingleObject returned invalid value.")); + PORT_SetError(PR_UNKNOWN_ERROR); + rv = SECFailure; + break; + + case WAIT_FAILED: /* failure returns this */ + rv = SECFailure; + lastError = GetLastError(); /* for debugging */ + nss_MD_win32_map_default_error(lastError); + break; + } + + if (! (SECSuccess == retvalue && SECSuccess == rv)) { + return SECFailure; + } + + return SECSuccess; +} + +#elif defined(XP_UNIX) + +#include <errno.h> +#include "unix_err.h" + +SECStatus +sslMutex_Init(sslMutex *pMutex, int shared) +{ + int rv; + PR_ASSERT(pMutex); + pMutex->isMultiProcess = (PRBool)(shared != 0); + if (!shared) { + return single_process_sslMutex_Init(pMutex); + } + do { + rv = sem_init(&pMutex->u.sem, shared, 1); + } while (rv < 0 && errno == EINTR); + if (rv < 0) { + nss_MD_unix_map_default_error(errno); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal) +{ + int rv; + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Destroy(pMutex); + } + + /* semaphores are global resources. See SEM_DESTROY(3) man page */ + if (processLocal) { + return SECSuccess; + } + do { + rv = sem_destroy(&pMutex->u.sem); + } while (rv < 0 && errno == EINTR); + if (rv < 0) { + nss_MD_unix_map_default_error(errno); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +sslMutex_Unlock(sslMutex *pMutex) +{ + int rv; + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Unlock(pMutex); + } + do { + rv = sem_post(&pMutex->u.sem); + } while (rv < 0 && errno == EINTR); + if (rv < 0) { + nss_MD_unix_map_default_error(errno); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +sslMutex_Lock(sslMutex *pMutex) +{ + int rv; + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Lock(pMutex); + } + do { + rv = sem_wait(&pMutex->u.sem); + } while (rv < 0 && errno == EINTR); + if (rv < 0) { + nss_MD_unix_map_default_error(errno); + return SECFailure; + } + return SECSuccess; +} + +#else + +SECStatus +sslMutex_Init(sslMutex *pMutex, int shared) +{ + PR_ASSERT(pMutex); + pMutex->isMultiProcess = (PRBool)(shared != 0); + if (!shared) { + return single_process_sslMutex_Init(pMutex); + } + PORT_Assert(!("sslMutex_Init not implemented for multi-process applications !")); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +SECStatus +sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal) +{ + PR_ASSERT(pMutex); + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Destroy(pMutex); + } + PORT_Assert(!("sslMutex_Destroy not implemented for multi-process applications !")); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +SECStatus +sslMutex_Unlock(sslMutex *pMutex) +{ + PR_ASSERT(pMutex); + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Unlock(pMutex); + } + PORT_Assert(!("sslMutex_Unlock not implemented for multi-process applications !")); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +SECStatus +sslMutex_Lock(sslMutex *pMutex) +{ + PR_ASSERT(pMutex); + if (PR_FALSE == pMutex->isMultiProcess) { + return single_process_sslMutex_Lock(pMutex); + } + PORT_Assert(!("sslMutex_Lock not implemented for multi-process applications !")); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +#endif + +#endif |