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 { 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 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 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 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 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 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 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 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 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 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 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 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 333 static SECStatus sslMutex_2LevelDestroy(sslMutex *sem) 334 { 335 return single_process_sslMutex_Destroy(sem); 336 } 337 338 #endif 339 340 SECStatus 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 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 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 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 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 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 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 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 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 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 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 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