text: /***
  *chandler4.c - Structured Exception Handling part of _except_handler4
  *
  *       Copyright (c) Microsoft Corporation.  All rights reserved.
  *
  *Purpose:
  *       Defines _except_handler4, the language specific handler for structured
  *       exception handling on x86.  First created for Visual C++ 2005, replacing
  *       _except_handler3, with the addition of security cookie checks.
  *
  *       For the CRT DLL, this actually defines _except_handler4_common, which
  *       performs the real processing for chandler4gs.c's stub routine
  *       _except_handler4.  We need a stub that is linked statically into an
  *       image, so it can access the image-local global security cookie.  But all
  *       it needs to do is pass through to this code, sending in the address of
  *       the global cookie and the image's __security_check_cookie routine.  This
  *       larger routine can then be placed in the CRT DLL.
  *
  *******************************************************************************/
  
  #include <vcruntime_internal.h>
  #include <Windows.h>
  
  // copied from ntxcapi.h
  #define EXCEPTION_UNWINDING 0x2         // Unwind is in progress
  #define EXCEPTION_EXIT_UNWIND 0x4       // Exit unwind is in progress
  #define EXCEPTION_STACK_INVALID 0x8     // Stack out of limits or unaligned
  #define EXCEPTION_NESTED_CALL 0x10      // Nested exception handler call
  #define EXCEPTION_TARGET_UNWIND 0x20    // Target unwind in progress
  #define EXCEPTION_COLLIDED_UNWIND 0x40  // Collided exception handler call
  
  #define EXCEPTION_UNWIND (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | \
                            EXCEPTION_TARGET_UNWIND | EXCEPTION_COLLIDED_UNWIND)
  
  #define IS_UNWINDING(Flag) ((Flag & EXCEPTION_UNWIND) != 0)
  #define IS_DISPATCHING(Flag) ((Flag & EXCEPTION_UNWIND) == 0)
  #define IS_TARGET_UNWIND(Flag) (Flag & EXCEPTION_TARGET_UNWIND)
  
  BEGIN_PRAGMA_OPTIMIZE_ENABLE("t", DevDivVSO:162582, "This file is performance-critical and should always be optimized for speed")
  
  /*
   * Define __except filter/handler and __finally handler function types.
   */
  
  typedef LONG (__cdecl *PEXCEPTION_FILTER_X86)(void);
  typedef void (__cdecl *PEXCEPTION_HANDLER_X86)(void);
  typedef void (__fastcall *PTERMINATION_HANDLER_X86)(BOOL);
  
  /*
   * Define type of pointer to __security_check_cookie.
   */
  
  typedef void (__fastcall *PCOOKIE_CHECK)(UINT_PTR);
  
  /*
   * The function-specific scope table pointed to by the on-stack exception
   * registration record.  This describes the nesting structure of __try blocks
   * in the function, the location of all __except filters, __except blocks, and
   * __finally blocks, and the data required to check the security cookies in
   * the function
   */
  
  typedef struct _EH4_SCOPETABLE_RECORD
  {
      ULONG                           EnclosingLevel;
      PEXCEPTION_FILTER_X86           FilterFunc;
      union
      {
          PEXCEPTION_HANDLER_X86      HandlerAddress;
          PTERMINATION_HANDLER_X86    FinallyFunc;
      } u;
  } EH4_SCOPETABLE_RECORD, *PEH4_SCOPETABLE_RECORD;
  
  typedef struct _EH4_SCOPETABLE
  {
      ULONG                       GSCookieOffset;
      ULONG                       GSCookieXOROffset;
      ULONG                       EHCookieOffset;
      ULONG                       EHCookieXOROffset;
      EH4_SCOPETABLE_RECORD       ScopeRecord[1];
  } EH4_SCOPETABLE, *PEH4_SCOPETABLE;
  
  #define NO_GS_COOKIE        ((ULONG)-2)
  
  #define TOPMOST_TRY_LEVEL   ((ULONG)-2)
  
  /*
   * The exception registration record stored in the stack frame.  The linked
   * list of registration records goes through the EXCEPTION_REGISTRATION_RECORD
   * sub-struct, so some fields here are at negative offsets with regards to
   * the registration record pointer we are passed.
   */
  
  typedef struct _EH4_EXCEPTION_REGISTRATION_RECORD
  {
      PVOID                           SavedESP;
      PEXCEPTION_POINTERS             ExceptionPointers;
      EXCEPTION_REGISTRATION_RECORD   SubRecord;
      UINT_PTR                        EncodedScopeTable;
      ULONG                           TryLevel;
  } EH4_EXCEPTION_REGISTRATION_RECORD, *PEH4_EXCEPTION_REGISTRATION_RECORD;
  
  /*
   * External dependencies not found in headers.
   */
  
  #pragma warning(disable: 4132)  // communal object is intentionally const
  void (__cdecl * const _pDestructExceptionObject)(
      PEXCEPTION_RECORD   pExcept,
      int                 fThrowNotAllowed
      );
  
  #define EH_EXCEPTION_NUMBER ('msc' | 0xE0000000)
  
  extern LONG __fastcall
  _EH4_CallFilterFunc(
      _In_ PEXCEPTION_FILTER_X86  FilterFunc,
      _In_ PCHAR                  FramePointer
      );
  
  extern void __declspec(noreturn) __fastcall
  _EH4_TransferToHandler(
      _In_ PEXCEPTION_HANDLER_X86 HandlerAddress,
      _In_ PCHAR                  FramePointer
      );
  
  extern void __fastcall
  _EH4_GlobalUnwind2(
      _In_opt_ PEXCEPTION_REGISTRATION_RECORD  EstablisherFrame,
      _In_opt_ PEXCEPTION_RECORD               ExceptionRecord
      );
  
  extern void __fastcall
  _EH4_LocalUnwind(
      _In_ PEXCEPTION_REGISTRATION_RECORD  EstablisherFrame,
      _In_ ULONG                           TargetLevel,
      _In_ PCHAR                           FramePointer,
      _In_ PUINT_PTR                       CookiePointer
      );
  
  #pragma warning(disable: 4100)  // ignore unreferenced formal parameters
  
  #ifdef _M_HYBRID_X86_ARM64
  
  #pragma intrinsic(_HybridGenerateThunks)
  
  //
  // Generate wrappers for routines implemented in asm.
  //
  
  LONG __fastcall
  _EH4_CallFilterFunc(
      PEXCEPTION_FILTER_X86   FilterFunc,
      PCHAR                   FramePointer
      )
  {
      _HybridGenerateThunks(_EH4_CallFilterFunc, 1);
      return 0;
  }
  
  void __declspec(noreturn) __fastcall
  _EH4_TransferToHandler(
      PEXCEPTION_HANDLER_X86  HandlerAddress,
      PCHAR                   FramePointer
      )
  {
      _HybridGenerateThunks(_EH4_TransferToHandler, 1);
  }
  
  void __fastcall
  _EH4_GlobalUnwind2(
      PEXCEPTION_REGISTRATION_RECORD  EstablisherFrame,
      PEXCEPTION_RECORD               ExceptionRecord
      )
  {
      _HybridGenerateThunks(_EH4_GlobalUnwind2, 1);
  }
  
  void __fastcall
  _EH4_LocalUnwind(
      PEXCEPTION_REGISTRATION_RECORD  EstablisherFrame,
      ULONG                           TargetLevel,
      PCHAR                           FramePointer,
      PUINT_PTR                       CookiePointer
      )
  {
      _HybridGenerateThunks(_EH4_LocalUnwind, 1);
  }
  
  #endif
  
  extern void
  __except_validate_context_record(
      _In_ PCONTEXT ContextRecord
      );
  
  /***
  *ValidateLocalCookies - perform local cookie validation during SEH processing
  *
  *Purpose:
  *   Perform the security checks for _except_handler4.
  *
  *Entry:
  *   CookieCheckFunction - (CRT DLL only) pointer to __security_check_cookie in
  *       the target image
  *   ScopeTable - pointer to the unencoded scope table from the exception
  *       registration record
  *   FramePointer - EBP frame pointer for the function that established the
  *       exception registration record
  *
  *Return:
  *   If the security checks fail, the process is terminated via a Watson dump.
  *
  *******************************************************************************/
  
  static __declspec(guard(ignore)) void
  ValidateLocalCookies(
  #if defined(CRTDLL)
      IN PCOOKIE_CHECK    CookieCheckFunction,
  #endif
      IN PEH4_SCOPETABLE  ScopeTable,
      _When_(ScopeTable->GSCookieOffset >= ScopeTable->EHCookieOffset,
          _In_reads_bytes_(ScopeTable->GSCookieOffset + sizeof(UINT_PTR)))
      _When_(ScopeTable->GSCookieOffset <  ScopeTable->EHCookieOffset,
          _In_reads_bytes_(ScopeTable->EHCookieOffset + sizeof(UINT_PTR)))
         PCHAR            FramePointer
      )
  {
      UINT_PTR        GSCookie;
      UINT_PTR        EHCookie;
  
      if (ScopeTable->GSCookieOffset != NO_GS_COOKIE)
      {
          GSCookie = *(PUINT_PTR)(FramePointer + ScopeTable->GSCookieOffset);
          GSCookie ^= (UINT_PTR)(FramePointer + ScopeTable->GSCookieXOROffset);
  #if defined(CRTDLL)
          _GUARD_CHECK_ICALL(CookieCheckFunction);
          (*CookieCheckFunction)(GSCookie);
  #else
          __security_check_cookie(GSCookie);
  #endif
      }
  
      EHCookie = *(PUINT_PTR)(FramePointer + ScopeTable->EHCookieOffset);
      EHCookie ^= (UINT_PTR)(FramePointer + ScopeTable->EHCookieXOROffset);
  #if defined(CRTDLL)
      _GUARD_CHECK_ICALL(CookieCheckFunction);
      (*CookieCheckFunction)(EHCookie);
  #else
      __security_check_cookie(EHCookie);
  #endif
  }
  
  /***
  *_except_handler4 - (non-CRT DLL only) SEH handler with security cookie checks
  *
  *_except_handler4_common - (CRT DLL only) actual SEH implementation called by
  *                          _except_handler4 stub
  *
  *Purpose:
  *   Implement structured exception handling for functions which have __try/
  *   __except/__finally.  This version of SEH also performs security cookie
  *   checks to detect buffer overruns which potentially corrupt the on-stack
  *   exception handling data, terminating the process before such corruption
  *   can be exploited.
  *
  *   Call exception and termination handlers as necessary, based on the current
  *   execution point within the function.
  *
  *Entry:
  *   CookiePointer - (CRT DLL only) pointer to the global security cookie to be
  *       used to decode the scope table pointer in the exception registration
  *       record
  *   CookieCheckFunction - (CRT DLL only) pointer to __security_check_cookie in
  *       the calling image
  *   ExceptionRecord - pointer to the exception being dispatched
  *   EstablisherFrame - pointer to the on-stack exception registration record
  *       for this function
  *   ContextRecord - pointer to a context record for the point of exception
  *   DispatcherContext - pointer to the exception dispatcher or unwind
  *       dispatcher context
  *
  *Return:
  *   If the security checks fail, the process is terminated via a Watson dump.
  *
  *   If an exception is being dispatched and the exception is handled by an
  *   __except filter for this exception frame, then this function does not
  *   return.  Instead, it calls RtlUnwind and transfers control to the __except
  *   block corresponding to the accepting __except filter.  Otherwise, an
  *   exception disposition of continue execution or continue search is returned.
  *
  *   If an unwind is being dispatched, then each termination handler (__finally)
  *   is called and a value of continue search is returned.
  *
  *******************************************************************************/
  
  DECLSPEC_GUARD_SUPPRESS
  EXCEPTION_DISPOSITION
  #if !defined(CRTDLL)
  _except_handler4(
  #else
  _except_handler4_common(
      IN PUINT_PTR                        CookiePointer,
      IN PCOOKIE_CHECK                    CookieCheckFunction,
  #endif
      IN PEXCEPTION_RECORD                ExceptionRecord,
      IN PEXCEPTION_REGISTRATION_RECORD   EstablisherFrame,
      IN OUT PCONTEXT                     ContextRecord,
      IN OUT PVOID                        DispatcherContext
      )
  {
      PEH4_EXCEPTION_REGISTRATION_RECORD  RegistrationNode;
      PCHAR                               FramePointer;
      PEH4_SCOPETABLE                     ScopeTable;
      ULONG                               TryLevel;
      ULONG                               EnclosingLevel;
      EXCEPTION_POINTERS                  ExceptionPointers;
      PEH4_SCOPETABLE_RECORD              ScopeTableRecord;
      PEXCEPTION_FILTER_X86               FilterFunc;
      LONG                                FilterResult;
      BOOLEAN                             Revalidate = FALSE;
      EXCEPTION_DISPOSITION               Disposition = ExceptionContinueSearch;
  
  #ifdef _M_HYBRID_X86_ARM64
  
      //
      // Ensure that this function is implemented as guest code so that there are
      // no dynamic EH registration nodes added for push thunk frames.
      //
  
  #if defined(CRTDLL)
      _HybridGenerateThunks(_except_handler4_common, 1);
  #else
      _HybridGenerateThunks(_except_handler4, 1);
  #endif
  #endif
  
      //
      // We are passed a registration record which is a field offset from the
      // start of our true registration record.
      //
  
      RegistrationNode =
          (PEH4_EXCEPTION_REGISTRATION_RECORD)
          ( (PCHAR)EstablisherFrame -
            FIELD_OFFSET(EH4_EXCEPTION_REGISTRATION_RECORD, SubRecord) );
  
      //
      // The EBP frame pointer in the function corresponding to the registration
      // record will be immediately following the record.  If the function uses
      // FPO, this is a "virtual" frame pointer outside of exception handling,
      // but it's still the EBP value set when calling into the handlers or
      // filters.
      //
  
      FramePointer = (PCHAR)(RegistrationNode + 1);
  
      //
      // Retrieve the scope table pointer, which encodes where we find the local
      // security cookies within the function's frame, as well as how the guarded
      // blocks in the target function are laid out.  This pointer was XORed with
      // the image-local global security cookie when originally stored, to avoid
      // attacks which spoof the table to address valid local cookies elsewhere
      // on the stack.
      //
  
  #if defined(CRTDLL)
      ScopeTable = (PEH4_SCOPETABLE)
                      (RegistrationNode->EncodedScopeTable ^ *CookiePointer);
  #else
      ScopeTable = (PEH4_SCOPETABLE)
                      (RegistrationNode->EncodedScopeTable ^ __security_cookie);
  #endif
  
      //
      // Perform the initial security cookie validation.
      //
  
      ValidateLocalCookies(
  #if defined(CRTDLL)
          CookieCheckFunction,
  #endif
          ScopeTable,
          FramePointer
          );
  
      __except_validate_context_record(ContextRecord);
  
      //
      // Security checks have passed, begin actual exception handling.
      //
  
      if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags))
      {
          //
          // An exception dispatch is in progress.  First build the
          // EXCEPTION_POINTERS record queried by the _exception_info intrinsic
          // and save it in the exception registration record so the __except
          // filter can find it.
          //
  
          ExceptionPointers.ExceptionRecord = ExceptionRecord;
          ExceptionPointers.ContextRecord = ContextRecord;
          RegistrationNode->ExceptionPointers = &ExceptionPointers;
  
          //
          // Scan the scope table and call the appropriate __except filters until
          // we find one that accepts the exception.
          //
  
          for (TryLevel = RegistrationNode->TryLevel;
               TryLevel != TOPMOST_TRY_LEVEL;
               TryLevel = EnclosingLevel)
          {
              ScopeTableRecord = &ScopeTable->ScopeRecord[TryLevel];
              FilterFunc = ScopeTableRecord->FilterFunc;
              EnclosingLevel = ScopeTableRecord->EnclosingLevel;
  
              if (FilterFunc != NULL)
              {
                  //
                  // The current scope table record is for an __except.
                  // Call the __except filter to see if we've found an
                  // accepting handler.
                  //
  
                  FilterResult = _EH4_CallFilterFunc(FilterFunc, FramePointer);
                  Revalidate = TRUE;
  
                  //
                  // If the __except filter returned a negative result, then
                  // dismiss the exception.  If it returned a positive result,
                  // unwind to the accepting exception handler.  Otherwise keep
                  // searching for an exception filter.
                  //
  
                  if (FilterResult < 0)
                  {
                      Disposition = ExceptionContinueExecution;
                      break;
                  }
                  else if (FilterResult > 0)
                  {
  #if !defined(_NTSUBSET_)
                      //
                      // If we're handling a thrown C++ exception, let the C++
                      // exception handler destruct the thrown object.  This call
                      // is through a function pointer to avoid linking to the
                      // C++ EH support unless it's already present.  Don't call
                      // the function pointer unless it's in read-only memory.
                      //
  
                      if (ExceptionRecord->ExceptionCode == EH_EXCEPTION_NUMBER &&
                          _pDestructExceptionObject != NULL &&
                          _IsNonwritableInCurrentImage((PBYTE)&_pDestructExceptionObject))
                      {
                          (*_pDestructExceptionObject)(ExceptionRecord, TRUE);
                      }
  #endif  // !defined(_NTSUBSET_)
  
  
  
                      //
                      // Unwind all registration nodes below this one, then unwind
                      // the nested __try levels.
                      //
                      _EH4_GlobalUnwind2(
                          &RegistrationNode->SubRecord,
                          ExceptionRecord
                          );
  
                      if (RegistrationNode->TryLevel != TryLevel)
                      {
                          _EH4_LocalUnwind(
                              &RegistrationNode->SubRecord,
                              TryLevel,
                              FramePointer,
  #if defined(CRTDLL)
                              CookiePointer
  #else
                              &__security_cookie
  #endif
                              );
                      }
  
                      //
                      // Set the __try level to the enclosing level, since it is
                      // the enclosing level, if any, that guards the __except
                      // handler.
                      //
  
                      RegistrationNode->TryLevel = EnclosingLevel;
  
                      //
                      // Redo the security checks, in case any __except filters
                      // or __finally handlers have caused overruns in the target
                      // frame.
                      //
  
                      ValidateLocalCookies(
  #if defined(CRTDLL)
                          CookieCheckFunction,
  #endif
                          ScopeTable,
                          FramePointer
                          );
  
                      //
                      // Call the __except handler.  This call will not return.
                      // The __except handler will reload ESP from the
                      // registration record upon entry.  The EBP frame pointer
                      // for the handler is directly after the registration node.
                      //
  
                      _EH4_TransferToHandler(
                          ScopeTableRecord->u.HandlerAddress,
                          FramePointer
                          );
                  }
              }
          }
      }
      else
      {
          //
          // An exception unwind is in progress, and this isn't the target of the
          // unwind.  Unwind any active __try levels in this function, calling
          // the applicable __finally handlers.
          //
  
          if (RegistrationNode->TryLevel != TOPMOST_TRY_LEVEL)
          {
              _EH4_LocalUnwind(
                  &RegistrationNode->SubRecord,
                  TOPMOST_TRY_LEVEL,
                  FramePointer,
  #if defined(CRTDLL)
                  CookiePointer
  #else
                  &__security_cookie
  #endif
                  );
              Revalidate = TRUE;
          }
      }
  
      //
      // If we called any __except filters or __finally handlers, then redo the
      // security checks, in case those funclets have caused overruns in the
      // target frame.
      //
  
      if (Revalidate)
      {
          ValidateLocalCookies(
  #if defined(CRTDLL)
              CookieCheckFunction,
  #endif
              ScopeTable,
              FramePointer
              );
      }
  
      //
      // Continue searching for exception or termination handlers in previous
      // registration records higher up the stack, or resume execution if we're
      // here because an __except filter returned a negative result.
      //
  
      return Disposition;
  }
  