xref: /reactos/ntoskrnl/ob/obwait.c (revision 34593d93)
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