Catching access violations on Windows
Asked Answered
S

2

6

I am trying to catch all unhandled exceptions in my application so I can save a log file when they occurr. This is a 64-bit Windows application compiled using Visual Studio 2013, written in C++. For testing I am using the default C++ Win32 project generated by VS.

I am catching all exceptions by registering a handler using SetUnhandledExceptionFilter. This works fine for /most/ cases, but not all. All throw()-n exceptions are caught, and most hardware exceptions like floating point or access violations as well. The code that doesn't trigger the handler is:

std::vector<int> foo(5, 0);
for (auto& f : foo)
    foo.erase(foo.begin() + 1);

Instead I just get the standard windows crash dialog box, without my exception handler getting called. If I run it in Visual Studio with debugger attached however, it correctly reports an access violation exception. Other types of access violations trigger the handler as well:

float* ptr = nullptr;
float value = *ptr;

Code above triggers the exception handler.

I have tried using try/catch or catching SIGSEGV signal too, but neither get triggered with the first example. abort/terminate signals don't get called either. In short I am in no way notified when that crash happens.

I want to know is there any way I can get some kind of a notification in my application before it crashes due to the access violation caused by the first example? Since VS seems to be able to detect it I'm assuming there's a way.

EDIT: I just want to make it clear I'm running the code in release mode and the error in the first example isn't caused by iterator out of bounds check done in debug mode.

EDIT2: I included the simplest example I can come up with using a win32 console app. See it here: http://pastebin.com/8L1SN5PQ

Make sure to run it in release mode with no debugger attached.

Sangsanger answered 9/10, 2015 at 11:30 Comment(4)
You need to lookup SEH exceptions. These aren't cases where normal c++ exceptions apply.Interbedded
I've tried. Using SEH __try/__except with SEH enabled in Visual Studio produces the same result. The first example doesn't trigger an exception, the second one does.Sangsanger
Since, as Hans says, this is not a good idea, I'm not going to spend any more time helping you do it. Instead: If you are debugging, run the application in a debug build and use the facilities to debug it until there are no bugs. If it is a live application and you are trying to debug an occasional crash consider deploying a debug build or making use of the telemetry services.Maighdiln
Thanks for your help so far. Sadly I cannot distribute a debug build to our users, and the product that is being built is huge so there will most certainly be hard to find issues that only users running release code will find, and I would like to cover all bases and have the error reports at least contain the debug log.Sangsanger
V
3

These kind of runtime errors are handled differently, they don't produce an SEH exception. Roughly classified somewhere between "programming bug" and "malware attack". And about as informative as a uncaught C++ exception if you don't have a debugger attached, the default handler invokes instant death with __fastfail().

You'll have to call _set_invalid_parameter_handler() in your main() function to alter the way they are handled. You could throw a C++ exception in your custom handler or call RaiseException() to trigger your catch-em-all handler or just report them right there. Favor the latter, you want to ensure that the process is always terminated.

Do beware that your snippet is not the best possible example. This is UB when you build your program without iterator debugging enabled, like the Release build with default settings. UB does not guarantee you'll get an SEH exception. And if it does then you'll have to write your exception filter very carefully, it will be called with the heap lock still taken so basic stuff cannot work. Best way is to wakeup a guard process with a named event. Which then takes a minidump and terminates the program.

Venal answered 9/10, 2015 at 12:15 Comment(12)
Thanks. I've tried registering a handler like this: "void _invalid_parameter( const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved ) { MessageBox(NULL, L"InvalidParameter", L"InvalidParameter", MB_OK); }" in WinMain with a call to: "_set_invalid_parameter_handler(_invalid_parameter);" However the behaviour is unchanged. (i.e. the handler doesn't appear to get called)Sangsanger
Works just fine when I try that with the snippet you posted. Be sure to avoid the iterator debugging feature from being helpful, click "Ignore" when it displays an assertion dialog.Venal
It works in debug mode with debugger attached for me as well. But running in release without debugger is where I'm having issues.Sangsanger
In the Release build you won't get help from the iterator debugging feature and this will die the normal way with an access violation SEH. Usually, UB has no guarantees. Which your normal exception filter will see.Venal
Sadly the filter doesn't seem to see the access violation in this case. But Windows is obviously aware of the issue since it crashes the application.Sangsanger
You are doing something wrong, I can't tell what. One thing you are not doing is the one thing I explicitly warned about, you must terminate the app yourself. Good luck with it.Venal
I have included a simple example (26 lines of code) in the original post that demonstrates the problem. I'm pretty sure there's little I could be doing wrong. I'm not sure how could I terminate the app myself when I get no notification of the error.Sangsanger
Running it in release mode (with a debugger) I see that we have heap corruption occurring. Instead of failing immediately the loop runs about five times (because the bounds checks are elided in release mode). I have tried __try/__except, and this does indeed work, however the heap corruption means that when I try to printf or anything else a further exception is thrown as memory cannot be allocated.Maighdiln
Thanks this helps a lot, it does seem to be the heap corruption that causes the application to terminate without any notifications or exceptions. I wonder if there is any way to get notified when it happens.Sangsanger
Thanks for the edit Hans. I was hoping to avoid having a separate process but it doesn't seem I have another option. This is good enough of an answer for me.Sangsanger
"Do beware that your snippet is not the best possible example. This is UB" - I thought that was the point? The asker wants to save a log file before his/her program crashes, to help with debugging the crash. Obviously in a real program the crash would be harder to find.Primatology
Hmya, there are "good" ways to invoke the "function being called with a bad argument" error trapper in the CRT. By simply calling a CRT function with a bad argument, usually because of a bug. And there are "bad" ways, UB will always be UB if you intentionally disable a UB detection feature like iterator debugging. This should be obvious. C++ is supposed to be fast, iterator debugging makes code very slow.Venal
M
0

You are not seeing the exception because it is being handled internally by the C runtime. Specifically it's a bounds check not an access violation.

Running it up in the debugger I find that it is line 242 in vector:

            _DEBUG_ERROR("vector iterators incompatible");

Which ultimately calls _CrtDebugReportW: https://msdn.microsoft.com/en-us/library/8hyw4sy7.aspx

You can control the behaviour of _CrtDebugReportW using _CrtSetReportMode.

Note that this has no effect in release mode as these are debug-mode bounds checks.

Maighdiln answered 9/10, 2015 at 12:15 Comment(2)
Thanks. I have tried setting "_set_error_mode(_OUT_TO_MSGBOX);" in WinMain, but the behaviour remains unchanged. Is there anything I'm doing wrong?Sangsanger
@user2953192 sorry _set_error_mode controls different type of errors. My apologies. In your case you want _CrtSetReportModeMaighdiln

© 2022 - 2024 — McMap. All rights reserved.