Fix exception handling by not reseting PreviousMode to kernel when a user-mode except...
[reactos.git] / reactos / ntoskrnl / ob / wait.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ob/wait.c
5 * PURPOSE: Handles Waiting on Objects
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@mcmail.com)
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 #define TAG_WAIT TAG('W', 'a', 'i', 't')
18
19 /* FUNCTIONS *****************************************************************/
20
21 NTSTATUS
22 STDCALL
23 NtWaitForMultipleObjects(IN ULONG ObjectCount,
24 IN PHANDLE HandleArray,
25 IN WAIT_TYPE WaitType,
26 IN BOOLEAN Alertable,
27 IN PLARGE_INTEGER TimeOut OPTIONAL)
28 {
29 PKWAIT_BLOCK WaitBlockArray = NULL;
30 HANDLE Handles[MAXIMUM_WAIT_OBJECTS];
31 PVOID Objects[MAXIMUM_WAIT_OBJECTS];
32 PVOID WaitObjects[MAXIMUM_WAIT_OBJECTS];
33 ULONG i = 0, ReferencedObjects = 0, j;
34 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
35 LARGE_INTEGER SafeTimeOut;
36 BOOLEAN LockInUse;
37 PHANDLE_TABLE_ENTRY HandleEntry;
38 POBJECT_HEADER ObjectHeader;
39 PHANDLE_TABLE HandleTable;
40 ACCESS_MASK GrantedAccess;
41 PVOID DefaultObject;
42 NTSTATUS Status = STATUS_SUCCESS;
43
44 DPRINT("NtWaitForMultipleObjects(ObjectCount %lu HandleArray[] %x, Alertable %d, "
45 "TimeOut %x)\n", ObjectCount, HandleArray, Alertable, TimeOut);
46
47 /* Enter a critical region since we'll play with handles */
48 LockInUse = TRUE;
49 KeEnterCriticalRegion();
50
51 /* Check for valid Object Count */
52 if ((ObjectCount > MAXIMUM_WAIT_OBJECTS) || !ObjectCount)
53 {
54 Status = STATUS_INVALID_PARAMETER_1;
55 DPRINT1("No object count, or too many objects\n");
56 goto Quickie;
57 }
58
59 /* Check for valid Wait Type */
60 if ((WaitType != WaitAll) && (WaitType != WaitAny))
61 {
62 Status = STATUS_INVALID_PARAMETER_3;
63 DPRINT1("Invalid wait type\n");
64 goto Quickie;
65 }
66
67 /* Capture arguments */
68 _SEH_TRY
69 {
70 if(PreviousMode != KernelMode)
71 {
72 ProbeForRead(HandleArray,
73 ObjectCount * sizeof(HANDLE),
74 sizeof(HANDLE));
75
76 if(TimeOut)
77 {
78 ProbeForRead(TimeOut,
79 sizeof(LARGE_INTEGER),
80 sizeof(ULONG));
81
82 /* Make a local copy of the timeout on the stack */
83 SafeTimeOut = *TimeOut;
84 TimeOut = &SafeTimeOut;
85 }
86 }
87
88 /*
89 * Make a copy so we don't have to guard with SEH later and keep
90 * track of what objects we referenced if dereferencing pointers
91 * suddenly fails
92 */
93 RtlCopyMemory(Handles,
94 HandleArray,
95 ObjectCount * sizeof(HANDLE));
96 }
97 _SEH_HANDLE
98 {
99 Status = _SEH_GetExceptionCode();
100 }
101 _SEH_END;
102
103 if(!NT_SUCCESS(Status)) goto Quickie;
104
105 /* Check if we can use the internal Wait Array */
106 if (ObjectCount > THREAD_WAIT_OBJECTS)
107 {
108 /* Allocate from Pool */
109 WaitBlockArray = ExAllocatePoolWithTag(NonPagedPool,
110 ObjectCount * sizeof(KWAIT_BLOCK),
111 TAG_WAIT);
112 }
113
114 /* Start the loop */
115 do
116 {
117 /* Use the right Executive Handle */
118 if(ObIsKernelHandle(Handles[i], PreviousMode))
119 {
120 /* Use the System Handle Table and decode */
121 HandleTable = ObpKernelHandleTable;
122 Handles[i] = ObKernelHandleToHandle(Handles[i]);
123 }
124 else
125 {
126 /* Use the Process' Handle table and get the Ex Handle */
127 HandleTable = PsGetCurrentProcess()->ObjectTable;
128 }
129
130 /* Get a pointer to it */
131 if (!(HandleEntry = ExMapHandleToPointer(HandleTable, Handles[i])))
132 {
133 DPRINT1("Invalid handle\n");
134 Status = STATUS_INVALID_HANDLE;
135 goto Quickie;
136 }
137
138 /* Check for synchronize access */
139 GrantedAccess = HandleEntry->u2.GrantedAccess;
140 if ((PreviousMode != KernelMode) && (!(GrantedAccess & SYNCHRONIZE)))
141 {
142 /* Unlock the entry and fail */
143 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
144 Status = STATUS_ACCESS_DENIED;
145 DPRINT1("Handle doesn't have SYNCH access\n");
146 goto Quickie;
147 }
148
149 /* Get the Object Header */
150 ObjectHeader = EX_HTE_TO_HDR(HandleEntry);
151
152 /* Get default Object */
153 DefaultObject = ObjectHeader->Type->DefaultObject;
154
155 /* Check if it's the internal offset */
156 if ((LONG_PTR)DefaultObject >= 0)
157 {
158 /* Increase reference count */
159 InterlockedIncrement(&ObjectHeader->PointerCount);
160 ReferencedObjects++;
161
162 /* Save the Object and Wait Object, this is a relative offset */
163 Objects[i] = &ObjectHeader->Body;
164 WaitObjects[i] = (PVOID)((ULONG_PTR)&ObjectHeader->Body +
165 (ULONG_PTR)DefaultObject);
166 }
167 else
168 {
169 /* This is our internal Object */
170 ReferencedObjects++;
171 Objects[i] = NULL;
172 WaitObjects[i] = DefaultObject;
173 }
174
175 /* Unlock the Handle Table Entry */
176 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
177
178 /* Keep looping */
179 i++;
180 } while (i < ObjectCount);
181
182 /* For a Waitall, we can't have the same object more then once */
183 if (WaitType == WaitAll)
184 {
185 /* Start the loop */
186 do
187 {
188 /* Check the current and forward object */
189 for (i = 0, j = i + 1; j < ObjectCount; j++)
190 {
191 /* Make sure they don't match */
192 if (WaitObjects[i] == WaitObjects[j])
193 {
194 /* Fail */
195 Status = STATUS_INVALID_PARAMETER_MIX;
196 DPRINT1("Objects duplicated with WaitAll\n");
197 goto Quickie;
198 }
199 }
200
201 /* Keep looping */
202 i++;
203 } while (i < ObjectCount);
204 }
205
206 /* Now we can finally wait. Use SEH since it can raise an exception */
207 _SEH_TRY
208 {
209 /* We're done playing with handles */
210 LockInUse = FALSE;
211 KeLeaveCriticalRegion();
212
213 /* Do the kernel wait */
214 Status = KeWaitForMultipleObjects(ObjectCount,
215 WaitObjects,
216 WaitType,
217 UserRequest,
218 PreviousMode,
219 Alertable,
220 TimeOut,
221 WaitBlockArray);
222 }
223 _SEH_HANDLE
224 {
225 Status = _SEH_GetExceptionCode();
226 }
227 _SEH_END;
228
229 Quickie:
230 /* First derefence */
231 while (ReferencedObjects)
232 {
233 ReferencedObjects--;
234 if (Objects[ReferencedObjects])
235 {
236 ObDereferenceObject(Objects[ReferencedObjects]);
237 }
238 }
239
240 /* Free wait block array */
241 if (WaitBlockArray) ExFreePool(WaitBlockArray);
242
243 /* Re-enable APCs if needed */
244 if (LockInUse) KeLeaveCriticalRegion();
245
246 /* Return status */
247 return Status;
248 }
249
250 /*
251 * @implemented
252 */
253 NTSTATUS
254 STDCALL
255 NtWaitForSingleObject(IN HANDLE ObjectHandle,
256 IN BOOLEAN Alertable,
257 IN PLARGE_INTEGER TimeOut OPTIONAL)
258 {
259 PVOID Object, WaitableObject;
260 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
261 LARGE_INTEGER SafeTimeOut;
262 NTSTATUS Status = STATUS_SUCCESS;
263
264 DPRINT("NtWaitForSingleObject(ObjectHandle %x, Alertable %d, TimeOut %x)\n",
265 ObjectHandle,Alertable,TimeOut);
266
267 /* Capture timeout */
268 if(TimeOut && PreviousMode != KernelMode)
269 {
270 _SEH_TRY
271 {
272 ProbeForRead(TimeOut,
273 sizeof(LARGE_INTEGER),
274 sizeof(ULONG));
275 /* Make a copy on the stack */
276 SafeTimeOut = *TimeOut;
277 TimeOut = &SafeTimeOut;
278 }
279 _SEH_HANDLE
280 {
281 Status = _SEH_GetExceptionCode();
282 }
283 _SEH_END;
284
285 if(!NT_SUCCESS(Status)) return Status;
286 }
287
288 /* Get the Object */
289 Status = ObReferenceObjectByHandle(ObjectHandle,
290 SYNCHRONIZE,
291 NULL,
292 PreviousMode,
293 &Object,
294 NULL);
295 if (NT_SUCCESS(Status))
296 {
297 /* Get the Waitable Object */
298 WaitableObject = BODY_TO_HEADER(Object)->Type->DefaultObject;
299
300 /* Is it an offset for internal objects? */
301 if ((LONG_PTR)WaitableObject >= 0)
302 {
303 /* Turn it into a pointer */
304 WaitableObject = (PVOID)((ULONG_PTR)Object +
305 (ULONG_PTR)WaitableObject);
306 }
307
308 /* Now wait. Also SEH this since it can also raise an exception */
309 _SEH_TRY
310 {
311 Status = KeWaitForSingleObject(WaitableObject,
312 UserRequest,
313 PreviousMode,
314 Alertable,
315 TimeOut);
316 }
317 _SEH_HANDLE
318 {
319 Status = _SEH_GetExceptionCode();
320 }
321 _SEH_END;
322
323 /* Dereference the Object */
324 ObDereferenceObject(Object);
325 }
326
327 /* Return the status */
328 return Status;
329 }
330
331 NTSTATUS
332 STDCALL
333 NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal,
334 IN HANDLE WaitableObjectHandle,
335 IN BOOLEAN Alertable,
336 IN PLARGE_INTEGER TimeOut OPTIONAL)
337 {
338 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
339 POBJECT_TYPE Type;
340 PVOID SignalObj;
341 PVOID WaitObj;
342 PVOID WaitableObject;
343 LARGE_INTEGER SafeTimeOut;
344 OBJECT_HANDLE_INFORMATION HandleInfo;
345 NTSTATUS Status = STATUS_SUCCESS;
346
347 /* Capture timeout */
348 DPRINT("NtSignalAndWaitForSingleObject\n");
349 if(TimeOut && PreviousMode != KernelMode)
350 {
351 _SEH_TRY
352 {
353 ProbeForRead(TimeOut,
354 sizeof(LARGE_INTEGER),
355 sizeof(ULONG));
356 /* Make a copy on the stack */
357 SafeTimeOut = *TimeOut;
358 TimeOut = &SafeTimeOut;
359 }
360 _SEH_HANDLE
361 {
362 Status = _SEH_GetExceptionCode();
363 }
364 _SEH_END;
365
366 if(!NT_SUCCESS(Status)) return Status;
367 }
368
369 /* Start by getting the signal object*/
370 Status = ObReferenceObjectByHandle(ObjectHandleToSignal,
371 0,
372 NULL,
373 PreviousMode,
374 &SignalObj,
375 &HandleInfo);
376 if (!NT_SUCCESS(Status))
377 {
378 return Status;
379 }
380
381 /* Now get the wait object */
382 Status = ObReferenceObjectByHandle(WaitableObjectHandle,
383 SYNCHRONIZE,
384 NULL,
385 PreviousMode,
386 &WaitObj,
387 NULL);
388 if (!NT_SUCCESS(Status))
389 {
390 ObDereferenceObject(SignalObj);
391 return Status;
392 }
393
394 /* Get the real waitable object */
395 WaitableObject = BODY_TO_HEADER(WaitObj)->Type->DefaultObject;
396
397 /* Handle internal offset */
398 if ((LONG_PTR)WaitableObject >= 0)
399 {
400 /* Get real pointer */
401 WaitableObject = (PVOID)((ULONG_PTR)WaitObj +
402 (ULONG_PTR)WaitableObject);
403 }
404
405 /* Check Signal Object Type */
406 Type = BODY_TO_HEADER(WaitObj)->Type;
407 if (Type == ExEventObjectType)
408 {
409 /* Set the Event */
410 /* FIXME: Check permissions */
411 KeSetEvent(SignalObj, EVENT_INCREMENT, TRUE);
412 }
413 else if (Type == ExMutantObjectType)
414 {
415 /* Release the Mutant. This can raise an exception*/
416 _SEH_TRY
417 {
418 KeReleaseMutant(SignalObj, MUTANT_INCREMENT, FALSE, TRUE);
419 }
420 _SEH_HANDLE
421 {
422 Status = _SEH_GetExceptionCode();
423 goto Quickie;
424 }
425 _SEH_END;
426 }
427 else if (Type == ExSemaphoreObjectType)
428 {
429 /* Release the Semaphore. This can raise an exception*/
430 /* FIXME: Check permissions */
431 _SEH_TRY
432 {
433 KeReleaseSemaphore(SignalObj, SEMAPHORE_INCREMENT, 1, TRUE);
434 }
435 _SEH_HANDLE
436 {
437 Status = _SEH_GetExceptionCode();
438 goto Quickie;
439 }
440 _SEH_END;
441 }
442 else
443 {
444 Status = STATUS_OBJECT_TYPE_MISMATCH;
445 DPRINT1("Waiting on invalid object type\n");
446 goto Quickie;
447 }
448
449 /* Now wait. Also SEH this since it can also raise an exception */
450 _SEH_TRY
451 {
452 Status = KeWaitForSingleObject(WaitableObject,
453 UserRequest,
454 PreviousMode,
455 Alertable,
456 TimeOut);
457 }
458 _SEH_HANDLE
459 {
460 Status = _SEH_GetExceptionCode();
461 }
462 _SEH_END;
463
464 /* We're done here */
465 Quickie:
466 ObDereferenceObject(SignalObj);
467 ObDereferenceObject(WaitObj);
468 return Status;
469 }
470
471 /* EOF */