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