xref: /reactos/sdk/lib/rtl/i386/except.c (revision d8c6ef5e)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS Run-Time Library
4  * PURPOSE:           User-mode exception support for IA-32
5  * FILE:              lib/rtl/i386/except.c
6  * PROGRAMERS:        Alex Ionescu (alex@relsoft.net)
7  *                    Casper S. Hornstrup (chorns@users.sourceforge.net)
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <rtl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* PUBLIC FUNCTIONS **********************************************************/
17 
18 /*
19  * @implemented
20  */
21 VOID
22 NTAPI
23 RtlGetCallersAddress(OUT PVOID *CallersAddress,
24                      OUT PVOID *CallersCaller)
25 {
26     USHORT FrameCount;
27     PVOID  BackTrace[2];
28     PULONG BackTraceHash = NULL;
29 
30     /* Get the tow back trace address */
31     FrameCount = RtlCaptureStackBackTrace(2, 2, &BackTrace[0],BackTraceHash);
32 
33     /* Only if user want it */
34     if (CallersAddress != NULL)
35     {
36         /* only when first frames exist */
37         if (FrameCount >= 1)
38         {
39             *CallersAddress = BackTrace[0];
40         }
41         else
42         {
43             *CallersAddress = NULL;
44         }
45     }
46 
47     /* Only if user want it */
48     if (CallersCaller != NULL)
49     {
50         /* only when second frames exist */
51         if (FrameCount >= 2)
52         {
53             *CallersCaller = BackTrace[1];
54         }
55         else
56         {
57             *CallersCaller = NULL;
58         }
59     }
60 }
61 
62 /*
63  * @implemented
64  */
65 BOOLEAN
66 NTAPI
67 RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
68                      IN PCONTEXT Context)
69 {
70     PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, NestedFrame = NULL;
71     DISPATCHER_CONTEXT DispatcherContext;
72     EXCEPTION_RECORD ExceptionRecord2;
73     EXCEPTION_DISPOSITION Disposition;
74     ULONG_PTR StackLow, StackHigh;
75     ULONG_PTR RegistrationFrameEnd;
76 
77     /* Perform vectored exception handling for user mode */
78     if (RtlCallVectoredExceptionHandlers(ExceptionRecord, Context))
79     {
80         /* Exception handled, now call vectored continue handlers */
81         RtlCallVectoredContinueHandlers(ExceptionRecord, Context);
82 
83         /* Continue execution */
84         return TRUE;
85     }
86 
87     /* Get the current stack limits and registration frame */
88     RtlpGetStackLimits(&StackLow, &StackHigh);
89     RegistrationFrame = RtlpGetExceptionList();
90 
91     /* Now loop every frame */
92     while (RegistrationFrame != EXCEPTION_CHAIN_END)
93     {
94         /* Registration chain entries are never NULL */
95         ASSERT(RegistrationFrame != NULL);
96 
97         /* Find out where it ends */
98         RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
99                                 sizeof(EXCEPTION_REGISTRATION_RECORD);
100 
101         /* Make sure the registration frame is located within the stack */
102         if ((RegistrationFrameEnd > StackHigh) ||
103             ((ULONG_PTR)RegistrationFrame < StackLow) ||
104             ((ULONG_PTR)RegistrationFrame & 0x3))
105         {
106             /* Check if this happened in the DPC Stack */
107             if (RtlpHandleDpcStackException(RegistrationFrame,
108                                             RegistrationFrameEnd,
109                                             &StackLow,
110                                             &StackHigh))
111             {
112                 /* Use DPC Stack Limits and restart */
113                 continue;
114             }
115 
116             /* Set invalid stack and bail out */
117             ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
118             return FALSE;
119         }
120 
121         //
122         // TODO: Implement and call here RtlIsValidHandler(RegistrationFrame->Handler)
123         // for supporting SafeSEH functionality, see the following articles:
124         // https://www.optiv.com/blog/old-meets-new-microsoft-windows-safeseh-incompatibility
125         // https://msrc-blog.microsoft.com/2012/01/10/more-information-on-the-impact-of-ms12-001/
126         //
127 
128         /* Check if logging is enabled */
129         RtlpCheckLogException(ExceptionRecord,
130                               Context,
131                               RegistrationFrame,
132                               sizeof(*RegistrationFrame));
133 
134         /* Call the handler */
135         Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
136                                                      RegistrationFrame,
137                                                      Context,
138                                                      &DispatcherContext,
139                                                      RegistrationFrame->Handler);
140 
141         /* Check if this is a nested frame */
142         if (RegistrationFrame == NestedFrame)
143         {
144             /* Mask out the flag and the nested frame */
145             ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
146             NestedFrame = NULL;
147         }
148 
149         /* Handle the dispositions */
150         switch (Disposition)
151         {
152             /* Continue execution */
153             case ExceptionContinueExecution:
154             {
155                 /* Check if it was non-continuable */
156                 if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
157                 {
158                     /* Set up the exception record */
159                     ExceptionRecord2.ExceptionRecord = ExceptionRecord;
160                     ExceptionRecord2.ExceptionCode =
161                         STATUS_NONCONTINUABLE_EXCEPTION;
162                     ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
163                     ExceptionRecord2.NumberParameters = 0;
164 
165                     /* Raise the exception */
166                     RtlRaiseException(&ExceptionRecord2);
167                 }
168                 else
169                 {
170                     /* In user mode, call any registered vectored continue handlers */
171                     RtlCallVectoredContinueHandlers(ExceptionRecord, Context);
172 
173                     /* Execution continues */
174                     return TRUE;
175                 }
176             }
177 
178             /* Continue searching */
179             case ExceptionContinueSearch:
180                 if (ExceptionRecord->ExceptionFlags & EXCEPTION_STACK_INVALID)
181                 {
182                     /* We have an invalid stack, bail out */
183                     return FALSE;
184                 }
185                 break;
186 
187             /* Nested exception */
188             case ExceptionNestedException:
189             {
190                 /* Turn the nested flag on */
191                 ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
192 
193                 /* Update the current nested frame */
194                 if (DispatcherContext.RegistrationPointer > NestedFrame)
195                 {
196                     /* Get the frame from the dispatcher context */
197                     NestedFrame = DispatcherContext.RegistrationPointer;
198                 }
199                 break;
200             }
201 
202             /* Anything else */
203             default:
204             {
205                 /* Set up the exception record */
206                 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
207                 ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
208                 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
209                 ExceptionRecord2.NumberParameters = 0;
210 
211                 /* Raise the exception */
212                 RtlRaiseException(&ExceptionRecord2);
213                 break;
214             }
215         }
216 
217         /* Go to the next frame */
218         RegistrationFrame = RegistrationFrame->Next;
219     }
220 
221     /* Unhandled, bail out */
222     return FALSE;
223 }
224 
225 /*
226  * @implemented
227  */
228 VOID
229 NTAPI
230 RtlUnwind(IN PVOID TargetFrame OPTIONAL,
231           IN PVOID TargetIp OPTIONAL,
232           IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
233           IN PVOID ReturnValue)
234 {
235     PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, OldFrame;
236     DISPATCHER_CONTEXT DispatcherContext;
237     EXCEPTION_RECORD ExceptionRecord2, ExceptionRecord3;
238     EXCEPTION_DISPOSITION Disposition;
239     ULONG_PTR StackLow, StackHigh;
240     ULONG_PTR RegistrationFrameEnd;
241     CONTEXT LocalContext;
242     PCONTEXT Context;
243 
244     /* Get the current stack limits */
245     RtlpGetStackLimits(&StackLow, &StackHigh);
246 
247     /* Check if we don't have an exception record */
248     if (!ExceptionRecord)
249     {
250         /* Overwrite the argument */
251         ExceptionRecord = &ExceptionRecord3;
252 
253         /* Setup a local one */
254         ExceptionRecord3.ExceptionFlags = 0;
255         ExceptionRecord3.ExceptionCode = STATUS_UNWIND;
256         ExceptionRecord3.ExceptionRecord = NULL;
257         ExceptionRecord3.ExceptionAddress = _ReturnAddress();
258         ExceptionRecord3.NumberParameters = 0;
259     }
260 
261     /* Check if we have a frame */
262     if (TargetFrame)
263     {
264         /* Set it as unwinding */
265         ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING;
266     }
267     else
268     {
269         /* Set the Exit Unwind flag as well */
270         ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING |
271                                             EXCEPTION_EXIT_UNWIND);
272     }
273 
274     /* Now capture the context */
275     Context = &LocalContext;
276     LocalContext.ContextFlags = CONTEXT_INTEGER |
277                                 CONTEXT_CONTROL |
278                                 CONTEXT_SEGMENTS;
279     RtlpCaptureContext(Context);
280 
281     /* Pop the current arguments off */
282     Context->Esp += sizeof(TargetFrame) +
283                     sizeof(TargetIp) +
284                     sizeof(ExceptionRecord) +
285                     sizeof(ReturnValue);
286 
287     /* Set the new value for EAX */
288     Context->Eax = (ULONG)ReturnValue;
289 
290     /* Get the current frame */
291     RegistrationFrame = RtlpGetExceptionList();
292 
293     /* Now loop every frame */
294     while (RegistrationFrame != EXCEPTION_CHAIN_END)
295     {
296         /* Registration chain entries are never NULL */
297         ASSERT(RegistrationFrame != NULL);
298 
299         /* If this is the target */
300         if (RegistrationFrame == TargetFrame) ZwContinue(Context, FALSE);
301 
302         /* Check if the frame is too low */
303         if ((TargetFrame) &&
304             ((ULONG_PTR)TargetFrame < (ULONG_PTR)RegistrationFrame))
305         {
306             /* Create an invalid unwind exception */
307             ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
308             ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
309             ExceptionRecord2.ExceptionRecord = ExceptionRecord;
310             ExceptionRecord2.NumberParameters = 0;
311 
312             /* Raise the exception */
313             RtlRaiseException(&ExceptionRecord2);
314         }
315 
316         /* Find out where it ends */
317         RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
318                                sizeof(EXCEPTION_REGISTRATION_RECORD);
319 
320         /* Make sure the registration frame is located within the stack */
321         if ((RegistrationFrameEnd > StackHigh) ||
322             ((ULONG_PTR)RegistrationFrame < StackLow) ||
323             ((ULONG_PTR)RegistrationFrame & 0x3))
324         {
325             /* Check if this happened in the DPC Stack */
326             if (RtlpHandleDpcStackException(RegistrationFrame,
327                                             RegistrationFrameEnd,
328                                             &StackLow,
329                                             &StackHigh))
330             {
331                 /* Use DPC Stack Limits and restart */
332                 continue;
333             }
334 
335             /* Create an invalid stack exception */
336             ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK;
337             ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
338             ExceptionRecord2.ExceptionRecord = ExceptionRecord;
339             ExceptionRecord2.NumberParameters = 0;
340 
341             /* Raise the exception */
342             RtlRaiseException(&ExceptionRecord2);
343         }
344         else
345         {
346             /* Call the handler */
347             Disposition = RtlpExecuteHandlerForUnwind(ExceptionRecord,
348                                                       RegistrationFrame,
349                                                       Context,
350                                                       &DispatcherContext,
351                                                       RegistrationFrame->Handler);
352 
353             switch(Disposition)
354             {
355                 /* Continue searching */
356                 case ExceptionContinueSearch:
357                     break;
358 
359                 /* Collision */
360                 case ExceptionCollidedUnwind:
361                 {
362                     /* Get the original frame */
363                     RegistrationFrame = DispatcherContext.RegistrationPointer;
364                     break;
365                 }
366 
367                 /* Anything else */
368                 default:
369                 {
370                     /* Set up the exception record */
371                     ExceptionRecord2.ExceptionRecord = ExceptionRecord;
372                     ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
373                     ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
374                     ExceptionRecord2.NumberParameters = 0;
375 
376                     /* Raise the exception */
377                     RtlRaiseException(&ExceptionRecord2);
378                     break;
379                 }
380             }
381 
382             /* Go to the next frame */
383             OldFrame = RegistrationFrame;
384             RegistrationFrame = RegistrationFrame->Next;
385 
386             /* Remove this handler */
387             RtlpSetExceptionList(OldFrame);
388         }
389     }
390 
391     /* Check if we reached the end */
392     if (TargetFrame == EXCEPTION_CHAIN_END)
393     {
394         /* Unwind completed, so we don't exit */
395         ZwContinue(Context, FALSE);
396     }
397     else
398     {
399         /* This is an exit_unwind or the frame wasn't present in the list */
400         ZwRaiseException(ExceptionRecord, Context, FALSE);
401     }
402 }
403 
404 /* EOF */
405