Metadata

  • Source
  • File: Snapshot
  • Zotero: View Item
  • Type: Webpage
  • Title: How a C++ compiler implements exception handling,
  • Author: Kochhar, Vishal;
  • Year: 2002

Annotations

Notes

pwn

related: Structured Exception Handling

  • This guide was written in 2002, which seemed very old to me, but knowing how hard Microsoft tries to maintain backwards-compatibility, I’m just going to pray that not much has changed since for now.
  • This guide is Structured Exception Handling (SEH)- / VC++-specific. Normally, only the MSVC exception handling extension (__try and __except) use SEH, and SEH may not always clean up resources properly (i.e., it may not be aware of language features such as destructors), unless /Eha switch is present during compilation (source). “Structured Exceptions may occur for many operations, e.g. due to undefined behavior, passing invalid pointers to APIs, unmounting the backing store of a memory mapped file, and many more” (source).
  • This guide comes up with names for internal structures (e.g., funcinfo, “tryblock table”, unwindtable) that may not identify with the actual internal MSVC names.

Relevant structure / member variable definitions:

EXCPT.H
EXCEPTION_DISPOSITION (*handler)(
    _EXCEPTION_RECORD *ExcRecord,
    void * EstablisherFrame, 
    _CONTEXT *ContextRecord,
    void * DispatcherContext);
WINNT.H
struct _EXCEPTION_RECORD
{
    DWORD ExceptionCode;
    DWORD ExceptionFlags; 
    _EXCEPTION_RECORD *ExcRecord;
    PVOID   ExceptionAddress; 
    DWORD NumberParameters;
    DWORD ExceptionInformation[15]; 
} EXCEPTION_RECORD; 
Exception registration
struct EXCEPTION_REGISTRATION
{
   EXCEPTION_REGISTRATION *prev;
   DWORD handler;
   int   id;
   DWORD ebp;
};
Registering a handler manually
EXCEPTION_DISPOSITION myHandler(
    _EXCEPTION_RECORD *ExcRecord,
    void * EstablisherFrame, 
    _CONTEXT *ContextRecord,
    void * DispatcherContext)
{
    exit(0);
    // will not reach here
    return ExceptionContinueExecution;
}
 
void register() {
    //initialize EXCEPTION_REGISTRATION structure
    EXCEPTION_REGISTRATION reg, *preg = ®
    reg.handler = (DWORD)myHandler;
 
    // Get the head of the exception handling chain
    DWORD prev;
    _asm
    {
        mov EAX, FS:[0]
        mov prev, EAX
    }
    reg.prev = (EXCEPTION_REGISTRATION*) prev;
 
    // Register myHandler
    _asm
    {
        mov EAX, preg
        mov FS:[0], EAX
    }
 
    doBadStuff(); // will be caught by myHandler
}
  • Each function call stores a EXCEPTION_REGISTRATION struct in its stack frame, which is a linked list node that overlaps with the saved ebp (order on stack: parameters, return address, saved ebp, id, handler, prev). Apart from the ebp, each node contains an id, handler function pointer, and a prev pointer pointing to the same structure in the previous/outer stack frame. During compilation, the MSVC compiler also generates (a) the exception callback function and (b) a block of data that stores information about try-catch blocks within the function, which the author calls funcinfo. A pointer to this funcinfo is saved in the EXCEPTION_REGISTRATION struct, which the handler passes to __CxxFrameHandler.
  • The funcinfo struct also stores a pointer to a “tryblock table,” which is a list of tryblocks. Each tryblock struct contains an id range (e.g., for identifying whether or not the control is currently in this try block, and also for stack unwinding) and also links to a catchblock table that contains things like the exception type the catch block is interested in and the address of the catch block code.
  • When an exception e is thrown under SEH:
    • The exception is thrown using _CxxThrowException(&e, E_EXCPT_INFO_ADDR). The method and call is generated by the compiler, and E_EXCPT_INFO_ADDR points to a struct storing information about the exception thrown (type info, copy ctor, dtor, etc).
    • _CxxThrowException transfers control to the OS via RaiseException and passes both parameters to it.
    • OS calls the handler, which acts as a wrapper for __CxxFrameHandler, jmping to the latter with a pointer to the relevant funcinfo in eax and also passing the exception pointer & the EXCEPTION_REGISTRATION node pointer. Refer to the code at the top of notes for all the parameters that handler accepts.
    • _CxxFrameHandler checks if the tryblock table (referenced in funcinfo) contains a catch block for the current exception and unwinds the stack. If there is a corresponding catch block, the handler transfers control to it; otherwise, control is transferred to the OS, which calls the next handler in the linked list.
      • The id field is dynamically updated upon entering a new try-block and when objects are initialized/destroyed, which informs the stack unwinding process about which objects are still alive and need to be destructed.
    • During stack unwinding:
      • Destructors for all alive objects in the try-block are called based on the id.
      • The funcinfo contains a pointer to the unwind table, which is a linked list (prev) whose nodes contain a cleanup function pointer (e.g., destructor) for each id. The current id is used to index this table, and each cleanup function at the index and onwards is called. To account for the case where an object ctor throws an exception when some member objects are already initialized, the compiler also generates similar structures for each ctor. If exception is thrown in a dtor during stack unwinding, however, the program will simply terminate.