/*static char *SCCSID = "@(#)xcptsubr.c 6.9 91/08/01";*/
/*      SCCSID = @(#)xcptsubr.c 6.9 91/08/01 */

/****************************************************************************/
/*                                                                          */
/*                       IBM/Microsoft Confidential                         */
/*                                                                          */
/*                 Copyright (c) IBM Corporation  1987, 1990                */
/*                 Copyright (c) Microsoft Corp.  1987, 1990                */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/

#define INCL_DOSERRORS
#define INCL_DOSPROCESS
#define INCL_DOSMEMMGR
#define INCL_DOSEXCEPTIONS
#define INCL_NOPMAPI
#define INCL_OS2STD

/*
 * The following defines create the correct names for Dos* while still
 * allowing use of definitions from os2.h
 */

#define DosSetMem DOS32SETMEM
#define DosQueryMem DOS32QUERYMEM
#define DosGetInfoBlocks DOS32GETINFOBLOCKS
#define DosRaiseException DOS32RAISEEXCEPTION
#define DosSetExceptionHandler DOS32SETEXCEPTIONHANDLER
#define DosUnsetExceptionHandler DOS32UNSETEXCEPTIONHANDLER

#include <os2.h>
#include <bsedosp.h>

#include <r3except.h>

/***LP  xcptR3ExceptionDispatcher - Exception disptacher
 *
 *      xcptR3ExceptionDispatcher traverses the chain of exception handlers
 *      until the exception is handled by one or there are none left.
 *
 *      ENTRY   pERepRec        Exception Report Record
 *              pContext        Exception Context Record
 *
 *      EXIT    Success TRUE
 *              Failure FALSE
 */

ULONG xcptR3ExceptionDispatcher( PEXCEPTIONREPORTRECORD pERepRec,
                                 PCONTEXTRECORD pContext)
{
   DISPATCHERCONTEXT DispatcherContext; // For nested exceptions
   ULONG ulDisposition;                 // Disposition
   PEXCEPTIONREGISTRATIONRECORD pCurrentRegistrationRecord; // For while loop
   PEXCEPTIONREGISTRATIONRECORD pNestedRegistration;    // For nested flag
   EXCEPTIONREPORTRECORD ReportRecord;  // For non-continuable & invalid disp.
   ULONG rc;
   ULONG SigFTerm = 0;                                                   //@SEC

   if (pERepRec->fHandlerFlags & EH_STACK_INVALID) {
      return(XCPT_CONTINUE_SEARCH);
   }

   pCurrentRegistrationRecord = xcptGetRegistrationHead();

   /*
    * If the nested bit is set coming in to the dispatcher, then this
    * is a 'nested' XCPT_PROCESS_TERMINATE and the last nested record
    * should be END_OF_CHAIN.
    */
   pNestedRegistration = (pERepRec->fHandlerFlags & EH_NESTED_CALL) ?
      END_OF_CHAIN : (PEXCEPTIONREGISTRATIONRECORD) NULL;

   SigFTerm = pERepRec->fHandlerFlags & EH_SIGFTERM;                     //@SEC

   while (pCurrentRegistrationRecord != END_OF_CHAIN) {

      /*
       * If the registration record is invalid (address is bad, next
       * registration record address is bad, or handler address is bad)
       * quit dispatching and return XCPT_CONTINUE_SEARCH so that the
       * initial exception is displayed in register dump.  Otherwise,
       * execute handler.
       */

      if (!xcptVerifyRegistrationRecord(pCurrentRegistrationRecord)) {

//        ReportRecord.ExceptionNum = XCPT_BAD_STACK;
//        ReportRecord.fHandlerFlags = EH_NONCONTINUABLE | EH_STACK_INVALID;
//        ReportRecord.NestedExceptionReportRecord = pERepRec;
//        ReportRecord.ExceptionAddress = (PVOID) 0;
//        ReportRecord.cParameters = 0;
//
//        DosRaiseException(&ReportRecord);

          return (XCPT_CONTINUE_SEARCH);

      } else {

         ulDisposition = xcptExecuteUserExceptionHandler(pERepRec,
          pCurrentRegistrationRecord, pContext, &DispatcherContext,
          xcptR3ExceptionHandler);

         /*
          * If coming out of a nested exception section, clear the flag.
          */
         if (pNestedRegistration == pCurrentRegistrationRecord) {
            pERepRec->fHandlerFlags &= ~EH_NESTED_CALL;
            pNestedRegistration = (PEXCEPTIONREGISTRATIONRECORD) NULL;
         }

         switch (ulDisposition) {

            /*
             * If exception has been handled (XCPT_CONTINUE_EXECUTION)
             * then make sure that is a legitimate action.
             */

         case XCPT_CONTINUE_EXECUTION:
            if (SigFTerm) {                                              //@SEC
               ulDisposition = XCPT_CONTINUE_SEARCH;                     //@SEC
               break;                                                    //@SEC
            }                                                            //@SEC
            if (pERepRec->fHandlerFlags & EH_NONCONTINUABLE) {
               ReportRecord.ExceptionNum = XCPT_NONCONTINUABLE_EXCEPTION;
               ReportRecord.fHandlerFlags = EH_NONCONTINUABLE;
               ReportRecord.NestedExceptionReportRecord = pERepRec;
               ReportRecord.ExceptionAddress = (PVOID) 0;
               ReportRecord.cParameters = 0;

               DosRaiseException(&ReportRecord);

            } else {
               return (ulDisposition);
            }

            /*
             * If the handler didn't handle the exception, just try
             * the next handler.
             */

         case XCPT_CONTINUE_SEARCH:
            break;

            /*
             * If the return disposition is NESTED, then set the nested
             * flag and perhaps bump the end of the nested section up
             * the stack.
             */

         case NESTED:
            pERepRec->fHandlerFlags |= EH_NESTED_CALL;
            if (DispatcherContext.pRegistrationPointer > pNestedRegistration) {
               pNestedRegistration = DispatcherContext.pRegistrationPointer;
            }
            break;

            /*
             * Any other disposition is invalid.
             */

         default:
            ReportRecord.ExceptionNum = XCPT_INVALID_DISPOSITION;
            ReportRecord.fHandlerFlags = EH_NONCONTINUABLE;
            ReportRecord.NestedExceptionReportRecord = pERepRec;
            ReportRecord.ExceptionAddress = (PVOID) 0;
            ReportRecord.cParameters = 0;

            DosRaiseException(&ReportRecord);
         }

         /*
          * Make sure the chain goes in the right direction.
          */

         if ((pCurrentRegistrationRecord != END_OF_CHAIN) &&
             !(xcptCheckPrevRegistrationRecord(pCurrentRegistrationRecord,
                                               pCurrentRegistrationRecord->prev_structure))) {

            ReportRecord.ExceptionNum = XCPT_BAD_STACK;
            ReportRecord.fHandlerFlags = EH_NONCONTINUABLE | EH_STACK_INVALID;
            if (SigFTerm)                                                //@SEC
               ReportRecord.fHandlerFlags |= EH_SIGFTERM;                //@SEC
            ReportRecord.NestedExceptionReportRecord = pERepRec;
            ReportRecord.ExceptionAddress = (PVOID) 0;
            ReportRecord.cParameters = 0;

            DosRaiseException(&ReportRecord);

         } else {

            pCurrentRegistrationRecord = pCurrentRegistrationRecord->prev_structure;

         }
      }
   }
   return (XCPT_CONTINUE_SEARCH);

}

/***EP  xcptUnwindDispatcher - System unwind dispatcher
 *
 *      xcptUnwindDispatcher will call each exception handler in turn
 *      during an unwind. If an exit unwind (pERegRec = NULL) is
 *      performed, the thread will die after all exception records
 *      have been unwound.
 *
 *      ENTRY   pERegRec        - Registration record to unwind to.
 *              TargetIP        - Address to return control to after unwind
 *              pERepRec        - Exception report record (optional)
 *
 *      EXIT    Exception records up to pERegRec are removed.
 *              Execution is resumed at TargetIP, unless an exit unwind
 *              was performed.
 */

void xcptUnwindDispatcher( PEXCEPTIONREGISTRATIONRECORD pTargetRegRec,
                           PVOID TargetIP,
                           PEXCEPTIONREPORTRECORD pERepRec,
                           PCONTEXTRECORD pContext)
{
   PCONTEXTRECORD ContextRecord;
   CONTEXTRECORD ContextRecord1;
   DISPATCHERCONTEXT DispatcherContext;
   ULONG ulDisposition;
   PEXCEPTIONREGISTRATIONRECORD RegistrationPointer;
   PEXCEPTIONREGISTRATIONRECORD PriorPointer;
   ULONG HighAddress;
   ULONG HighLimit;
   ULONG LowLimit;
   EXCEPTIONREPORTRECORD ExceptionRecord1;
   EXCEPTIONREPORTRECORD ExceptionRecord2;

   /*
    * If an exception record is not specified, then build a local exception
    * record for use in calling exception handlers during the unwind operation.
    */

   if (pERepRec == NULL) {
      pERepRec = &ExceptionRecord1;
      ExceptionRecord1.ExceptionNum = XCPT_UNWIND;
      ExceptionRecord1.fHandlerFlags = 0;
      ExceptionRecord1.NestedExceptionReportRecord = NULL;
      ExceptionRecord1.ExceptionAddress = (PVOID) pContext->ctx_RegEip;
      ExceptionRecord1.cParameters = 0;
   }

   if (pERepRec->fHandlerFlags & EH_STACK_INVALID) {
      return;
   }

   /*
    * If the target frame of the unwind is specified, then set EXCEPTION_UNWINDING
    * flag in the exception flags and verify that the frame is present in the
    * exception handler list. Otherwise set both EXCEPTION_EXIT_UNWIND and
    * EXCEPTION_UNWINDING flags in the exception flags.
    */

   if (pTargetRegRec == NULL) {
      pERepRec->fHandlerFlags |= (EH_UNWINDING | EH_EXIT_UNWIND);
   } else {
      pERepRec->fHandlerFlags |= EH_UNWINDING;

      if (pTargetRegRec != END_OF_CHAIN)
      {
          RegistrationPointer = xcptGetRegistrationHead();
          while (RegistrationPointer != END_OF_CHAIN &&
                 RegistrationPointer != pTargetRegRec &&
                 xcptVerifyRegistrationRecord(RegistrationPointer)) {
              RegistrationPointer = RegistrationPointer->prev_structure;
          }

          /*
           * If the target frame of the unwind is not present in the
           * exception handler list, then raise STATUS_INVALID_UNWIND
           * exception.
           */
          if (RegistrationPointer != pTargetRegRec) {
              ExceptionRecord2.ExceptionNum = XCPT_INVALID_UNWIND_TARGET;
              ExceptionRecord2.fHandlerFlags = EH_NONCONTINUABLE;
              ExceptionRecord2.NestedExceptionReportRecord = pERepRec;
              ExceptionRecord2.ExceptionAddress = (PVOID) 0;
              ExceptionRecord2.cParameters = 0;

              DosRaiseException(&ExceptionRecord2);
          }
      }
   }


   RegistrationPointer = xcptGetRegistrationHead();

   /*
    * Scan backward through the call frame hierarchy, calling exception
    * handlers as they are encountered, until the target frame of the unwind
    * is reached.
    */

   while (RegistrationPointer != END_OF_CHAIN) {

      /*
       * If this is the target of the unwind, then continue execution
       * by calling the continue system service.
       */

      if ((ULONG)RegistrationPointer == (ULONG)pTargetRegRec) {
         (PVOID)pContext->ctx_RegEip = TargetIP;
         DOS32EXCEPTIONCALLBACK(EXCSW, pERepRec, pContext, XCPT_CONTINUE_EXECUTION);

         /*
          * We'll never actually get here.
          */

         /*
          * If the target frame is lower in the stack than the current frame,
          * then raise STATUS_INVALID_UNWIND exception.
          */

      } else if ((pTargetRegRec != NULL) &&
                 !(xcptCheckPrevRegistrationRecord( RegistrationPointer,
                                                    pTargetRegRec ))) {
         ExceptionRecord2.ExceptionNum = XCPT_INVALID_UNWIND_TARGET;
         ExceptionRecord2.fHandlerFlags = EH_NONCONTINUABLE;
         ExceptionRecord2.NestedExceptionReportRecord = pERepRec;
         ExceptionRecord2.ExceptionAddress = (PVOID) 0;
         ExceptionRecord2.cParameters = 0;

         DosRaiseException(&ExceptionRecord2);
      }

      /*
       * If the registration record is invalid (address is bad, next
       * registration record address is bad, or handler address is bad)
       * quit unwinding and pass EXIT_UNWIND back to kernel so that the
       * initial exception is displayed in register dump.  Otherwise,
       * execute handler.
       */

      if (!xcptVerifyRegistrationRecord(RegistrationPointer)) {

//        ExceptionRecord2.ExceptionNum = XCPT_BAD_STACK;
//        ExceptionRecord2.fHandlerFlags = EH_NONCONTINUABLE | EH_STACK_INVALID;
//        ExceptionRecord2.NestedExceptionReportRecord = pERepRec;
//        ExceptionRecord2.ExceptionAddress = (PVOID) 0;
//        ExceptionRecord2.cParameters = 0;
//
//        DosRaiseException(&ExceptionRecord2);

          DOS32EXCEPTIONCALLBACK(EXCSW, pERepRec, pContext, EXIT_UNWIND);

      } else {

         ulDisposition = xcptExecuteUserExceptionHandler(pERepRec,
                         (PVOID)RegistrationPointer,
                         pContext,
                         (PVOID)&DispatcherContext,
                         xcptR3UnwindHandler);

         pERepRec->fHandlerFlags &= ~EH_NESTED_CALL;

         /*
          * Case on the handler disposition.
          */

         switch (ulDisposition) {

            /*
             * The disposition is to continue the search. Get next
             * frame address and continue the search.
             */

         case XCPT_CONTINUE_SEARCH:
            break;

            /*
             * The disposition is colided unwind. Maximize the target
             * of the unwind and change the context record pointer.
             */

         case COLLIDED_UNWIND:

            /*
             * Pick up the registration pointer that was active at
             * the time of the unwind, and simply continue.
             */

            RegistrationPointer = DispatcherContext.pRegistrationPointer;
            pERepRec->fHandlerFlags |= EH_NESTED_CALL;
            break;


            /*
             * All other disposition values are invalid. Raise
             * invalid disposition exception.
             */

         default :
            ExceptionRecord2.ExceptionNum = XCPT_INVALID_DISPOSITION;
            ExceptionRecord2.fHandlerFlags = EH_NONCONTINUABLE;
            ExceptionRecord2.NestedExceptionReportRecord = pERepRec;
            ExceptionRecord2.ExceptionAddress = (PVOID) 0;
            ExceptionRecord2.cParameters = 0;

            DosRaiseException(&ExceptionRecord2);
            break;
         }

         /*
          * Step to next registration record
          */

         PriorPointer = RegistrationPointer;

         /*
          * Unlink the unwind handler, since it's been called.
          */
         RegistrationPointer = xcptRemoveRegistrationHead();

         /*
          * If chain goes in wrong direction or loops, raise an
          * exception.
          */

         if ((RegistrationPointer != END_OF_CHAIN) &&
             !(xcptCheckPrevRegistrationRecord( PriorPointer,
                                                RegistrationPointer))) {
               ExceptionRecord2.ExceptionNum = XCPT_BAD_STACK;
               ExceptionRecord2.fHandlerFlags = EH_NONCONTINUABLE | EH_STACK_INVALID;
               ExceptionRecord2.NestedExceptionReportRecord = pERepRec;
               ExceptionRecord2.ExceptionAddress = (PVOID) 0;
               ExceptionRecord2.cParameters = 0;

               DosRaiseException(&ExceptionRecord2);
         }
      }
   }

   if (pTargetRegRec == END_OF_CHAIN) {

      /*
       *  Caller simply wants to unwind all exception records.
       *  This differs from an exit_unwind in that no "exit" is desired.
       *  Do a normal continue, since we've effectively found the
       *  "target" the caller wanted. We'll unwind to the TargetIP by
       *  setting EIP in the context record here.
       */

      (PVOID)pContext->ctx_RegEip = TargetIP;

      DOS32EXCEPTIONCALLBACK(EXCSW, pERepRec, pContext, XCPT_CONTINUE_EXECUTION);

   } else {

      /*
       *  Either (1) a real exit unwind was performed, or (2) the
       *  specified TargetFrame is not present in the exception handler
       *  list.  In either case, give debugger and subsystem a chance
       *  to see the unwind.
       */

      DOS32EXCEPTIONCALLBACK(EXCSW, pERepRec, pContext, EXIT_UNWIND);

   }
   return;
}

/***LP  xcptExecuteUserExceptionHandler - Run the user's exception handler
 *
 *      xcptExecuteUserExceptionHandler registers and exception handler
 *      to catch nested exceptions or unwinds, saves the state of the
 *      non-continuable bit, and then runs the handler passed in.  On
 *      exit, it restores the state of the non-continuable bit and
 *      removes the EH it installed.
 *
 *      ENTRY   pERepRec        Exception Report Record
 *              pERegRec        Exception Registration Record
 *              pContext        Exception Context record
 *              pDisContext     Ptr to store dispatcher context data
 *              pNestCatcher    Ptr to appropriate nested exception catcher
 *
 *      EXIT    Return value from handler
 */

ULONG xcptExecuteUserExceptionHandler(PEXCEPTIONREPORTRECORD pERepRec,
                                      PEXCEPTIONREGISTRATIONRECORD pERegRec,
                                      PCONTEXTRECORD pContext,
                                      PDISPATCHERCONTEXT pDisContext,
                                      PEXCEPTIONHANDLER pNestCatcher)
{
   DISPATCHERREGISTRATION NestedHandler;
   ULONG rc;
   ULONG ulNonContinuable;


   NestedHandler.pRegistrationPointer = (PDISPATCHERCONTEXT) pERegRec;
   (PEXCEPTIONHANDLER) NestedHandler.ERegRec.ExceptionHandler = pNestCatcher;
   DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&NestedHandler);

   ulNonContinuable = pERepRec->fHandlerFlags & EH_NONCONTINUABLE;

   rc = ((ULONG (*)(PEXCEPTIONREPORTRECORD, PEXCEPTIONREGISTRATIONRECORD,
                  PCONTEXTRECORD, PDISPATCHERCONTEXT))
    pERegRec->ExceptionHandler)(pERepRec, pERegRec, pContext, pDisContext);

   pERepRec->fHandlerFlags &= ~EH_NONCONTINUABLE;
   pERepRec->fHandlerFlags |= ulNonContinuable;

   DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&NestedHandler);

   return (rc);
}

/***LP   xcptR3ExceptionHandler - Catches nested exceptions.
 *
 *       xcptR3ExceptionHandler returns a special code if it detects
 *       a nested exception. No special action is taken during an
 *       unwind.
 *
 *       ENTRY   pERepRec   - Exception report record
 *               pERegRec   - Exception registration record
 *               pContext   - Context record
 *               ppDispatcherContext - Filled in w/ nesting information
 *
 *       EXIT    Success - XCPT_CONTINUE_SEARCH (no nesting)
 *                         NESTED (nesting)
 */

ULONG _cdecl xcptR3ExceptionHandler( PEXCEPTIONREPORTRECORD pERepRec,
                              PDISPATCHERREGISTRATION pERegRec,
                              PCONTEXTRECORD pContext,
                              PDISPATCHERCONTEXT *ppDispatcherContext)

{
   if (pERepRec->fHandlerFlags & EH_UNWINDING) {
      return (XCPT_CONTINUE_SEARCH);
   } else {
      *ppDispatcherContext = pERegRec->pRegistrationPointer;

      return (NESTED);
   }
}

/***LP   xcptR3UnwindHandler - Catches nested Unwinds.
 *
 *       xcptR3UnwindHandler returns a special code if it detects
 *       a nested Unwind. No special action is taken during an
 *       exception.
 *
 *       ENTRY   pERepRec   - Exception report record
 *               pERegRec   - Exception registration record
 *               pContext   - Context record
 *               ppDispatcherContext - Filled in w/ nesting information
 *
 *       EXIT    Success - XCPT_CONTINUE_SEARCH (no nesting)
 *                         COLLIDED_UNWIND (nesting)
 */

ULONG _cdecl xcptR3UnwindHandler( PEXCEPTIONREPORTRECORD pERepRec,
                           PDISPATCHERREGISTRATION pERegRec,
                           PCONTEXTRECORD pContext,
                           PDISPATCHERCONTEXT *ppDispatcherContext)

{
   if (pERepRec->fHandlerFlags & EH_UNWINDING) {
      *ppDispatcherContext = pERegRec->pRegistrationPointer;
      return (COLLIDED_UNWIND);
   } else {
      return (XCPT_CONTINUE_SEARCH);
   }
}

/*************************** START OF SPECIFICATION *****************
 *
 * Function Name: xcptGPFDefaultAction
 *
 * Descriptive Name: Guard Page Fault Default Action
 *
 * Function: Mark the page below the page (page-1) that got the guard
 *      page fault as guarded.  If this is successful, return NO_ERROR
 *      otherwise return NO_INVALID_ACCESS.
 *
 * Notes: Guard page faults are handled entirely at ring 3.
 *
 * Input: pXcpt           - ptr to an ExceptionStructure
 *
 * Exit Normal: NO_ERROR          - default action suceeeded
 *
 * Exit Error: NO_INVALID_ACCESS  - default action failed
 *
 *************************** END OF SPECIFICATION *******************/

ULONG xcptGPFDefaultAction( PVOID FaultAddr )
{
    ULONG   page_sz;
    ULONG   access_info, guard_page;

    /*
     * Check if the faulting address is the base page of the object.
     * If so, then we cannot guard page-1.
     */
    (ULONG)FaultAddr &= ~(PAGESIZE-1); /* page align faulting addresss */
    page_sz = PAGESIZE;                /* query one page */
    if ((DosQueryMem(FaultAddr, &page_sz, &access_info) != NO_ERROR) ||
        (access_info & PAG_BASE))
    {
        return ERROR_INVALID_ACCESS;
    }

    /*
     * Get attribute for page we want to mark guarded (page-1).
     */
    (ULONG)FaultAddr -= PAGESIZE;
    page_sz = PAGESIZE; /* query one page */
    if (DosQueryMem(FaultAddr, &page_sz, &guard_page) != NO_ERROR)
    {
        return ERROR_INVALID_ACCESS;
    }

    /*
     * Mark page-1 as guarded.
     */
    access_info &= PAG_READ+PAG_WRITE;
    access_info |= (guard_page & PAG_COMMIT ? PAG_GUARD : PAG_GUARD+PAG_COMMIT);
    if (DosSetMem(FaultAddr, PAGESIZE, access_info) != NO_ERROR)
    {
        return ERROR_INVALID_ACCESS;
    }

    return NO_ERROR;   /* page-1 marked guarded */
}

/*************************** START OF SPECIFICATION *****************
 *
 * Function Name: xcptVerifyRegistrationRecord
 *
 * Descriptive Name: Verify Registration Record and Contents
 *
 * Function: Make sure that the registration record is on the stack,
 *      the previous registration record (if one exists) is on the
 *      stack, and the exception handler is in a code segment.
 *
 * Input: pERegRec        - ptr to an ExceptionRegistrationRecord
 *
 * Exit: TRUE             - registration record valid
 *       FALSE            - registration record invalid
 *
 *************************** END OF SPECIFICATION *******************/

typedef struct _THUNKSTACKHEADER
{
    ULONG NextStack;                    // Pointer to next thread stack
    ULONG ThreadId;                     // Thread Id

    ULONG ThunkDepth;                   // Count of nested Thunks
    ULONG RogueDepth;                   // Count of nested Rogue Thunks

    ULONG SystemESP;                    // Current ESP on System Stack
    ULONG UserESP;                      // Current ESP on User Stack

    ULONG UserStack;                    // User Stack pointer from TIB
    ULONG UserLimit;                    // User Stack limit from TIB

} THUNKSTACKHEADER;

extern PVOID *pThunkTLM;                // Pointer to Thunk Thread Local Memory

BOOL xcptVerifyRegistrationRecord( PEXCEPTIONREGISTRATIONRECORD pERegRec )
{
    BOOL rc = TRUE;
    ULONG ulHighStack, ulLowStack;      // Stack limits
    ULONG ulSize = 1L, ulFlags;         // Memory flags
    THUNKSTACKHEADER *pTSH;

    xcptGetStackLimits( &ulHighStack, &ulLowStack );
    ulHighStack -= sizeof(EXCEPTIONREGISTRATIONRECORD);

// Ensure that this reregistration record address is between the two stack
// pointers in the TIB.  If not, it must be between the two stack pointers
// for the alternate stack used by the thunks.

    if ( (ULONG)pERegRec < ulLowStack
         || (ULONG)pERegRec > ulHighStack )        // MN@88976
    {
        // Record not on Current Stack

        if ( pThunkTLM && *pThunkTLM )
        {
            pTSH = (THUNKSTACKHEADER *)( (ULONG)(*pThunkTLM) + 0x0000EFE0 );

            if ( ((ULONG)pERegRec < pTSH->UserStack ||
                  (ULONG)pERegRec > pTSH->UserLimit ) &&
                 ((ULONG)pERegRec < (ULONG)(*pThunkTLM) ||
                  (ULONG)pERegRec > (ULONG)pTSH ) )
            {
                // Record not on User Stack either
                // Record not on System Stack either
                rc = FALSE;
            }
        }
        else
        {
            rc = FALSE;
        }
    }

// Make sure the handler address is valid and in a code segment.

    if ( rc )                           // Not yet invalid
    {
        if ( ( DosQueryMem( (PVOID)pERegRec->ExceptionHandler, &ulSize, &ulFlags ) != NO_ERROR )
             || ( !(ulFlags & ( PAG_EXECUTE | PAG_COMMIT ) ) ) )
        {
            rc = FALSE;
        }
    }
    return rc;
}

/*************************** START OF SPECIFICATION ***************************
 *
 *  Function Name: xcptCheckPrevRegistrationRecord
 *
 *  Function : To validate the direction of the exception registration record
 *             chain. It is done by testing two registration records on the
 *             chain, where the previous record must be on a higher
 *             address than the current record if both of them are on the
 *             same stack. Since stack swapping is enabled in the system,
 *             it is possible that the two registration records are on
 *             different stacks. In that case, we assume that the two
 *             registration record are in the right direction.
 *
 *  Input: pCurrERegRec
 *         pPrevERegRec
 *
 *  Output: TRUE - two registration records are chained in the right direction.
 *          FALSE - two registration records are chained in the wrong direction
 *                  and they must be on the same stack.
 *
 *  History
 *  -------
 *  07-31-94   MartyN   Created. Defect @90929
 *
 *************************** END OF SPECIFICATION ****************************/

BOOL xcptCheckPrevRegistrationRecord( PEXCEPTIONREGISTRATIONRECORD pCurrERegRec,
                                      PEXCEPTIONREGISTRATIONRECORD pPrevERegRec )
{

    ULONG ulHighStack, ulLowStack;      // Stack limits
    THUNKSTACKHEADER *pTSH;
    PEXCEPTIONREGISTRATIONRECORD   pERegRec;
    ULONG ulFlag[2]={0,0};
    LONG i;


    if (pPrevERegRec==END_OF_CHAIN)
        return TRUE;

    xcptGetStackLimits( &ulHighStack, &ulLowStack );
    ulHighStack -= sizeof(EXCEPTIONREGISTRATIONRECORD);

// Ensure that this reregistration record address is between the two stack
// pointers in the TIB.  If not, it must be between the two stack pointers
// for the alternate stack used by the thunks.

    for (i=0,pERegRec=pCurrERegRec; i<=1; i++)
    {
         if ((ULONG)pERegRec >= ulLowStack && (ULONG)pERegRec <= ulHighStack )
         {
             ulFlag[i]=1;               // Record is on Current Stack
         }
         else
         {
             if ( pThunkTLM && *pThunkTLM )
             {
                pTSH = (THUNKSTACKHEADER *)( (ULONG)(*pThunkTLM) + 0x0000EFE0 );

                if ( (ULONG)pERegRec >= pTSH->UserStack &&
                     (ULONG)pERegRec <= pTSH->UserLimit )
                {
                     ulFlag[i]=2;  // Record is on User Stack
                }
                else
                {
                     if ( (ULONG)pERegRec >= (ULONG)(*pThunkTLM) &&
                          (ULONG)pERegRec <= (ULONG)pTSH  )
                     {
                           ulFlag[i]=4;  // Record is on System Stack
                     }
                }
             }
         }
         pERegRec=pPrevERegRec;
    }

    if (!(ulFlag[0] ^ ulFlag[1]))       // Two registration records are on the
    {                                   // same stack
       if (pCurrERegRec > pPrevERegRec )// Wrong Direction !!!
           return FALSE;
    }                                   // Fall through if two records are in
                                        // the right direction.
    return TRUE;                        // Also return TRUE, if two records are
                                        // on differeent stack and we cannot
}                                       // perform comparison.
