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