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