Click here to Skip to main content
Click here to Skip to main content

SetUnhandledExceptionFilter and the C/C++ Runtime Library

By , 7 Feb 2011
 

Introduction

Windows provides a way for applications to override the default application "crash" handling functionality by the means of the SetUnhandledExceptionFilter function.

Usually, the SetUndhandledExceptionFilter function is used in conjunction with the crash reporting activity. Having the ability of pinpointing the line of code which caused a crash is invaluable in post mortem debugging.

Post mortem debugging has been discussed in other articles on CodeProject and is not the scope of this article.

Here is how a simple unhandled exception filter (which displays only "Gotcha!" in the console) looks like:

bool g_showCrashDialog = false;

LONG WINAPI OurCrashHandler(EXCEPTION_POINTERS * /*ExceptionInfo*/)
{
    std::cout << "Gotcha!" << std::endl;

    return g_showCrashDialog ? EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER;
}

If the crash handling function returns EXCEPTION_EXECUTE_HANDLER, the Operating System will display the default crash dialog or call the Just in time (JIT) debugger if such a debugger is installed on the system.

In order to test the code, we will simulate a null pointer invalid access like this:

int main()
{
    ::SetUnhandledExceptionFilter(OurCrashHandler);

    std::cout << "Normal null pointer crash" << std::endl;

    char *p = 0;
    *p = 5;
}

The program should then display:

Normal null pointer crash
Gotcha!

The C/C++ Runtime Library

The C/C++ Runtime Library will remove any custom crash handler in certain circumstances, and our crash handler will never be called.

Circumstances such as:

abort() function

void AbortCrash()
{
    std::cout << "Calling Abort" << std::endl;
    abort();
}

will display:

Calling Abort

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

abort is called internally in the CRT, we need to catch all those cases too.

Out of bounds vector access

void OutOfBoundsVectorCrash()
{
    std::cout << "std::vector out of bounds crash!" << std::endl;

    std::vector<int> v;
    v[0] = 5;
}

Will display:

std::vector out of bounds crash!

Pure virtual function call

void VirtualFunctionCallCrash()
{
    struct B
    {
        B()
        {
            std::cout << "Pure Virtual Function Call crash!" << std::endl;
            Bar();
        }

        virtual void Foo() = 0;

        void Bar()
        {
            Foo();
        }
    };

    struct D: public B
    {
        void Foo()
        {
        }
    };

    B* b = new D;
    // Just to silence the warning C4101:
    //    'VirtualFunctionCallCrash::B::Foo' : unreferenced local variable
    b->Foo(); 
}

Will display:

Pure Virtual Function Call crash!
R6025
- pure virtual function call

The fix

In order to have the above cases also caught, we need to redirect the SetUnhandledExceptionFilter function to a dummy function so that when the CRT calls SetUnhandledExceptionFilter(0) in order to remove any custom crash handlers, it will call our dummy function.

The redirection was done using the CAPIHook class presented in Chapter 22: DLL Injection and API Hooking of the book Windows via C/C++, Fifth Edition written by Jeffrey Richter and Christophe Nasarre, Microsoft Press (c) 2008.

The code looks like:

LONG WINAPI RedirectedSetUnhandledExceptionFilter(EXCEPTION_POINTERS * /*ExceptionInfo*/)
{
    // When the CRT calls SetUnhandledExceptionFilter with NULL parameter
    // our handler will not get removed.
    return 0;
}

int main()
{
    ::SetUnhandledExceptionFilter(OurCrashHandler);

    CAPIHook apiHook("kernel32.dll", 
      "SetUnhandledExceptionFilter", 
      (PROC)RedirectedSetUnhandledExceptionFilter);

    // Code that crashes
}

64 bit programs

The redirection procedure works for 32 bit code as well as for 64 bit code. The sample code provides 32 bit and 64 bit compiler targets.

Static vs. dynamic CRT linking

The code works only with dynamic CRT linking (default behavior).

History

  • 07.02.2011: Initial release.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License

About the Author

Cristian Adam
Software Developer
Germany Germany
Member
Software engineer by day, Xiph.Org contributor by night.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberJunfengGuo18 Sep '12 - 17:19 
nice work, many thanks
GeneralMy vote of 5memberAjay Vijayvargiya13 Jun '12 - 6:35 
Love this! Very useful for me!
QuestionStatic linking?membercolinbr4 Feb '12 - 2:20 
[quote]The code works only with dynamic CRT linking[/quote]
 
Is there any way of getting this (or something similar) to work with static CRT linking ?
GeneralMy vote of 5memberj5sung27 Jun '11 - 23:10 
Great! This article help me a lot!
QuestionOther errors?membermartin_bisson22 Feb '11 - 15:39 
Do you know a way to catch memory errors such as :
 

char* p = new char[1000];
delete [] p;
delete [] p;

 
It seems that it goes right through the exception filter...
AnswerRe: Other errors?memberCristian Adam23 Feb '11 - 2:27 
Good catch, I've played a bit but I don't have a solution for the problem.
Interesting enough the Debug version does throw an exception.
GeneralRe: Other errors?membermartin_bisson23 Feb '11 - 3:29 
The only way I've found to catch that error is to have another process attach itself to the process that will crash using DebugActiveProcess(). Other than that, I can't catch it... If you do find a way, please let me know.
GeneralRe: Other errors?memberCristian Adam24 Feb '11 - 0:00 
Starting with Visual C++ 2005 free[^] "if an error occurs in freeing the memory, errno is set with information from the operating system on the nature of the failure."
 
What actually happens is if HeapFree[^] fails, errno is set with the result from GetLastError[^].
 
So I've hooked into HeapFree and when it fails I generate a crash. The modified code can be found here[^]
 
HeapFree doesn't fail on every double call. For example the following code works:
char * buf = new char [10000];
delete [] buf;
delete [] buf;

 
But the following code doesn't work:
char * buf = new char [1000];
delete [] buf;
delete [] buf;

 
In the end I think that your method is more accurate.
GeneralRe: Other errors?membermartin_bisson24 Feb '11 - 6:36 
I tried your new code with Microsoft Visual Studio 2010, and it still crashes in the same way... Something probably changed between 2008 and 2010...
 
I'll still look for a way to catch these kind of error within the same process (i.e. not using another process attached as a debugger), but I haven't found one yet... If you do, please let me know!
GeneralRe: Other errors?mvpAjay Vijayvargiya14 Mar '11 - 19:54 
In my case, on x64 Release only, the following fails:
char* pBuffer= malloc(10000;
free(pBuffer);
free(pBuffer);
 
And I've tried everything, starting from try, __try, SetUnhandledExceptionFilter, the hooking, but it still leads to crash! I didn't find any solution. While debugging, I have found it calls HeapValidate (via _free_dbg), and on VS Debugger, it would display "F12" dialog box.
 
On x64 Release, I cannot debug inside the free function, but it still leads to "F12" dialog box.
 
On striaghtaway running the program, it crashes with 0xC0000374 exception code. And this bloody exception cannot be caught! Unsure | :~
 
I have even tried raising the same exception using RaiseException, and it can be handled. The __except code does the work. I dont understand what/how free/HeapValidate is raising the same exception.
 
Hope this input aids in finding possible exception-handling within same process.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 7 Feb 2011
Article Copyright 2011 by Cristian Adam
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid