1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ob/obwait.c 5 * PURPOSE: Handles Waiting on Objects 6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 7 * Thomas Weidenmueller (w3seek@reactos.org) 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 /* FUNCTIONS *****************************************************************/ 17 18 /*++ 19 * @name NtWaitForMultipleObjects 20 * @implemented NT4 21 * 22 * The NtWaitForMultipleObjects routine <FILLMEIN> 23 * 24 * @param ObjectCount 25 * <FILLMEIN> 26 * 27 * @param HandleArray 28 * <FILLMEIN> 29 * 30 * @param WaitType 31 * <FILLMEIN> 32 * 33 * @param Alertable 34 * <FILLMEIN> 35 * 36 * @param TimeOut 37 * <FILLMEIN> 38 * 39 * @return STATUS_SUCCESS or appropriate error value. 40 * 41 * @remarks None. 42 * 43 *--*/ 44 NTSTATUS 45 NTAPI 46 NtWaitForMultipleObjects(IN ULONG ObjectCount, 47 IN PHANDLE HandleArray, 48 IN WAIT_TYPE WaitType, 49 IN BOOLEAN Alertable, 50 IN PLARGE_INTEGER TimeOut OPTIONAL) 51 { 52 PKWAIT_BLOCK WaitBlockArray; 53 HANDLE Handles[MAXIMUM_WAIT_OBJECTS], KernelHandle; 54 PVOID Objects[MAXIMUM_WAIT_OBJECTS]; 55 PVOID WaitObjects[MAXIMUM_WAIT_OBJECTS]; 56 ULONG i, ReferencedObjects, j; 57 KPROCESSOR_MODE PreviousMode; 58 LARGE_INTEGER SafeTimeOut; 59 BOOLEAN LockInUse; 60 PHANDLE_TABLE_ENTRY HandleEntry; 61 POBJECT_HEADER ObjectHeader; 62 PHANDLE_TABLE HandleTable; 63 ACCESS_MASK GrantedAccess; 64 PVOID DefaultObject; 65 NTSTATUS Status; 66 PAGED_CODE(); 67 68 /* Check for valid Object Count */ 69 if ((ObjectCount > MAXIMUM_WAIT_OBJECTS) || !(ObjectCount)) 70 { 71 /* Fail */ 72 return STATUS_INVALID_PARAMETER_1; 73 } 74 75 /* Check for valid Wait Type */ 76 if ((WaitType != WaitAll) && (WaitType != WaitAny)) 77 { 78 /* Fail */ 79 return STATUS_INVALID_PARAMETER_3; 80 } 81 82 /* Enter SEH */ 83 PreviousMode = ExGetPreviousMode(); 84 _SEH2_TRY 85 { 86 /* Probe for user mode */ 87 if (PreviousMode != KernelMode) 88 { 89 /* Check if we have a timeout */ 90 if (TimeOut) 91 { 92 /* Make a local copy of the timeout on the stack */ 93 SafeTimeOut = ProbeForReadLargeInteger(TimeOut); 94 TimeOut = &SafeTimeOut; 95 } 96 97 /* Probe all the handles */ 98 ProbeForRead(HandleArray, 99 ObjectCount * sizeof(HANDLE), 100 sizeof(HANDLE)); 101 } 102 103 /* 104 * Make a copy so we don't have to guard with SEH later and keep 105 * track of what objects we referenced if dereferencing pointers 106 * suddenly fails 107 */ 108 RtlCopyMemory(Handles, 109 HandleArray, 110 ObjectCount * sizeof(HANDLE)); 111 } 112 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) //ExSystemExceptionFilter() 113 { 114 /* Cover up for kernel mode */ 115 if (PreviousMode == KernelMode) 116 { 117 /* But don't fail silently */ 118 DbgPrint("Mon dieu! Covering up for BAD driver passing invalid pointer (0x%p)! Hon hon hon!\n", HandleArray); 119 } 120 121 /* Return the exception code */ 122 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 123 } 124 _SEH2_END; 125 126 /* Check if we can use the internal Wait Array */ 127 if (ObjectCount > THREAD_WAIT_OBJECTS) 128 { 129 /* Allocate from Pool */ 130 WaitBlockArray = ExAllocatePoolWithTag(NonPagedPool, 131 ObjectCount * 132 sizeof(KWAIT_BLOCK), 133 TAG_WAIT); 134 if (!WaitBlockArray) 135 { 136 /* Fail */ 137 return STATUS_INSUFFICIENT_RESOURCES; 138 } 139 } 140 else 141 { 142 /* No need for the array */ 143 WaitBlockArray = NULL; 144 } 145 146 /* Enter a critical region since we'll play with handles */ 147 LockInUse = TRUE; 148 KeEnterCriticalRegion(); 149 150 /* Start the loop */ 151 i = 0; 152 ReferencedObjects = 0; 153 do 154 { 155 /* Use the right Executive Handle */ 156 if (ObpIsKernelHandle(Handles[i], PreviousMode)) 157 { 158 /* Use the System Handle Table and decode */ 159 HandleTable = ObpKernelHandleTable; 160 KernelHandle = ObKernelHandleToHandle(Handles[i]); 161 162 /* Get a pointer to it */ 163 HandleEntry = ExMapHandleToPointer(HandleTable, KernelHandle); 164 } 165 else 166 { 167 /* Use the Process' Handle table and get the Ex Handle */ 168 HandleTable = PsGetCurrentProcess()->ObjectTable; 169 170 /* Get a pointer to it */ 171 HandleEntry = ExMapHandleToPointer(HandleTable, Handles[i]); 172 } 173 174 /* Check if we have an entry */ 175 if (!HandleEntry) 176 { 177 /* Fail, handle is invalid */ 178 Status = STATUS_INVALID_HANDLE; 179 DPRINT1("Invalid handle %p passed to NtWaitForMultipleObjects\n", Handles[i]); 180 goto Quickie; 181 } 182 183 /* Check for synchronize access */ 184 GrantedAccess = HandleEntry->GrantedAccess; 185 if ((PreviousMode != KernelMode) && (!(GrantedAccess & SYNCHRONIZE))) 186 { 187 /* Unlock the entry and fail */ 188 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 189 DPRINT1("Handle does not have SYNCHRONIZE access\n"); 190 Status = STATUS_ACCESS_DENIED; 191 goto Quickie; 192 } 193 194 /* Get the Object Header */ 195 ObjectHeader = ObpGetHandleObject(HandleEntry); 196 197 /* Get default Object */ 198 DefaultObject = ObjectHeader->Type->DefaultObject; 199 200 /* Check if it's the internal offset */ 201 if (IsPointerOffset(DefaultObject)) 202 { 203 /* Increase reference count */ 204 InterlockedIncrementSizeT(&ObjectHeader->PointerCount); 205 ReferencedObjects++; 206 207 /* Save the Object and Wait Object, this is a relative offset */ 208 Objects[i] = &ObjectHeader->Body; 209 WaitObjects[i] = (PVOID)((ULONG_PTR)&ObjectHeader->Body + 210 (ULONG_PTR)DefaultObject); 211 } 212 else 213 { 214 /* This is our internal Object */ 215 ReferencedObjects++; 216 Objects[i] = NULL; 217 WaitObjects[i] = DefaultObject; 218 } 219 220 /* Unlock the Handle Table Entry */ 221 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 222 223 /* Keep looping */ 224 i++; 225 } while (i < ObjectCount); 226 227 /* For a Waitall, we can't have the same object more then once */ 228 if (WaitType == WaitAll) 229 { 230 /* Clear the main loop variable */ 231 i = 0; 232 233 /* Start the loop */ 234 do 235 { 236 /* Check the current and forward object */ 237 for (j = i + 1; j < ObjectCount; j++) 238 { 239 /* Make sure they don't match */ 240 if (WaitObjects[i] == WaitObjects[j]) 241 { 242 /* Fail */ 243 Status = STATUS_INVALID_PARAMETER_MIX; 244 DPRINT1("Passed a duplicate object to NtWaitForMultipleObjects\n"); 245 goto Quickie; 246 } 247 } 248 249 /* Keep looping */ 250 i++; 251 } while (i < ObjectCount); 252 } 253 254 /* Now we can finally wait. Always use SEH since it can raise an exception */ 255 _SEH2_TRY 256 { 257 /* We're done playing with handles */ 258 LockInUse = FALSE; 259 KeLeaveCriticalRegion(); 260 261 /* Do the kernel wait */ 262 Status = KeWaitForMultipleObjects(ObjectCount, 263 WaitObjects, 264 WaitType, 265 UserRequest, 266 PreviousMode, 267 Alertable, 268 TimeOut, 269 WaitBlockArray); 270 } 271 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_MUTANT_LIMIT_EXCEEDED) ? 272 EXCEPTION_EXECUTE_HANDLER : 273 EXCEPTION_CONTINUE_SEARCH) 274 { 275 /* Get the exception code */ 276 Status = _SEH2_GetExceptionCode(); 277 } 278 _SEH2_END; 279 280 Quickie: 281 /* First derefence */ 282 while (ReferencedObjects) 283 { 284 /* Decrease the number of objects */ 285 ReferencedObjects--; 286 287 /* Check if we had a valid object in this position */ 288 if (Objects[ReferencedObjects]) 289 { 290 /* Dereference it */ 291 ObDereferenceObject(Objects[ReferencedObjects]); 292 } 293 } 294 295 /* Free wait block array */ 296 if (WaitBlockArray) ExFreePoolWithTag(WaitBlockArray, TAG_WAIT); 297 298 /* Re-enable APCs if needed */ 299 if (LockInUse) KeLeaveCriticalRegion(); 300 301 /* Return status */ 302 return Status; 303 } 304 305 /*++ 306 * @name NtWaitForMultipleObjects32 307 * @implemented NT5.1 308 * 309 * The NtWaitForMultipleObjects32 routine <FILLMEIN> 310 * 311 * @param ObjectCount 312 * <FILLMEIN> 313 * 314 * @param HandleArray 315 * <FILLMEIN> 316 * 317 * @param WaitType 318 * <FILLMEIN> 319 * 320 * @param Alertable 321 * <FILLMEIN> 322 * 323 * @param TimeOut 324 * <FILLMEIN> 325 * 326 * @return STATUS_SUCCESS or appropriate error value. 327 * 328 * @remarks None. 329 * 330 *--*/ 331 NTSTATUS 332 NTAPI 333 NtWaitForMultipleObjects32(IN ULONG ObjectCount, 334 IN PLONG Handles, 335 IN WAIT_TYPE WaitType, 336 IN BOOLEAN Alertable, 337 IN PLARGE_INTEGER TimeOut OPTIONAL) 338 { 339 /* FIXME WOW64 */ 340 return NtWaitForMultipleObjects(ObjectCount, 341 (PHANDLE)Handles, 342 WaitType, 343 Alertable, 344 TimeOut); 345 } 346 347 /*++ 348 * @name NtWaitForSingleObject 349 * @implemented NT4 350 * 351 * The NtWaitForSingleObject routine <FILLMEIN> 352 * 353 * @param ObjectHandle 354 * <FILLMEIN> 355 * 356 * @param Alertable 357 * <FILLMEIN> 358 * 359 * @param TimeOut 360 * <FILLMEIN> 361 * 362 * @return STATUS_SUCCESS or appropriate error value. 363 * 364 * @remarks None. 365 * 366 *--*/ 367 NTSTATUS 368 NTAPI 369 NtWaitForSingleObject(IN HANDLE ObjectHandle, 370 IN BOOLEAN Alertable, 371 IN PLARGE_INTEGER TimeOut OPTIONAL) 372 { 373 PVOID Object, WaitableObject; 374 KPROCESSOR_MODE PreviousMode; 375 LARGE_INTEGER SafeTimeOut; 376 NTSTATUS Status; 377 378 /* Check if we came with a timeout from user mode */ 379 PreviousMode = ExGetPreviousMode(); 380 if ((TimeOut) && (PreviousMode != KernelMode)) 381 { 382 /* Enter SEH for proving */ 383 _SEH2_TRY 384 { 385 /* Make a copy on the stack */ 386 SafeTimeOut = ProbeForReadLargeInteger(TimeOut); 387 TimeOut = &SafeTimeOut; 388 } 389 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 390 { 391 /* Return the exception code */ 392 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 393 } 394 _SEH2_END; 395 } 396 397 /* Get the Object */ 398 Status = ObReferenceObjectByHandle(ObjectHandle, 399 SYNCHRONIZE, 400 NULL, 401 PreviousMode, 402 &Object, 403 NULL); 404 if (NT_SUCCESS(Status)) 405 { 406 /* Get the Waitable Object */ 407 WaitableObject = OBJECT_TO_OBJECT_HEADER(Object)->Type->DefaultObject; 408 409 /* Is it an offset for internal objects? */ 410 if (IsPointerOffset(WaitableObject)) 411 { 412 /* Turn it into a pointer */ 413 WaitableObject = (PVOID)((ULONG_PTR)Object + 414 (ULONG_PTR)WaitableObject); 415 } 416 417 /* SEH this since it can also raise an exception */ 418 _SEH2_TRY 419 { 420 /* Ask the kernel to do the wait */ 421 Status = KeWaitForSingleObject(WaitableObject, 422 UserRequest, 423 PreviousMode, 424 Alertable, 425 TimeOut); 426 } 427 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_MUTANT_LIMIT_EXCEEDED) ? 428 EXCEPTION_EXECUTE_HANDLER : 429 EXCEPTION_CONTINUE_SEARCH) 430 { 431 /* Get the exception code */ 432 Status = _SEH2_GetExceptionCode(); 433 } 434 _SEH2_END; 435 436 /* Dereference the Object */ 437 ObDereferenceObject(Object); 438 } 439 else 440 { 441 DPRINT1("Failed to reference the handle with status 0x%x\n", Status); 442 } 443 444 /* Return the status */ 445 return Status; 446 } 447 448 /*++ 449 * @name NtSignalAndWaitForSingleObject 450 * @implemented NT4 451 * 452 * The NtSignalAndWaitForSingleObject routine <FILLMEIN> 453 * 454 * @param ObjectHandleToSignal 455 * <FILLMEIN> 456 * 457 * @param WaitableObjectHandle 458 * <FILLMEIN> 459 * 460 * @param Alertable 461 * <FILLMEIN> 462 * 463 * @param TimeOut 464 * <FILLMEIN> 465 * 466 * @return STATUS_SUCCESS or appropriate error value. 467 * 468 * @remarks None. 469 * 470 *--*/ 471 NTSTATUS 472 NTAPI 473 NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal, 474 IN HANDLE WaitableObjectHandle, 475 IN BOOLEAN Alertable, 476 IN PLARGE_INTEGER TimeOut OPTIONAL) 477 { 478 KPROCESSOR_MODE PreviousMode; 479 POBJECT_TYPE Type; 480 PVOID SignalObj, WaitObj, WaitableObject; 481 LARGE_INTEGER SafeTimeOut; 482 OBJECT_HANDLE_INFORMATION HandleInfo; 483 NTSTATUS Status; 484 485 /* Check if we came with a timeout from user mode */ 486 PreviousMode = ExGetPreviousMode(); 487 if ((TimeOut) && (PreviousMode != KernelMode)) 488 { 489 /* Enter SEH for probing */ 490 _SEH2_TRY 491 { 492 /* Make a copy on the stack */ 493 SafeTimeOut = ProbeForReadLargeInteger(TimeOut); 494 TimeOut = &SafeTimeOut; 495 } 496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 497 { 498 /* Return the exception code */ 499 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 500 } 501 _SEH2_END; 502 } 503 504 /* Start by getting the signal object*/ 505 Status = ObReferenceObjectByHandle(ObjectHandleToSignal, 506 0, 507 NULL, 508 PreviousMode, 509 &SignalObj, 510 &HandleInfo); 511 if (!NT_SUCCESS(Status)) return Status; 512 513 /* Now get the wait object */ 514 Status = ObReferenceObjectByHandle(WaitableObjectHandle, 515 SYNCHRONIZE, 516 NULL, 517 PreviousMode, 518 &WaitObj, 519 NULL); 520 if (!NT_SUCCESS(Status)) 521 { 522 /* Failed to reference the wait object */ 523 ObDereferenceObject(SignalObj); 524 return Status; 525 } 526 527 /* Get the real waitable object */ 528 WaitableObject = OBJECT_TO_OBJECT_HEADER(WaitObj)->Type->DefaultObject; 529 530 /* Handle internal offset */ 531 if (IsPointerOffset(WaitableObject)) 532 { 533 /* Get real pointer */ 534 WaitableObject = (PVOID)((ULONG_PTR)WaitObj + 535 (ULONG_PTR)WaitableObject); 536 } 537 538 /* Check Signal Object Type */ 539 Type = OBJECT_TO_OBJECT_HEADER(SignalObj)->Type; 540 if (Type == ExEventObjectType) 541 { 542 /* Check if we came from user-mode without the right access */ 543 if ((PreviousMode != KernelMode) && 544 !(HandleInfo.GrantedAccess & EVENT_MODIFY_STATE)) 545 { 546 /* Fail: lack of rights */ 547 Status = STATUS_ACCESS_DENIED; 548 goto Quickie; 549 } 550 551 /* Set the Event */ 552 KeSetEvent(SignalObj, EVENT_INCREMENT, TRUE); 553 } 554 else if (Type == ExMutantObjectType) 555 { 556 /* This can raise an exception */ 557 _SEH2_TRY 558 { 559 /* Release the mutant */ 560 KeReleaseMutant(SignalObj, MUTANT_INCREMENT, FALSE, TRUE); 561 } 562 _SEH2_EXCEPT(((_SEH2_GetExceptionCode() == STATUS_ABANDONED) || 563 (_SEH2_GetExceptionCode() == STATUS_MUTANT_NOT_OWNED)) ? 564 EXCEPTION_EXECUTE_HANDLER : 565 EXCEPTION_CONTINUE_SEARCH) 566 { 567 /* Get the exception code */ 568 Status = _SEH2_GetExceptionCode(); 569 } 570 _SEH2_END; 571 } 572 else if (Type == ExSemaphoreObjectType) 573 { 574 /* Check if we came from user-mode without the right access */ 575 if ((PreviousMode != KernelMode) && 576 !(HandleInfo.GrantedAccess & SEMAPHORE_MODIFY_STATE)) 577 { 578 /* Fail: lack of rights */ 579 Status = STATUS_ACCESS_DENIED; 580 goto Quickie; 581 } 582 583 /* This can raise an exception*/ 584 _SEH2_TRY 585 { 586 /* Release the semaphore */ 587 KeReleaseSemaphore(SignalObj, SEMAPHORE_INCREMENT, 1, TRUE); 588 } 589 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_SEMAPHORE_LIMIT_EXCEEDED) ? 590 EXCEPTION_EXECUTE_HANDLER : 591 EXCEPTION_CONTINUE_SEARCH) 592 { 593 /* Get the exception code */ 594 Status = _SEH2_GetExceptionCode(); 595 } 596 _SEH2_END; 597 } 598 else 599 { 600 /* This isn't a valid object to be waiting on */ 601 Status = STATUS_OBJECT_TYPE_MISMATCH; 602 } 603 604 /* Make sure we didn't fail */ 605 if (NT_SUCCESS(Status)) 606 { 607 /* SEH this since it can also raise an exception */ 608 _SEH2_TRY 609 { 610 /* Perform the wait now */ 611 Status = KeWaitForSingleObject(WaitableObject, 612 UserRequest, 613 PreviousMode, 614 Alertable, 615 TimeOut); 616 } 617 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_MUTANT_LIMIT_EXCEEDED) ? 618 EXCEPTION_EXECUTE_HANDLER : 619 EXCEPTION_CONTINUE_SEARCH) 620 { 621 /* Get the exception code */ 622 Status = _SEH2_GetExceptionCode(); 623 } 624 _SEH2_END; 625 } 626 627 /* We're done here, dereference both objects */ 628 Quickie: 629 ObDereferenceObject(SignalObj); 630 ObDereferenceObject(WaitObj); 631 return Status; 632 } 633 634 /* EOF */ 635