tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

sslmutex.c (16781B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "seccomon.h"
      6 /* This ifdef should match the one in sslsnce.c */
      7 #if defined(XP_UNIX) || defined(XP_WIN32)
      8 
      9 #include "sslmutex.h"
     10 #include "prerr.h"
     11 
     12 static SECStatus
     13 single_process_sslMutex_Init(sslMutex* pMutex)
     14 {
     15    PR_ASSERT(pMutex != 0 && pMutex->u.sslLock == 0);
     16 
     17    pMutex->u.sslLock = PR_NewLock();
     18    if (!pMutex->u.sslLock) {
     19        return SECFailure;
     20    }
     21    return SECSuccess;
     22 }
     23 
     24 static SECStatus
     25 single_process_sslMutex_Destroy(sslMutex* pMutex)
     26 {
     27    PR_ASSERT(pMutex != 0);
     28    PR_ASSERT(pMutex->u.sslLock != 0);
     29    if (!pMutex->u.sslLock) {
     30        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
     31        return SECFailure;
     32    }
     33    PR_DestroyLock(pMutex->u.sslLock);
     34    return SECSuccess;
     35 }
     36 
     37 static SECStatus
     38 single_process_sslMutex_Unlock(sslMutex* pMutex)
     39 {
     40    PR_ASSERT(pMutex != 0);
     41    PR_ASSERT(pMutex->u.sslLock != 0);
     42    if (!pMutex->u.sslLock) {
     43        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
     44        return SECFailure;
     45    }
     46    PR_Unlock(pMutex->u.sslLock);
     47    return SECSuccess;
     48 }
     49 
     50 static SECStatus
     51 single_process_sslMutex_Lock(sslMutex* pMutex)
     52 {
     53    PR_ASSERT(pMutex != 0);
     54    PR_ASSERT(pMutex->u.sslLock != 0);
     55    if (!pMutex->u.sslLock) {
     56        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
     57        return SECFailure;
     58    }
     59    PR_Lock(pMutex->u.sslLock);
     60    return SECSuccess;
     61 }
     62 
     63 #if defined(LINUX) || defined(AIX) || defined(BSDI) || \
     64    (defined(NETBSD) && __NetBSD_Version__ < 500000000) || defined(OPENBSD) || defined(__GLIBC__)
     65 
     66 #include <unistd.h>
     67 #include <fcntl.h>
     68 #include <string.h>
     69 #include <errno.h>
     70 #include "unix_err.h"
     71 #include "pratom.h"
     72 
     73 #define SSL_MUTEX_MAGIC 0xfeedfd
     74 #define NONBLOCKING_POSTS 1 /* maybe this is faster */
     75 
     76 #if NONBLOCKING_POSTS
     77 
     78 #ifndef FNONBLOCK
     79 #define FNONBLOCK O_NONBLOCK
     80 #endif
     81 
     82 static int
     83 setNonBlocking(int fd, int nonBlocking)
     84 {
     85    int flags;
     86    int err;
     87 
     88    flags = fcntl(fd, F_GETFL, 0);
     89    if (0 > flags)
     90        return flags;
     91    if (nonBlocking)
     92        flags |= FNONBLOCK;
     93    else
     94        flags &= ~FNONBLOCK;
     95    err = fcntl(fd, F_SETFL, flags);
     96    return err;
     97 }
     98 #endif
     99 
    100 SECStatus
    101 sslMutex_Init(sslMutex* pMutex, int shared)
    102 {
    103    int err;
    104    PR_ASSERT(pMutex);
    105    pMutex->isMultiProcess = (PRBool)(shared != 0);
    106    if (!shared) {
    107        return single_process_sslMutex_Init(pMutex);
    108    }
    109    pMutex->u.pipeStr.mPipes[0] = -1;
    110    pMutex->u.pipeStr.mPipes[1] = -1;
    111    pMutex->u.pipeStr.mPipes[2] = -1;
    112    pMutex->u.pipeStr.nWaiters = 0;
    113 
    114    err = pipe(pMutex->u.pipeStr.mPipes);
    115    if (err) {
    116        nss_MD_unix_map_default_error(errno);
    117        return err;
    118    }
    119 #if NONBLOCKING_POSTS
    120    err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1);
    121    if (err)
    122        goto loser;
    123 #endif
    124 
    125    pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC;
    126 
    127 #if defined(LINUX) && defined(i386)
    128    /* Pipe starts out empty */
    129    return SECSuccess;
    130 #else
    131    /* Pipe starts with one byte. */
    132    return sslMutex_Unlock(pMutex);
    133 #endif
    134 
    135 loser:
    136    nss_MD_unix_map_default_error(errno);
    137    close(pMutex->u.pipeStr.mPipes[0]);
    138    close(pMutex->u.pipeStr.mPipes[1]);
    139    return SECFailure;
    140 }
    141 
    142 SECStatus
    143 sslMutex_Destroy(sslMutex* pMutex, PRBool processLocal)
    144 {
    145    if (PR_FALSE == pMutex->isMultiProcess) {
    146        return single_process_sslMutex_Destroy(pMutex);
    147    }
    148    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
    149        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    150        return SECFailure;
    151    }
    152    close(pMutex->u.pipeStr.mPipes[0]);
    153    close(pMutex->u.pipeStr.mPipes[1]);
    154 
    155    if (processLocal) {
    156        return SECSuccess;
    157    }
    158 
    159    pMutex->u.pipeStr.mPipes[0] = -1;
    160    pMutex->u.pipeStr.mPipes[1] = -1;
    161    pMutex->u.pipeStr.mPipes[2] = -1;
    162    pMutex->u.pipeStr.nWaiters = 0;
    163 
    164    return SECSuccess;
    165 }
    166 
    167 #if defined(LINUX) && defined(i386)
    168 /* No memory barrier needed for this platform */
    169 
    170 /* nWaiters includes the holder of the lock (if any) and the number
    171 ** threads waiting for it.  After incrementing nWaiters, if the count
    172 ** is exactly 1, then you have the lock and may proceed.  If the
    173 ** count is greater than 1, then you must wait on the pipe.
    174 */
    175 
    176 SECStatus
    177 sslMutex_Unlock(sslMutex* pMutex)
    178 {
    179    PRInt32 newValue;
    180    if (PR_FALSE == pMutex->isMultiProcess) {
    181        return single_process_sslMutex_Unlock(pMutex);
    182    }
    183 
    184    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
    185        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    186        return SECFailure;
    187    }
    188    /* Do Memory Barrier here. */
    189    newValue = PR_ATOMIC_DECREMENT(&pMutex->u.pipeStr.nWaiters);
    190    if (newValue > 0) {
    191        int cc;
    192        char c = 1;
    193        do {
    194            cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
    195        } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
    196        if (cc != 1) {
    197            if (cc < 0)
    198                nss_MD_unix_map_default_error(errno);
    199            else
    200                PORT_SetError(PR_UNKNOWN_ERROR);
    201            return SECFailure;
    202        }
    203    }
    204    return SECSuccess;
    205 }
    206 
    207 SECStatus
    208 sslMutex_Lock(sslMutex* pMutex)
    209 {
    210    PRInt32 newValue;
    211    if (PR_FALSE == pMutex->isMultiProcess) {
    212        return single_process_sslMutex_Lock(pMutex);
    213    }
    214 
    215    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
    216        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    217        return SECFailure;
    218    }
    219    newValue = PR_ATOMIC_INCREMENT(&pMutex->u.pipeStr.nWaiters);
    220    /* Do Memory Barrier here. */
    221    if (newValue > 1) {
    222        int cc;
    223        char c;
    224        do {
    225            cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
    226        } while (cc < 0 && errno == EINTR);
    227        if (cc != 1) {
    228            if (cc < 0)
    229                nss_MD_unix_map_default_error(errno);
    230            else
    231                PORT_SetError(PR_UNKNOWN_ERROR);
    232            return SECFailure;
    233        }
    234    }
    235    return SECSuccess;
    236 }
    237 
    238 #else
    239 
    240 /* Using Atomic operations requires the use of a memory barrier instruction
    241 ** on PowerPC, Sparc, and Alpha.  NSPR's PR_Atomic functions do not perform
    242 ** them, and NSPR does not provide a function that does them (e.g. PR_Barrier).
    243 ** So, we don't use them on those platforms.
    244 */
    245 
    246 SECStatus
    247 sslMutex_Unlock(sslMutex* pMutex)
    248 {
    249    int cc;
    250    char c = 1;
    251 
    252    if (PR_FALSE == pMutex->isMultiProcess) {
    253        return single_process_sslMutex_Unlock(pMutex);
    254    }
    255 
    256    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
    257        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    258        return SECFailure;
    259    }
    260    do {
    261        cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
    262    } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
    263    if (cc != 1) {
    264        if (cc < 0)
    265            nss_MD_unix_map_default_error(errno);
    266        else
    267            PORT_SetError(PR_UNKNOWN_ERROR);
    268        return SECFailure;
    269    }
    270 
    271    return SECSuccess;
    272 }
    273 
    274 SECStatus
    275 sslMutex_Lock(sslMutex* pMutex)
    276 {
    277    int cc;
    278    char c;
    279 
    280    if (PR_FALSE == pMutex->isMultiProcess) {
    281        return single_process_sslMutex_Lock(pMutex);
    282    }
    283 
    284    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
    285        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    286        return SECFailure;
    287    }
    288 
    289    do {
    290        cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
    291    } while (cc < 0 && errno == EINTR);
    292    if (cc != 1) {
    293        if (cc < 0)
    294            nss_MD_unix_map_default_error(errno);
    295        else
    296            PORT_SetError(PR_UNKNOWN_ERROR);
    297        return SECFailure;
    298    }
    299 
    300    return SECSuccess;
    301 }
    302 
    303 #endif
    304 
    305 #elif defined(WIN32)
    306 
    307 #include "win32err.h"
    308 
    309 /* on Windows, we need to find the optimal type of locking mechanism to use
    310 for the sslMutex.
    311 
    312 There are 3 cases :
    313 1) single-process, use a PRLock, as for all other platforms
    314 2) Win95 multi-process, use a Win32 mutex
    315 3) on WINNT multi-process, use a PRLock + a Win32 mutex
    316 
    317 */
    318 
    319 #ifdef WINNT
    320 
    321 SECStatus
    322 sslMutex_2LevelInit(sslMutex *sem)
    323 {
    324    /*  the following adds a PRLock to sslMutex . This is done in each
    325        process of a multi-process server and is only needed on WINNT, if
    326        using fibers. We can't tell if native threads or fibers are used, so
    327        we always do it on WINNT
    328    */
    329    PR_ASSERT(sem);
    330    if (sem) {
    331        /* we need to reset the sslLock in the children or the single_process init
    332           function below will assert */
    333        sem->u.sslLock = NULL;
    334    }
    335    return single_process_sslMutex_Init(sem);
    336 }
    337 
    338 static SECStatus
    339 sslMutex_2LevelDestroy(sslMutex *sem)
    340 {
    341    return single_process_sslMutex_Destroy(sem);
    342 }
    343 
    344 #endif
    345 
    346 SECStatus
    347 sslMutex_Init(sslMutex *pMutex, int shared)
    348 {
    349 #ifdef WINNT
    350    SECStatus retvalue;
    351 #endif
    352    HANDLE hMutex;
    353    SECURITY_ATTRIBUTES attributes = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
    354 
    355    PR_ASSERT(pMutex != 0 && (pMutex->u.sslMutx == 0 ||
    356                              pMutex->u.sslMutx ==
    357                                  INVALID_HANDLE_VALUE));
    358 
    359    pMutex->isMultiProcess = (PRBool)(shared != 0);
    360 
    361    if (PR_FALSE == pMutex->isMultiProcess) {
    362        return single_process_sslMutex_Init(pMutex);
    363    }
    364 
    365 #ifdef WINNT
    366    /*  we need a lock on WINNT for fibers in the parent process */
    367    retvalue = sslMutex_2LevelInit(pMutex);
    368    if (SECSuccess != retvalue)
    369        return SECFailure;
    370 #endif
    371 
    372    if (!pMutex || ((hMutex = pMutex->u.sslMutx) != 0 &&
    373                    hMutex !=
    374                        INVALID_HANDLE_VALUE)) {
    375        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    376        return SECFailure;
    377    }
    378    attributes.bInheritHandle = (shared ? TRUE : FALSE);
    379    hMutex = CreateMutex(&attributes, FALSE, NULL);
    380    if (hMutex == NULL) {
    381        hMutex = INVALID_HANDLE_VALUE;
    382        nss_MD_win32_map_default_error(GetLastError());
    383        return SECFailure;
    384    }
    385    pMutex->u.sslMutx = hMutex;
    386    return SECSuccess;
    387 }
    388 
    389 SECStatus
    390 sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
    391 {
    392    HANDLE hMutex;
    393    int rv;
    394    int retvalue = SECSuccess;
    395 
    396    PR_ASSERT(pMutex != 0);
    397    if (!pMutex) {
    398        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    399        return SECFailure;
    400    }
    401 
    402    if (PR_FALSE == pMutex->isMultiProcess) {
    403        return single_process_sslMutex_Destroy(pMutex);
    404    }
    405 
    406 /*  multi-process mode */
    407 #ifdef WINNT
    408    /* on NT, get rid of the PRLock used for fibers within a process */
    409    retvalue = sslMutex_2LevelDestroy(pMutex);
    410 #endif
    411 
    412    PR_ASSERT(pMutex->u.sslMutx != 0 &&
    413              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
    414    if ((hMutex = pMutex->u.sslMutx) == 0 || hMutex == INVALID_HANDLE_VALUE) {
    415        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    416        return SECFailure;
    417    }
    418 
    419    rv = CloseHandle(hMutex); /* ignore error */
    420    if (!processLocal && rv) {
    421        pMutex->u.sslMutx = hMutex = INVALID_HANDLE_VALUE;
    422    }
    423    if (!rv) {
    424        nss_MD_win32_map_default_error(GetLastError());
    425        retvalue = SECFailure;
    426    }
    427    return retvalue;
    428 }
    429 
    430 int
    431 sslMutex_Unlock(sslMutex *pMutex)
    432 {
    433    BOOL success = FALSE;
    434    HANDLE hMutex;
    435 
    436    PR_ASSERT(pMutex != 0);
    437    if (!pMutex) {
    438        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    439        return SECFailure;
    440    }
    441 
    442    if (PR_FALSE == pMutex->isMultiProcess) {
    443        return single_process_sslMutex_Unlock(pMutex);
    444    }
    445 
    446    PR_ASSERT(pMutex->u.sslMutx != 0 &&
    447              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
    448    if ((hMutex = pMutex->u.sslMutx) == 0 || hMutex == INVALID_HANDLE_VALUE) {
    449        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    450        return SECFailure;
    451    }
    452    success = ReleaseMutex(hMutex);
    453    if (!success) {
    454        nss_MD_win32_map_default_error(GetLastError());
    455        return SECFailure;
    456    }
    457 #ifdef WINNT
    458    return single_process_sslMutex_Unlock(pMutex);
    459 /* release PRLock for other fibers in the process */
    460 #else
    461    return SECSuccess;
    462 #endif
    463 }
    464 
    465 int
    466 sslMutex_Lock(sslMutex *pMutex)
    467 {
    468    HANDLE hMutex;
    469    DWORD event;
    470    DWORD lastError;
    471    SECStatus rv;
    472    SECStatus retvalue = SECSuccess;
    473 
    474    PR_ASSERT(pMutex != 0);
    475    if (!pMutex) {
    476        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    477        return SECFailure;
    478    }
    479 
    480    if (PR_FALSE == pMutex->isMultiProcess) {
    481        return single_process_sslMutex_Lock(pMutex);
    482    }
    483 #ifdef WINNT
    484    /* lock first to preserve from other threads/fibers in the same process */
    485    retvalue = single_process_sslMutex_Lock(pMutex);
    486 #endif
    487    PR_ASSERT(pMutex->u.sslMutx != 0 &&
    488              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
    489    if ((hMutex = pMutex->u.sslMutx) == 0 || hMutex == INVALID_HANDLE_VALUE) {
    490        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    491        return SECFailure; /* what else ? */
    492    }
    493    /* acquire the mutex to be the only owner accross all other processes */
    494    event = WaitForSingleObject(hMutex, INFINITE);
    495    switch (event) {
    496        case WAIT_OBJECT_0:
    497        case WAIT_ABANDONED:
    498            rv = SECSuccess;
    499            break;
    500 
    501        case WAIT_TIMEOUT:
    502 #if defined(WAIT_IO_COMPLETION)
    503        case WAIT_IO_COMPLETION:
    504 #endif
    505        default: /* should never happen. nothing we can do. */
    506            PR_ASSERT(PR_FALSE && "WaitForSingleObject returned invalid value.");
    507            PORT_SetError(PR_UNKNOWN_ERROR);
    508            rv = SECFailure;
    509            break;
    510 
    511        case WAIT_FAILED: /* failure returns this */
    512            rv = SECFailure;
    513            lastError = GetLastError(); /* for debugging */
    514            nss_MD_win32_map_default_error(lastError);
    515            break;
    516    }
    517 
    518    if (!(SECSuccess == retvalue && SECSuccess == rv)) {
    519        return SECFailure;
    520    }
    521 
    522    return SECSuccess;
    523 }
    524 
    525 #elif defined(XP_UNIX) && !defined(DARWIN)
    526 
    527 #include <errno.h>
    528 #include "unix_err.h"
    529 
    530 SECStatus
    531 sslMutex_Init(sslMutex* pMutex, int shared)
    532 {
    533    int rv;
    534    PR_ASSERT(pMutex);
    535    pMutex->isMultiProcess = (PRBool)(shared != 0);
    536    if (!shared) {
    537        return single_process_sslMutex_Init(pMutex);
    538    }
    539    do {
    540        rv = sem_init(&pMutex->u.sem, shared, 1);
    541    } while (rv < 0 && errno == EINTR);
    542    if (rv < 0) {
    543        nss_MD_unix_map_default_error(errno);
    544        return SECFailure;
    545    }
    546    return SECSuccess;
    547 }
    548 
    549 SECStatus
    550 sslMutex_Destroy(sslMutex* pMutex, PRBool processLocal)
    551 {
    552    int rv;
    553    if (PR_FALSE == pMutex->isMultiProcess) {
    554        return single_process_sslMutex_Destroy(pMutex);
    555    }
    556 
    557    /* semaphores are global resources. See SEM_DESTROY(3) man page */
    558    if (processLocal) {
    559        return SECSuccess;
    560    }
    561    do {
    562        rv = sem_destroy(&pMutex->u.sem);
    563    } while (rv < 0 && errno == EINTR);
    564    if (rv < 0) {
    565        nss_MD_unix_map_default_error(errno);
    566        return SECFailure;
    567    }
    568    return SECSuccess;
    569 }
    570 
    571 SECStatus
    572 sslMutex_Unlock(sslMutex* pMutex)
    573 {
    574    int rv;
    575    if (PR_FALSE == pMutex->isMultiProcess) {
    576        return single_process_sslMutex_Unlock(pMutex);
    577    }
    578    do {
    579        rv = sem_post(&pMutex->u.sem);
    580    } while (rv < 0 && errno == EINTR);
    581    if (rv < 0) {
    582        nss_MD_unix_map_default_error(errno);
    583        return SECFailure;
    584    }
    585    return SECSuccess;
    586 }
    587 
    588 SECStatus
    589 sslMutex_Lock(sslMutex* pMutex)
    590 {
    591    int rv;
    592    if (PR_FALSE == pMutex->isMultiProcess) {
    593        return single_process_sslMutex_Lock(pMutex);
    594    }
    595    do {
    596        rv = sem_wait(&pMutex->u.sem);
    597    } while (rv < 0 && errno == EINTR);
    598    if (rv < 0) {
    599        nss_MD_unix_map_default_error(errno);
    600        return SECFailure;
    601    }
    602    return SECSuccess;
    603 }
    604 
    605 #else
    606 
    607 SECStatus
    608 sslMutex_Init(sslMutex* pMutex, int shared)
    609 {
    610    PR_ASSERT(pMutex);
    611    pMutex->isMultiProcess = (PRBool)(shared != 0);
    612    if (!shared) {
    613        return single_process_sslMutex_Init(pMutex);
    614    }
    615    PORT_Assert(PR_FALSE && "sslMutex_Init not implemented for multi-process applications !");
    616    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
    617    return SECFailure;
    618 }
    619 
    620 SECStatus
    621 sslMutex_Destroy(sslMutex* pMutex, PRBool processLocal)
    622 {
    623    PR_ASSERT(pMutex);
    624    if (PR_FALSE == pMutex->isMultiProcess) {
    625        return single_process_sslMutex_Destroy(pMutex);
    626    }
    627    PORT_Assert(PR_FALSE && "sslMutex_Destroy not implemented for multi-process applications !");
    628    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
    629    return SECFailure;
    630 }
    631 
    632 SECStatus
    633 sslMutex_Unlock(sslMutex* pMutex)
    634 {
    635    PR_ASSERT(pMutex);
    636    if (PR_FALSE == pMutex->isMultiProcess) {
    637        return single_process_sslMutex_Unlock(pMutex);
    638    }
    639    PORT_Assert(PR_FALSE && "sslMutex_Unlock not implemented for multi-process applications !");
    640    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
    641    return SECFailure;
    642 }
    643 
    644 SECStatus
    645 sslMutex_Lock(sslMutex* pMutex)
    646 {
    647    PR_ASSERT(pMutex);
    648    if (PR_FALSE == pMutex->isMultiProcess) {
    649        return single_process_sslMutex_Lock(pMutex);
    650    }
    651    PORT_Assert(PR_FALSE && "sslMutex_Lock not implemented for multi-process applications !");
    652    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
    653    return SECFailure;
    654 }
    655 
    656 #endif
    657 
    658 #endif