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
NtWaitForMultipleObjects(IN ULONG ObjectCount,IN PHANDLE HandleArray,IN WAIT_TYPE WaitType,IN BOOLEAN Alertable,IN PLARGE_INTEGER TimeOut OPTIONAL)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
NtWaitForMultipleObjects32(IN ULONG ObjectCount,IN PLONG Handles,IN WAIT_TYPE WaitType,IN BOOLEAN Alertable,IN PLARGE_INTEGER TimeOut OPTIONAL)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
NtWaitForSingleObject(IN HANDLE ObjectHandle,IN BOOLEAN Alertable,IN PLARGE_INTEGER TimeOut OPTIONAL)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
NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal,IN HANDLE WaitableObjectHandle,IN BOOLEAN Alertable,IN PLARGE_INTEGER TimeOut OPTIONAL)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