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