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
single_process_sslMutex_Init(sslMutex * pMutex)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
single_process_sslMutex_Destroy(sslMutex * pMutex)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
single_process_sslMutex_Unlock(sslMutex * pMutex)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
single_process_sslMutex_Lock(sslMutex * pMutex)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(BEOS) || defined(BSDI) || (defined(NETBSD) && __NetBSD_Version__ < 500000000) || defined(OPENBSD)
64
65 #include <unistd.h>
66 #include <fcntl.h>
67 #include <string.h>
68 #include <errno.h>
69 #include "unix_err.h"
70 #include "pratom.h"
71
72 #define SSL_MUTEX_MAGIC 0xfeedfd
73 #define NONBLOCKING_POSTS 1 /* maybe this is faster */
74
75 #if NONBLOCKING_POSTS
76
77 #ifndef FNONBLOCK
78 #define FNONBLOCK O_NONBLOCK
79 #endif
80
81 static int
setNonBlocking(int fd,int nonBlocking)82 setNonBlocking(int fd, int nonBlocking)
83 {
84 int flags;
85 int err;
86
87 flags = fcntl(fd, F_GETFL, 0);
88 if (0 > flags)
89 return flags;
90 if (nonBlocking)
91 flags |= FNONBLOCK;
92 else
93 flags &= ~FNONBLOCK;
94 err = fcntl(fd, F_SETFL, flags);
95 return err;
96 }
97 #endif
98
99 SECStatus
sslMutex_Init(sslMutex * pMutex,int shared)100 sslMutex_Init(sslMutex* pMutex, int shared)
101 {
102 int err;
103 PR_ASSERT(pMutex);
104 pMutex->isMultiProcess = (PRBool)(shared != 0);
105 if (!shared) {
106 return single_process_sslMutex_Init(pMutex);
107 }
108 pMutex->u.pipeStr.mPipes[0] = -1;
109 pMutex->u.pipeStr.mPipes[1] = -1;
110 pMutex->u.pipeStr.mPipes[2] = -1;
111 pMutex->u.pipeStr.nWaiters = 0;
112
113 err = pipe(pMutex->u.pipeStr.mPipes);
114 if (err) {
115 nss_MD_unix_map_default_error(errno);
116 return err;
117 }
118 #if NONBLOCKING_POSTS
119 err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1);
120 if (err)
121 goto loser;
122 #endif
123
124 pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC;
125
126 #if defined(LINUX) && defined(i386)
127 /* Pipe starts out empty */
128 return SECSuccess;
129 #else
130 /* Pipe starts with one byte. */
131 return sslMutex_Unlock(pMutex);
132 #endif
133
134 loser:
135 nss_MD_unix_map_default_error(errno);
136 close(pMutex->u.pipeStr.mPipes[0]);
137 close(pMutex->u.pipeStr.mPipes[1]);
138 return SECFailure;
139 }
140
141 SECStatus
sslMutex_Destroy(sslMutex * pMutex,PRBool processLocal)142 sslMutex_Destroy(sslMutex* pMutex, PRBool processLocal)
143 {
144 if (PR_FALSE == pMutex->isMultiProcess) {
145 return single_process_sslMutex_Destroy(pMutex);
146 }
147 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
148 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
149 return SECFailure;
150 }
151 close(pMutex->u.pipeStr.mPipes[0]);
152 close(pMutex->u.pipeStr.mPipes[1]);
153
154 if (processLocal) {
155 return SECSuccess;
156 }
157
158 pMutex->u.pipeStr.mPipes[0] = -1;
159 pMutex->u.pipeStr.mPipes[1] = -1;
160 pMutex->u.pipeStr.mPipes[2] = -1;
161 pMutex->u.pipeStr.nWaiters = 0;
162
163 return SECSuccess;
164 }
165
166 #if defined(LINUX) && defined(i386)
167 /* No memory barrier needed for this platform */
168
169 /* nWaiters includes the holder of the lock (if any) and the number
170 ** threads waiting for it. After incrementing nWaiters, if the count
171 ** is exactly 1, then you have the lock and may proceed. If the
172 ** count is greater than 1, then you must wait on the pipe.
173 */
174
175 SECStatus
sslMutex_Unlock(sslMutex * pMutex)176 sslMutex_Unlock(sslMutex* pMutex)
177 {
178 PRInt32 newValue;
179 if (PR_FALSE == pMutex->isMultiProcess) {
180 return single_process_sslMutex_Unlock(pMutex);
181 }
182
183 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
184 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
185 return SECFailure;
186 }
187 /* Do Memory Barrier here. */
188 newValue = PR_ATOMIC_DECREMENT(&pMutex->u.pipeStr.nWaiters);
189 if (newValue > 0) {
190 int cc;
191 char c = 1;
192 do {
193 cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
194 } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
195 if (cc != 1) {
196 if (cc < 0)
197 nss_MD_unix_map_default_error(errno);
198 else
199 PORT_SetError(PR_UNKNOWN_ERROR);
200 return SECFailure;
201 }
202 }
203 return SECSuccess;
204 }
205
206 SECStatus
sslMutex_Lock(sslMutex * pMutex)207 sslMutex_Lock(sslMutex* pMutex)
208 {
209 PRInt32 newValue;
210 if (PR_FALSE == pMutex->isMultiProcess) {
211 return single_process_sslMutex_Lock(pMutex);
212 }
213
214 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
215 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
216 return SECFailure;
217 }
218 newValue = PR_ATOMIC_INCREMENT(&pMutex->u.pipeStr.nWaiters);
219 /* Do Memory Barrier here. */
220 if (newValue > 1) {
221 int cc;
222 char c;
223 do {
224 cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
225 } while (cc < 0 && errno == EINTR);
226 if (cc != 1) {
227 if (cc < 0)
228 nss_MD_unix_map_default_error(errno);
229 else
230 PORT_SetError(PR_UNKNOWN_ERROR);
231 return SECFailure;
232 }
233 }
234 return SECSuccess;
235 }
236
237 #else
238
239 /* Using Atomic operations requires the use of a memory barrier instruction
240 ** on PowerPC, Sparc, and Alpha. NSPR's PR_Atomic functions do not perform
241 ** them, and NSPR does not provide a function that does them (e.g. PR_Barrier).
242 ** So, we don't use them on those platforms.
243 */
244
245 SECStatus
sslMutex_Unlock(sslMutex * pMutex)246 sslMutex_Unlock(sslMutex* pMutex)
247 {
248 int cc;
249 char c = 1;
250
251 if (PR_FALSE == pMutex->isMultiProcess) {
252 return single_process_sslMutex_Unlock(pMutex);
253 }
254
255 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
256 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
257 return SECFailure;
258 }
259 do {
260 cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
261 } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
262 if (cc != 1) {
263 if (cc < 0)
264 nss_MD_unix_map_default_error(errno);
265 else
266 PORT_SetError(PR_UNKNOWN_ERROR);
267 return SECFailure;
268 }
269
270 return SECSuccess;
271 }
272
273 SECStatus
sslMutex_Lock(sslMutex * pMutex)274 sslMutex_Lock(sslMutex* pMutex)
275 {
276 int cc;
277 char c;
278
279 if (PR_FALSE == pMutex->isMultiProcess) {
280 return single_process_sslMutex_Lock(pMutex);
281 }
282
283 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
284 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
285 return SECFailure;
286 }
287
288 do {
289 cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
290 } while (cc < 0 && errno == EINTR);
291 if (cc != 1) {
292 if (cc < 0)
293 nss_MD_unix_map_default_error(errno);
294 else
295 PORT_SetError(PR_UNKNOWN_ERROR);
296 return SECFailure;
297 }
298
299 return SECSuccess;
300 }
301
302 #endif
303
304 #elif defined(WIN32)
305
306 #include "win32err.h"
307
308 /* on Windows, we need to find the optimal type of locking mechanism to use
309 for the sslMutex.
310
311 There are 3 cases :
312 1) single-process, use a PRLock, as for all other platforms
313 2) Win95 multi-process, use a Win32 mutex
314 3) on WINNT multi-process, use a PRLock + a Win32 mutex
315
316 */
317
318 #ifdef WINNT
319
320 SECStatus
sslMutex_2LevelInit(sslMutex * sem)321 sslMutex_2LevelInit(sslMutex *sem)
322 {
323 /* the following adds a PRLock to sslMutex . This is done in each
324 process of a multi-process server and is only needed on WINNT, if
325 using fibers. We can't tell if native threads or fibers are used, so
326 we always do it on WINNT
327 */
328 PR_ASSERT(sem);
329 if (sem) {
330 /* we need to reset the sslLock in the children or the single_process init
331 function below will assert */
332 sem->u.sslLock = NULL;
333 }
334 return single_process_sslMutex_Init(sem);
335 }
336
337 static SECStatus
sslMutex_2LevelDestroy(sslMutex * sem)338 sslMutex_2LevelDestroy(sslMutex *sem)
339 {
340 return single_process_sslMutex_Destroy(sem);
341 }
342
343 #endif
344
345 SECStatus
sslMutex_Init(sslMutex * pMutex,int shared)346 sslMutex_Init(sslMutex *pMutex, int shared)
347 {
348 #ifdef WINNT
349 SECStatus retvalue;
350 #endif
351 HANDLE hMutex;
352 SECURITY_ATTRIBUTES attributes =
353 { 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
sslMutex_Destroy(sslMutex * pMutex,PRBool processLocal)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
sslMutex_Unlock(sslMutex * pMutex)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
sslMutex_Lock(sslMutex * pMutex)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(!("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
sslMutex_Init(sslMutex * pMutex,int shared)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
sslMutex_Destroy(sslMutex * pMutex,PRBool processLocal)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
sslMutex_Unlock(sslMutex * pMutex)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
sslMutex_Lock(sslMutex * pMutex)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
sslMutex_Init(sslMutex * pMutex,int shared)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(!("sslMutex_Init not implemented for multi-process applications !"));
616 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
617 return SECFailure;
618 }
619
620 SECStatus
sslMutex_Destroy(sslMutex * pMutex,PRBool processLocal)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(!("sslMutex_Destroy not implemented for multi-process applications !"));
628 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
629 return SECFailure;
630 }
631
632 SECStatus
sslMutex_Unlock(sslMutex * pMutex)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(!("sslMutex_Unlock not implemented for multi-process applications !"));
640 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
641 return SECFailure;
642 }
643
644 SECStatus
sslMutex_Lock(sslMutex * pMutex)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(!("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
659