xref: /reactos/sdk/lib/rtl/vectoreh.c (revision 4561998a)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS sysem libraries
4  * PURPOSE:           Vectored Exception Handling
5  * FILE:              lib/rtl/vectoreh.c
6  * PROGRAMERS:        Thomas Weidenmueller
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <rtl.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 RTL_CRITICAL_SECTION RtlpVectoredHandlerLock;
17 LIST_ENTRY RtlpVectoredExceptionList, RtlpVectoredContinueList;
18 
19 typedef struct _RTL_VECTORED_HANDLER_ENTRY
20 {
21     LIST_ENTRY ListEntry;
22     PVECTORED_EXCEPTION_HANDLER VectoredHandler;
23     ULONG Refs;
24 } RTL_VECTORED_HANDLER_ENTRY, *PRTL_VECTORED_HANDLER_ENTRY;
25 
26 /* FUNCTIONS ***************************************************************/
27 
28 VOID
29 NTAPI
30 RtlpInitializeVectoredExceptionHandling(VOID)
31 {
32     /* Initialize our two lists and the common lock */
33     RtlInitializeCriticalSection(&RtlpVectoredHandlerLock);
34     InitializeListHead(&RtlpVectoredExceptionList);
35     InitializeListHead(&RtlpVectoredContinueList);
36 }
37 
38 BOOLEAN
39 NTAPI
40 RtlpCallVectoredHandlers(IN PEXCEPTION_RECORD ExceptionRecord,
41                          IN PCONTEXT Context,
42                          IN PLIST_ENTRY VectoredHandlerList)
43 {
44     PLIST_ENTRY CurrentEntry;
45     PRTL_VECTORED_HANDLER_ENTRY VectoredExceptionHandler;
46     PVECTORED_EXCEPTION_HANDLER VectoredHandler;
47     EXCEPTION_POINTERS ExceptionInfo;
48     BOOLEAN HandlerRemoved;
49     LONG HandlerReturn;
50 
51     /*
52      * Initialize these in case there are no entries,
53      * or if no one handled the exception
54      */
55     HandlerRemoved = FALSE;
56     HandlerReturn = EXCEPTION_CONTINUE_SEARCH;
57 
58     /* Set up the data to pass to the handler */
59     ExceptionInfo.ExceptionRecord = ExceptionRecord;
60     ExceptionInfo.ContextRecord = Context;
61 
62     /* Grab the lock */
63     RtlEnterCriticalSection(&RtlpVectoredHandlerLock);
64 
65     /* Loop entries */
66     CurrentEntry = VectoredHandlerList->Flink;
67     while (CurrentEntry != VectoredHandlerList)
68     {
69         /* Get the struct */
70         VectoredExceptionHandler = CONTAINING_RECORD(CurrentEntry,
71                                                      RTL_VECTORED_HANDLER_ENTRY,
72                                                      ListEntry);
73 
74         /* Reference it so it doesn't go away while we are using it */
75         VectoredExceptionHandler->Refs++;
76 
77         /* Drop the lock before calling the handler */
78         RtlLeaveCriticalSection(&RtlpVectoredHandlerLock);
79 
80         /*
81          * Get the function pointer, decoding it so we will crash
82          * if malicious code has altered it. That is, if something has
83          * set VectoredHandler to a non-encoded pointer
84          */
85         VectoredHandler = RtlDecodePointer(VectoredExceptionHandler->VectoredHandler);
86 
87         /* Call the handler */
88         HandlerReturn = VectoredHandler(&ExceptionInfo);
89 
90         /* Handler called -- grab the lock to dereference */
91         RtlEnterCriticalSection(&RtlpVectoredHandlerLock);
92 
93         /* Dereference and see if it got deleted */
94         VectoredExceptionHandler->Refs--;
95         if (VectoredExceptionHandler->Refs == 0)
96         {
97             /* It did -- do we have to free it now? */
98             if (HandlerReturn == EXCEPTION_CONTINUE_EXECUTION)
99             {
100                 /* We don't, just remove it from the list and break out */
101                 RemoveEntryList(&VectoredExceptionHandler->ListEntry);
102                 HandlerRemoved = TRUE;
103                 break;
104             }
105 
106             /*
107              * Get the next entry before freeing,
108              * and remove the current one from the list
109              */
110             CurrentEntry = VectoredExceptionHandler->ListEntry.Flink;
111             RemoveEntryList(&VectoredExceptionHandler->ListEntry);
112 
113             /* Free the entry outside of the lock, then reacquire it */
114             RtlLeaveCriticalSection(&RtlpVectoredHandlerLock);
115             RtlFreeHeap(RtlGetProcessHeap(),
116                         0,
117                         VectoredExceptionHandler);
118             RtlEnterCriticalSection(&RtlpVectoredHandlerLock);
119         }
120         else
121         {
122             /* No delete -- should we continue execution? */
123             if (HandlerReturn == EXCEPTION_CONTINUE_EXECUTION)
124             {
125                 /* Break out */
126                 break;
127             }
128             else
129             {
130                 /* Continue searching the list */
131                 CurrentEntry = CurrentEntry->Flink;
132             }
133         }
134     }
135 
136     /* Let go of the lock now */
137     RtlLeaveCriticalSection(&RtlpVectoredHandlerLock);
138 
139     /* Anything to free? */
140     if (HandlerRemoved)
141     {
142         /* Get rid of it */
143         RtlFreeHeap(RtlGetProcessHeap(),
144                     0,
145                     VectoredExceptionHandler);
146     }
147 
148     /* Return whether to continue execution (ignored for continue handlers) */
149     return (HandlerReturn == EXCEPTION_CONTINUE_EXECUTION) ? TRUE : FALSE;
150 }
151 
152 PVOID
153 NTAPI
154 RtlpAddVectoredHandler(IN ULONG FirstHandler,
155                        IN PVECTORED_EXCEPTION_HANDLER VectoredHandler,
156                        IN PLIST_ENTRY VectoredHandlerList)
157 {
158     PRTL_VECTORED_HANDLER_ENTRY VectoredHandlerEntry;
159 
160     /* Allocate our structure */
161     VectoredHandlerEntry = RtlAllocateHeap(RtlGetProcessHeap(),
162                                            0,
163                                            sizeof(RTL_VECTORED_HANDLER_ENTRY));
164     if (!VectoredHandlerEntry) return NULL;
165 
166     /* Set it up, encoding the pointer for security */
167     VectoredHandlerEntry->VectoredHandler = RtlEncodePointer(VectoredHandler);
168     VectoredHandlerEntry->Refs = 1;
169 
170     /* Lock the list before modifying it */
171     RtlEnterCriticalSection(&RtlpVectoredHandlerLock);
172 
173     /*
174      * While holding the list lock, insert the handler
175      * at beginning or end of list according to caller.
176      */
177     if (FirstHandler)
178     {
179         InsertHeadList(VectoredHandlerList,
180                        &VectoredHandlerEntry->ListEntry);
181     }
182     else
183     {
184         InsertTailList(VectoredHandlerList,
185                        &VectoredHandlerEntry->ListEntry);
186     }
187 
188     /* Done with the list, unlock it */
189     RtlLeaveCriticalSection(&RtlpVectoredHandlerLock);
190 
191     /* Return pointer to the structure as the handle */
192     return VectoredHandlerEntry;
193 }
194 
195 ULONG
196 NTAPI
197 RtlpRemoveVectoredHandler(IN PVOID VectoredHandlerHandle,
198                           IN PLIST_ENTRY VectoredHandlerList)
199 {
200     PLIST_ENTRY CurrentEntry;
201     PRTL_VECTORED_HANDLER_ENTRY VectoredExceptionHandler;
202     BOOLEAN HandlerRemoved;
203     BOOLEAN HandlerFound;
204 
205     /* Initialize these in case we don't find anything */
206     HandlerRemoved = FALSE;
207     HandlerFound = FALSE;
208 
209     /* Acquire list lock */
210     RtlEnterCriticalSection(&RtlpVectoredHandlerLock);
211 
212     /* Loop the list */
213     CurrentEntry = VectoredHandlerList->Flink;
214     while (CurrentEntry != VectoredHandlerList)
215     {
216         /* Get the struct */
217         VectoredExceptionHandler = CONTAINING_RECORD(CurrentEntry,
218                                                      RTL_VECTORED_HANDLER_ENTRY,
219                                                      ListEntry);
220 
221         /* Does it match? */
222         if (VectoredExceptionHandler == VectoredHandlerHandle)
223         {
224             /*
225              * Great, this means it is a valid entry.
226              * However, it may be in use by the exception
227              * dispatcher, so we have a ref count to respect.
228              * If we can't remove it now then it will be done
229              * right after it is not in use anymore.
230              *
231              * Caller is supposed to keep track of if it has deleted the
232              * entry and should not call us twice for the same entry.
233              * We could maybe throw in some kind of ASSERT to detect this
234              * if this was to become a problem.
235              */
236             VectoredExceptionHandler->Refs--;
237             if (VectoredExceptionHandler->Refs == 0)
238             {
239                 /* Not in use, ok to remove and free */
240                 RemoveEntryList(&VectoredExceptionHandler->ListEntry);
241                 HandlerRemoved = TRUE;
242             }
243 
244             /* Found what we are looking for, stop searching */
245             HandlerFound = TRUE;
246             break;
247         }
248         else
249         {
250             /* Get the next entry */
251             CurrentEntry = CurrentEntry->Flink;
252         }
253     }
254 
255     /* Done with the list, let go of the lock */
256     RtlLeaveCriticalSection(&RtlpVectoredHandlerLock);
257 
258     /* Can we free what we found? */
259     if (HandlerRemoved)
260     {
261         /* Do it */
262         RtlFreeHeap(RtlGetProcessHeap(),
263                     0,
264                     VectoredExceptionHandler);
265     }
266 
267     /* Return whether we found it */
268     return (ULONG)HandlerFound;
269 }
270 
271 BOOLEAN
272 NTAPI
273 RtlCallVectoredExceptionHandlers(IN PEXCEPTION_RECORD ExceptionRecord,
274                                  IN PCONTEXT Context)
275 {
276     /* Call the shared routine */
277     return RtlpCallVectoredHandlers(ExceptionRecord,
278                                     Context,
279                                     &RtlpVectoredExceptionList);
280 }
281 
282 VOID
283 NTAPI
284 RtlCallVectoredContinueHandlers(IN PEXCEPTION_RECORD ExceptionRecord,
285                                 IN PCONTEXT Context)
286 {
287     /*
288      * Call the shared routine (ignoring result,
289      * execution always continues at this point)
290      */
291     (VOID)RtlpCallVectoredHandlers(ExceptionRecord,
292                                    Context,
293                                    &RtlpVectoredContinueList);
294 }
295 
296 DECLSPEC_HOTPATCH
297 PVOID
298 NTAPI
299 RtlAddVectoredExceptionHandler(IN ULONG FirstHandler,
300                                IN PVECTORED_EXCEPTION_HANDLER VectoredHandler)
301 {
302     /* Call the shared routine */
303     return RtlpAddVectoredHandler(FirstHandler,
304                                   VectoredHandler,
305                                   &RtlpVectoredExceptionList);
306 }
307 
308 DECLSPEC_HOTPATCH
309 PVOID
310 NTAPI
311 RtlAddVectoredContinueHandler(IN ULONG FirstHandler,
312                               IN PVECTORED_EXCEPTION_HANDLER VectoredHandler)
313 {
314     /* Call the shared routine */
315     return RtlpAddVectoredHandler(FirstHandler,
316                                   VectoredHandler,
317                                   &RtlpVectoredContinueList);
318 }
319 
320 //DECLSPEC_HOTPATCH
321 ULONG
322 NTAPI
323 RtlRemoveVectoredExceptionHandler(IN PVOID VectoredHandlerHandle)
324 {
325     /* Call the shared routine */
326     return RtlpRemoveVectoredHandler(VectoredHandlerHandle,
327                                      &RtlpVectoredExceptionList);
328 }
329 
330 //DECLSPEC_HOTPATCH
331 ULONG
332 NTAPI
333 RtlRemoveVectoredContinueHandler(IN PVOID VectoredHandlerHandle)
334 {
335     /* Call the shared routine */
336     return RtlpRemoveVectoredHandler(VectoredHandlerHandle,
337                                      &RtlpVectoredContinueList);
338 }
339 
340 /* EOF */
341