March 17, 2006

"SetUnhandledExceptionFilter" and VC8

Many programs are setting an own Unhandled-Exception-Filter , for catching unhandled exceptions and do some reporting or logging (for example creating a mini-dump ).

Now, starting with VC8 (VS2005), MS changed the behaviour of the CRT is some security related and special situations.
The CRT forces the call of the default-debugger (normally Dr.Watson) without informing the registered unhandled exception filter. The situations in which this happens are the following:

So the conclusion is: The are many situations in which your user-defined Unhandled-Exception-Filter will never be called. This is a major change to the previous versions of the CRT and IMHO not very well documented.

The solution

If you don’t want this behavior and you will be sure that your handler will be called, you need to intercept the call to SetUnhandledExceptionFilter which is used by the CRT to disable all previously installed filters. You can achieve this for x86 with the following code:

#include <windows.h>
  #include <tchar.h>
  #include <stdio.h>

  #ifndef _M_IX86
  #error "The following code only works for x86!"
  #endif
  LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
    LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
  {
    return NULL;
  }

  BOOL PreventSetUnhandledExceptionFilter()
  {
    HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
    if (hKernel32 == NULL) return FALSE;
    void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
    if(pOrgEntry == NULL) return FALSE;
    unsigned char newJump[ 100 ];
    DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
    dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
    void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
    DWORD dwNewEntryAddr = (DWORD) pNewFunc;
    DWORD dwRelativeAddr = dwNewEntryAddr – dwOrgEntryAddr;

    newJump[ 0 ] = 0xE9;  // JMP absolute
    memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
    SIZE_T bytesWritten;
    BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
      pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
    return bRet;
  }

  LONG WINAPI MyUnhandledExceptionFilter(
    struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
  {
    // TODO: MiniDumpWriteDump
    FatalAppExit(0, _T(“Unhandled Exception occured!”));
    return EXCEPTION_CONTINUE_SEARCH;
  }

  int _tmain()
  {
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
    BOOL bRet = PreventSetUnhandledExceptionFilter();
    _tprintf(_T(“Prevented: %d”), bRet);

    abort();  // force Dr.Watson in release!
  }

Before calling PreventSetUnhandledExceptionFilter you must be sure that no other thread is running (or at least no other thread is trying to call SetUnhandledExceptionFilter).


Posted 1 year, 9 months ago on March 17, 2006
The trackback url for this post is http://blog.kalmbachnet.de/bblog/trackback.php/75/

Re:
I still can call _set_abort_behavior(_WRITE_ABORT_MSG) if I want a hammerless solution, right?
Posted 1 year, 9 months ago by Rodrigo Strauss • • wwwReply
Comment Trackback URL : http://blog.kalmbachnet.de/bblog/trackback.php/75/402/
Re:
No! This only will prevent ONE situation (calling abort). In the other two situations Dr.Watson is still called...
Posted 1 year, 9 months ago by Jochen Kalmbach • • • Reply
Comment Trackback URL : http://blog.kalmbachnet.de/bblog/trackback.php/75/403/
Re:
Ok, got it. This is the only way to go if you want to receive notifications even if there was a security check.
Posted 1 year, 9 months ago by Rodrigo Strauss • • wwwReply
Comment Trackback URL : http://blog.kalmbachnet.de/bblog/trackback.php/75/404/
Re:
Hello! Here is a proposal to make Dr.Watson invocation configurable http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=4c9e4bf1-43e7-4ad5-9610-c09ff294bf18 Please vote. Thanks
Posted 1 year, 9 months ago by Paul Shmakov • • • Reply
Comment Trackback URL : http://blog.kalmbachnet.de/bblog/trackback.php/75/406/
Re:
Hi! Thanks for your link to the proposal! I didn't know that we were working on the same issue at almost the same time ;-)
Posted 1 year, 9 months ago by Jochen Kalmbach • • • Reply
Comment Trackback URL : http://blog.kalmbachnet.de/bblog/trackback.php/75/407/
Microsoft's aswer
Microsoft reported back. Short answer is: No, there will be no ability to replace Dr.Watson invocation or register a callback function that would be fired just before Dr.Watson. :( For more information refer to: http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=4c9e4bf1-43e7-4ad5-9610-c09ff294bf18
Posted 1 year, 9 months ago by Paul Shmakov • • • Reply
Comment Trackback URL : http://blog.kalmbachnet.de/bblog/trackback.php/75/408/

Comments have now been turned off for this post