Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/nss/lib/ssl/sslmutex.c
Line
Count
Source (jump to first uncovered line)
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) || defined(XP_OS2) || defined(XP_BEOS)
8
9
#include "sslmutex.h"
10
#include "prerr.h"
11
12
static SECStatus
13
single_process_sslMutex_Init(sslMutex* pMutex)
14
0
{
15
0
    PR_ASSERT(pMutex != 0 && pMutex->u.sslLock == 0);
16
0
17
0
    pMutex->u.sslLock = PR_NewLock();
18
0
    if (!pMutex->u.sslLock) {
19
0
        return SECFailure;
20
0
    }
21
0
    return SECSuccess;
22
0
}
23
24
static SECStatus
25
single_process_sslMutex_Destroy(sslMutex* pMutex)
26
0
{
27
0
    PR_ASSERT(pMutex != 0);
28
0
    PR_ASSERT(pMutex->u.sslLock != 0);
29
0
    if (!pMutex->u.sslLock) {
30
0
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
31
0
        return SECFailure;
32
0
    }
33
0
    PR_DestroyLock(pMutex->u.sslLock);
34
0
    return SECSuccess;
35
0
}
36
37
static SECStatus
38
single_process_sslMutex_Unlock(sslMutex* pMutex)
39
0
{
40
0
    PR_ASSERT(pMutex != 0);
41
0
    PR_ASSERT(pMutex->u.sslLock != 0);
42
0
    if (!pMutex->u.sslLock) {
43
0
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
44
0
        return SECFailure;
45
0
    }
46
0
    PR_Unlock(pMutex->u.sslLock);
47
0
    return SECSuccess;
48
0
}
49
50
static SECStatus
51
single_process_sslMutex_Lock(sslMutex* pMutex)
52
0
{
53
0
    PR_ASSERT(pMutex != 0);
54
0
    PR_ASSERT(pMutex->u.sslLock != 0);
55
0
    if (!pMutex->u.sslLock) {
56
0
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
57
0
        return SECFailure;
58
0
    }
59
0
    PR_Lock(pMutex->u.sslLock);
60
0
    return SECSuccess;
61
0
}
62
63
#if defined(LINUX) || defined(AIX) || defined(BEOS) || 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
0
#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
0
{
85
0
    int flags;
86
0
    int err;
87
0
88
0
    flags = fcntl(fd, F_GETFL, 0);
89
0
    if (0 > flags)
90
0
        return flags;
91
0
    if (nonBlocking)
92
0
        flags |= FNONBLOCK;
93
0
    else
94
0
        flags &= ~FNONBLOCK;
95
0
    err = fcntl(fd, F_SETFL, flags);
96
0
    return err;
97
0
}
98
#endif
99
100
SECStatus
101
sslMutex_Init(sslMutex* pMutex, int shared)
102
0
{
103
0
    int err;
104
0
    PR_ASSERT(pMutex);
105
0
    pMutex->isMultiProcess = (PRBool)(shared != 0);
106
0
    if (!shared) {
107
0
        return single_process_sslMutex_Init(pMutex);
108
0
    }
109
0
    pMutex->u.pipeStr.mPipes[0] = -1;
110
0
    pMutex->u.pipeStr.mPipes[1] = -1;
111
0
    pMutex->u.pipeStr.mPipes[2] = -1;
112
0
    pMutex->u.pipeStr.nWaiters = 0;
113
0
114
0
    err = pipe(pMutex->u.pipeStr.mPipes);
115
0
    if (err) {
116
0
        nss_MD_unix_map_default_error(errno);
117
0
        return err;
118
0
    }
119
0
#if NONBLOCKING_POSTS
120
0
    err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1);
121
0
    if (err)
122
0
        goto loser;
123
0
#endif
124
0
125
0
    pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC;
126
0
127
#if defined(LINUX) && defined(i386)
128
    /* Pipe starts out empty */
129
    return SECSuccess;
130
#else
131
    /* Pipe starts with one byte. */
132
0
    return sslMutex_Unlock(pMutex);
133
0
#endif
134
0
135
0
loser:
136
0
    nss_MD_unix_map_default_error(errno);
137
0
    close(pMutex->u.pipeStr.mPipes[0]);
138
0
    close(pMutex->u.pipeStr.mPipes[1]);
139
0
    return SECFailure;
140
0
}
141
142
SECStatus
143
sslMutex_Destroy(sslMutex* pMutex, PRBool processLocal)
144
0
{
145
0
    if (PR_FALSE == pMutex->isMultiProcess) {
146
0
        return single_process_sslMutex_Destroy(pMutex);
147
0
    }
148
0
    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
149
0
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
150
0
        return SECFailure;
151
0
    }
152
0
    close(pMutex->u.pipeStr.mPipes[0]);
153
0
    close(pMutex->u.pipeStr.mPipes[1]);
154
0
155
0
    if (processLocal) {
156
0
        return SECSuccess;
157
0
    }
158
0
159
0
    pMutex->u.pipeStr.mPipes[0] = -1;
160
0
    pMutex->u.pipeStr.mPipes[1] = -1;
161
0
    pMutex->u.pipeStr.mPipes[2] = -1;
162
0
    pMutex->u.pipeStr.nWaiters = 0;
163
0
164
0
    return SECSuccess;
165
0
}
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
0
{
249
0
    int cc;
250
0
    char c = 1;
251
0
252
0
    if (PR_FALSE == pMutex->isMultiProcess) {
253
0
        return single_process_sslMutex_Unlock(pMutex);
254
0
    }
255
0
256
0
    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
257
0
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
258
0
        return SECFailure;
259
0
    }
260
0
    do {
261
0
        cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
262
0
    } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
263
0
    if (cc != 1) {
264
0
        if (cc < 0)
265
0
            nss_MD_unix_map_default_error(errno);
266
0
        else
267
0
            PORT_SetError(PR_UNKNOWN_ERROR);
268
0
        return SECFailure;
269
0
    }
270
0
271
0
    return SECSuccess;
272
0
}
273
274
SECStatus
275
sslMutex_Lock(sslMutex* pMutex)
276
0
{
277
0
    int cc;
278
0
    char c;
279
0
280
0
    if (PR_FALSE == pMutex->isMultiProcess) {
281
0
        return single_process_sslMutex_Lock(pMutex);
282
0
    }
283
0
284
0
    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
285
0
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
286
0
        return SECFailure;
287
0
    }
288
0
289
0
    do {
290
0
        cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
291
0
    } while (cc < 0 && errno == EINTR);
292
0
    if (cc != 1) {
293
0
        if (cc < 0)
294
0
            nss_MD_unix_map_default_error(errno);
295
0
        else
296
0
            PORT_SetError(PR_UNKNOWN_ERROR);
297
0
        return SECFailure;
298
0
    }
299
0
300
0
    return SECSuccess;
301
0
}
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 =
354
        { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
355
356
    PR_ASSERT(pMutex != 0 && (pMutex->u.sslMutx == 0 ||
357
                              pMutex->u.sslMutx ==
358
                                  INVALID_HANDLE_VALUE));
359
360
    pMutex->isMultiProcess = (PRBool)(shared != 0);
361
362
    if (PR_FALSE == pMutex->isMultiProcess) {
363
        return single_process_sslMutex_Init(pMutex);
364
    }
365
366
#ifdef WINNT
367
    /*  we need a lock on WINNT for fibers in the parent process */
368
    retvalue = sslMutex_2LevelInit(pMutex);
369
    if (SECSuccess != retvalue)
370
        return SECFailure;
371
#endif
372
373
    if (!pMutex || ((hMutex = pMutex->u.sslMutx) != 0 &&
374
                    hMutex !=
375
                        INVALID_HANDLE_VALUE)) {
376
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
377
        return SECFailure;
378
    }
379
    attributes.bInheritHandle = (shared ? TRUE : FALSE);
380
    hMutex = CreateMutex(&attributes, FALSE, NULL);
381
    if (hMutex == NULL) {
382
        hMutex = INVALID_HANDLE_VALUE;
383
        nss_MD_win32_map_default_error(GetLastError());
384
        return SECFailure;
385
    }
386
    pMutex->u.sslMutx = hMutex;
387
    return SECSuccess;
388
}
389
390
SECStatus
391
sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
392
{
393
    HANDLE hMutex;
394
    int rv;
395
    int retvalue = SECSuccess;
396
397
    PR_ASSERT(pMutex != 0);
398
    if (!pMutex) {
399
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
400
        return SECFailure;
401
    }
402
403
    if (PR_FALSE == pMutex->isMultiProcess) {
404
        return single_process_sslMutex_Destroy(pMutex);
405
    }
406
407
/*  multi-process mode */
408
#ifdef WINNT
409
    /* on NT, get rid of the PRLock used for fibers within a process */
410
    retvalue = sslMutex_2LevelDestroy(pMutex);
411
#endif
412
413
    PR_ASSERT(pMutex->u.sslMutx != 0 &&
414
              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
415
    if ((hMutex = pMutex->u.sslMutx) == 0 || hMutex == INVALID_HANDLE_VALUE) {
416
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
417
        return SECFailure;
418
    }
419
420
    rv = CloseHandle(hMutex); /* ignore error */
421
    if (!processLocal && rv) {
422
        pMutex->u.sslMutx = hMutex = INVALID_HANDLE_VALUE;
423
    }
424
    if (!rv) {
425
        nss_MD_win32_map_default_error(GetLastError());
426
        retvalue = SECFailure;
427
    }
428
    return retvalue;
429
}
430
431
int
432
sslMutex_Unlock(sslMutex *pMutex)
433
{
434
    BOOL success = FALSE;
435
    HANDLE hMutex;
436
437
    PR_ASSERT(pMutex != 0);
438
    if (!pMutex) {
439
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
440
        return SECFailure;
441
    }
442
443
    if (PR_FALSE == pMutex->isMultiProcess) {
444
        return single_process_sslMutex_Unlock(pMutex);
445
    }
446
447
    PR_ASSERT(pMutex->u.sslMutx != 0 &&
448
              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
449
    if ((hMutex = pMutex->u.sslMutx) == 0 || hMutex == INVALID_HANDLE_VALUE) {
450
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
451
        return SECFailure;
452
    }
453
    success = ReleaseMutex(hMutex);
454
    if (!success) {
455
        nss_MD_win32_map_default_error(GetLastError());
456
        return SECFailure;
457
    }
458
#ifdef WINNT
459
    return single_process_sslMutex_Unlock(pMutex);
460
/* release PRLock for other fibers in the process */
461
#else
462
    return SECSuccess;
463
#endif
464
}
465
466
int
467
sslMutex_Lock(sslMutex *pMutex)
468
{
469
    HANDLE hMutex;
470
    DWORD event;
471
    DWORD lastError;
472
    SECStatus rv;
473
    SECStatus retvalue = SECSuccess;
474
475
    PR_ASSERT(pMutex != 0);
476
    if (!pMutex) {
477
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
478
        return SECFailure;
479
    }
480
481
    if (PR_FALSE == pMutex->isMultiProcess) {
482
        return single_process_sslMutex_Lock(pMutex);
483
    }
484
#ifdef WINNT
485
    /* lock first to preserve from other threads/fibers in the same process */
486
    retvalue = single_process_sslMutex_Lock(pMutex);
487
#endif
488
    PR_ASSERT(pMutex->u.sslMutx != 0 &&
489
              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
490
    if ((hMutex = pMutex->u.sslMutx) == 0 || hMutex == INVALID_HANDLE_VALUE) {
491
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
492
        return SECFailure; /* what else ? */
493
    }
494
    /* acquire the mutex to be the only owner accross all other processes */
495
    event = WaitForSingleObject(hMutex, INFINITE);
496
    switch (event) {
497
        case WAIT_OBJECT_0:
498
        case WAIT_ABANDONED:
499
            rv = SECSuccess;
500
            break;
501
502
        case WAIT_TIMEOUT:
503
#if defined(WAIT_IO_COMPLETION)
504
        case WAIT_IO_COMPLETION:
505
#endif
506
        default: /* should never happen. nothing we can do. */
507
            PR_ASSERT(!("WaitForSingleObject returned invalid value."));
508
            PORT_SetError(PR_UNKNOWN_ERROR);
509
            rv = SECFailure;
510
            break;
511
512
        case WAIT_FAILED: /* failure returns this */
513
            rv = SECFailure;
514
            lastError = GetLastError(); /* for debugging */
515
            nss_MD_win32_map_default_error(lastError);
516
            break;
517
    }
518
519
    if (!(SECSuccess == retvalue && SECSuccess == rv)) {
520
        return SECFailure;
521
    }
522
523
    return SECSuccess;
524
}
525
526
#elif defined(XP_UNIX) && !defined(DARWIN)
527
528
#include <errno.h>
529
#include "unix_err.h"
530
531
SECStatus
532
sslMutex_Init(sslMutex* pMutex, int shared)
533
{
534
    int rv;
535
    PR_ASSERT(pMutex);
536
    pMutex->isMultiProcess = (PRBool)(shared != 0);
537
    if (!shared) {
538
        return single_process_sslMutex_Init(pMutex);
539
    }
540
    do {
541
        rv = sem_init(&pMutex->u.sem, shared, 1);
542
    } while (rv < 0 && errno == EINTR);
543
    if (rv < 0) {
544
        nss_MD_unix_map_default_error(errno);
545
        return SECFailure;
546
    }
547
    return SECSuccess;
548
}
549
550
SECStatus
551
sslMutex_Destroy(sslMutex* pMutex, PRBool processLocal)
552
{
553
    int rv;
554
    if (PR_FALSE == pMutex->isMultiProcess) {
555
        return single_process_sslMutex_Destroy(pMutex);
556
    }
557
558
    /* semaphores are global resources. See SEM_DESTROY(3) man page */
559
    if (processLocal) {
560
        return SECSuccess;
561
    }
562
    do {
563
        rv = sem_destroy(&pMutex->u.sem);
564
    } while (rv < 0 && errno == EINTR);
565
    if (rv < 0) {
566
        nss_MD_unix_map_default_error(errno);
567
        return SECFailure;
568
    }
569
    return SECSuccess;
570
}
571
572
SECStatus
573
sslMutex_Unlock(sslMutex* pMutex)
574
{
575
    int rv;
576
    if (PR_FALSE == pMutex->isMultiProcess) {
577
        return single_process_sslMutex_Unlock(pMutex);
578
    }
579
    do {
580
        rv = sem_post(&pMutex->u.sem);
581
    } while (rv < 0 && errno == EINTR);
582
    if (rv < 0) {
583
        nss_MD_unix_map_default_error(errno);
584
        return SECFailure;
585
    }
586
    return SECSuccess;
587
}
588
589
SECStatus
590
sslMutex_Lock(sslMutex* pMutex)
591
{
592
    int rv;
593
    if (PR_FALSE == pMutex->isMultiProcess) {
594
        return single_process_sslMutex_Lock(pMutex);
595
    }
596
    do {
597
        rv = sem_wait(&pMutex->u.sem);
598
    } while (rv < 0 && errno == EINTR);
599
    if (rv < 0) {
600
        nss_MD_unix_map_default_error(errno);
601
        return SECFailure;
602
    }
603
    return SECSuccess;
604
}
605
606
#else
607
608
SECStatus
609
sslMutex_Init(sslMutex* pMutex, int shared)
610
{
611
    PR_ASSERT(pMutex);
612
    pMutex->isMultiProcess = (PRBool)(shared != 0);
613
    if (!shared) {
614
        return single_process_sslMutex_Init(pMutex);
615
    }
616
    PORT_Assert(!("sslMutex_Init not implemented for multi-process applications !"));
617
    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
618
    return SECFailure;
619
}
620
621
SECStatus
622
sslMutex_Destroy(sslMutex* pMutex, PRBool processLocal)
623
{
624
    PR_ASSERT(pMutex);
625
    if (PR_FALSE == pMutex->isMultiProcess) {
626
        return single_process_sslMutex_Destroy(pMutex);
627
    }
628
    PORT_Assert(!("sslMutex_Destroy not implemented for multi-process applications !"));
629
    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
630
    return SECFailure;
631
}
632
633
SECStatus
634
sslMutex_Unlock(sslMutex* pMutex)
635
{
636
    PR_ASSERT(pMutex);
637
    if (PR_FALSE == pMutex->isMultiProcess) {
638
        return single_process_sslMutex_Unlock(pMutex);
639
    }
640
    PORT_Assert(!("sslMutex_Unlock not implemented for multi-process applications !"));
641
    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
642
    return SECFailure;
643
}
644
645
SECStatus
646
sslMutex_Lock(sslMutex* pMutex)
647
{
648
    PR_ASSERT(pMutex);
649
    if (PR_FALSE == pMutex->isMultiProcess) {
650
        return single_process_sslMutex_Lock(pMutex);
651
    }
652
    PORT_Assert(!("sslMutex_Lock not implemented for multi-process applications !"));
653
    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
654
    return SECFailure;
655
}
656
657
#endif
658
659
#endif