Catching access violation exceptions?
Asked Answered
F

9

100

Example

int *ptr;
*ptr = 1000;

can I catch memory access violation exception using standard C++ without using any microsoft specific.

Forevermore answered 19/1, 2009 at 13:27 Comment(0)
R
47

Nope. C++ does not throw an exception when you do something bad, that would incur a performance hit. Things like access violations or division by zero errors are more like "machine" exceptions, rather than language-level things that you can catch.

Rewarding answered 19/1, 2009 at 13:30 Comment(6)
I know it is HW exceptions, but there are microsoft specific keywords handle this(__try __except)?Forevermore
@Ahmed: yes, but if you use them, 'impossible' things may happen. For instance, some of the statements after the AV line of code may have already executed, or statements before the AV have not executed.Eugene
See my answer below how to enable such exceptions handling using regular try...catch block in VC++.Dossal
@Eugene can you elaborate on the "impossible things happening" part? is it because of compiler and/or CPU reordering instructions?Bazluke
The underlying operating system will often provide mechanisms to catch such issues, and they will incur no cost, as the exception is generated by the CPU architecture. This is evidenced by the way that debuggers are able to trap exception to allow you to debug, without slowing down code execution.Careful
@DinoDini SEH definitely slows down execution. But as you indicated its negligible, when the question is "Why are we sigseg-ing". But when shipping the Gold Master, you always turn it off. As its a noticeable difference.Weywadt
S
118

Read it and weep!

I figured it out. If you don't throw from the handler, the handler will just continue and so will the exception.

The magic happens when you throw you own exception and handle that.

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <tchar.h>

void SignalHandler(int signal)
{
    printf("Signal %d",signal);
    throw "!Access Violation!";
}

int main()
{
    typedef void (*SignalHandlerPointer)(int);

    SignalHandlerPointer previousHandler;
    previousHandler = signal(SIGSEGV , SignalHandler);
    try{
        *(int *) 0 = 0;// Baaaaaaad thing that should never be caught. You should write good code in the first place.
    }
    catch(char *e)
    {
        printf("Exception Caught: %s\n",e);
    }
    printf("Now we continue, unhindered, like the abomination never happened. (I am an EVIL genius)\n");
    printf("But please kids, DONT TRY THIS AT HOME ;)\n");

}
Skill answered 28/5, 2009 at 2:7 Comment(8)
Nice tip, especially because __try/__except won't catch AV either.Lethargy
@FrederikSlijkerman you're right, I got this 'prejudice' after a badly conducted test in which it seemed not to catch.Lethargy
This does NOT work in gcc but does work in VC++ but only in the "Debug" build. Still upvoting for an interesting solution. The signal handler would be called but the exception won't get thrown.Comedic
Nathan Adams, it works in Release build in my case with VS 2013. I love this method. I used try catch with inline assembly to avoid AVs errors.Matz
That does not work portabley. When a signal handler is invoked the stack frame and register munging is not the same as when a normal function stack frame (it may not even use the same stack on some systems). The best you can do is set a flag to indicate the signal handler has been activated. Then in your code test for that flag and throw.Eliathas
This has a high chance of introducing undefined behavior. In order for this to work on POSIX, there must not be any alternative signal stacks (sigaltstack) installed (unless the C++ exception unwinding implementation allows it), and every runtime function handling the unwinding mechanism itself should be signal-safe.Willable
If you want to return default handler to signal (SIGSEGV in this case), juse use the following: signal(SIGSEGV, SIG_DFL);Citric
Dereferencing and writing to null is not guaranteed to raise a SIGSEGVYardmaster
D
73

There is a very easy way to catch any kind of exception (division by zero, access violation, etc.) in Visual Studio using try -> catch (...) block. A minor project settings tweaking is enough. Just enable /EHa option in the project settings. See Project Properties -> C/C++ -> Code Generation -> Modify the Enable C++ Exceptions to "Yes With SEH Exceptions". That's it!

See details here: https://learn.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-160

Dossal answered 22/11, 2011 at 22:43 Comment(3)
There is no such setting value in Visual Studio .NET 2003, there are only "No" and "Yes (/EHsc)". Can you clarify what minimal version of Visual Studio you need to be able to enable this setting?Factorial
The link appears to specify "Visual Studio 2005"Maidstone
what if with gcc or MinGW?Despot
R
47

Nope. C++ does not throw an exception when you do something bad, that would incur a performance hit. Things like access violations or division by zero errors are more like "machine" exceptions, rather than language-level things that you can catch.

Rewarding answered 19/1, 2009 at 13:30 Comment(6)
I know it is HW exceptions, but there are microsoft specific keywords handle this(__try __except)?Forevermore
@Ahmed: yes, but if you use them, 'impossible' things may happen. For instance, some of the statements after the AV line of code may have already executed, or statements before the AV have not executed.Eugene
See my answer below how to enable such exceptions handling using regular try...catch block in VC++.Dossal
@Eugene can you elaborate on the "impossible things happening" part? is it because of compiler and/or CPU reordering instructions?Bazluke
The underlying operating system will often provide mechanisms to catch such issues, and they will incur no cost, as the exception is generated by the CPU architecture. This is evidenced by the way that debuggers are able to trap exception to allow you to debug, without slowing down code execution.Careful
@DinoDini SEH definitely slows down execution. But as you indicated its negligible, when the question is "Why are we sigseg-ing". But when shipping the Gold Master, you always turn it off. As its a noticeable difference.Weywadt
C
14

At least for me, the signal(SIGSEGV ...) approach mentioned in another answer did not work on Win32 with Visual C++ 2015. What did work for me was to use _set_se_translator() found in eh.h. It works like this:

Step 1) Make sure you enable Yes with SEH Exceptions (/EHa) in Project Properties / C++ / Code Generation / Enable C++ Exceptions, as mentioned in the answer by Volodymyr Frytskyy.

Step 2) Call _set_se_translator(), passing in a function pointer (or lambda) for the new exception translator. It is called a translator because it basically just takes the low-level exception and re-throws it as something easier to catch, such as std::exception:

#include <string>
#include <eh.h>

// Be sure to enable "Yes with SEH Exceptions (/EHa)" in C++ / Code Generation;
_set_se_translator([](unsigned int u, EXCEPTION_POINTERS *pExp) {
    std::string error = "SE Exception: ";
    switch (u) {
    case 0xC0000005:
        error += "Access Violation";
        break;
    default:
        char result[11];
        sprintf_s(result, 11, "0x%08X", u);
        error += result;
    };
    throw std::exception(error.c_str());
});

Step 3) Catch the exception like you normally would:

try{
    MakeAnException();
}
catch(std::exception ex){
    HandleIt();
};
Crumple answered 26/2, 2017 at 4:35 Comment(1)
This site contains simple couple of example on _set_se_translator() methods and works for me, msdn.microsoft.com/en-us/library/5z4bw5h5.aspxFlavone
S
10

This type of situation is implementation dependent and consequently it will require a vendor specific mechanism in order to trap. With Microsoft this will involve SEH, and *nix will involve a signal

In general though catching an Access Violation exception is a very bad idea. There is almost no way to recover from an AV exception and attempting to do so will just lead to harder to find bugs in your program.

Synchrocyclotron answered 19/1, 2009 at 13:33 Comment(8)
So your advice is to know what is the cause of AV exception,is not it?Forevermore
Absolutely. AV's are representative of a bug in your code and catching the exception will just hide the problem.Synchrocyclotron
To clarify, the C++ standard makes a distinction between undefined, unspecified, and implementation defined. Implementation defined means that the implementation must specify what takes place. The code in the question is undefined, which means that anything can happen, and be different each time.Sisneros
Catching Access Violation is not bad idea - it is good for User experience. However, the only meaningful thing I do in this case is - spawn another process with Bug Reporting GUI and try to create a current process dump. Spawning a process is always succeessful operation. Then, I do TerminateProcess() to self-kill.Shaftesbury
It is a bad idea to catch an exception and silently ignore it. It is a very good idea when possible to catch an exception and record information about the state of the application for diagnostic purposes. I once wrote a UI for a backend graphics library that needed some debugging. Every time it crashed, people came to me because they knew I wrote the UI. I put a sig trap around the backend that popped up an alert that told the user that the library crashed. People started going to the author of the library.Warfeld
What - of course it is a good idea to catch AV's. Not doing so and just let the 'default AV' handler handle it helps nobody. 'Default AV' means the OS will just terminating ya program - that's it. Even putting a all-possible catching exception handler at certain points of ya program that'll kind of ignore / just log that there was a problem and then let the flow of execution go on is much better than to being preemptive as and try to avoid all kind of error scenarios in advance. To see that there are always a crack for the unexpected to step in.Pocahontas
Catching AVs is a very good idea to help with debugging or even making a debugger.Careful
If you are just interested in telling the user that "something went wrong", a better way is to launch your actual application from another process, and inspect the exit code when the application either cleanly exits, or dies a horrible death. This covers all bases and all signals. And it is more robust. If memory is already corrupted because of AV, your catch handler could do anything including spawning the dreaded nasal daemon and erase your hard drive.Dupuis
S
9

As stated, there is no non Microsoft / compiler vendor way to do this on the windows platform. However, it is obviously useful to catch these types of exceptions in the normal try { } catch (exception ex) { } way for error reporting and more a graceful exit of your app (as JaredPar says, the app is now probably in trouble). We use _se_translator_function in a simple class wrapper that allows us to catch the following exceptions in a a try handler:

DECLARE_EXCEPTION_CLASS(datatype_misalignment)
DECLARE_EXCEPTION_CLASS(breakpoint)
DECLARE_EXCEPTION_CLASS(single_step)
DECLARE_EXCEPTION_CLASS(array_bounds_exceeded)
DECLARE_EXCEPTION_CLASS(flt_denormal_operand)
DECLARE_EXCEPTION_CLASS(flt_divide_by_zero)
DECLARE_EXCEPTION_CLASS(flt_inexact_result)
DECLARE_EXCEPTION_CLASS(flt_invalid_operation)
DECLARE_EXCEPTION_CLASS(flt_overflow)
DECLARE_EXCEPTION_CLASS(flt_stack_check)
DECLARE_EXCEPTION_CLASS(flt_underflow)
DECLARE_EXCEPTION_CLASS(int_divide_by_zero)
DECLARE_EXCEPTION_CLASS(int_overflow)
DECLARE_EXCEPTION_CLASS(priv_instruction)
DECLARE_EXCEPTION_CLASS(in_page_error)
DECLARE_EXCEPTION_CLASS(illegal_instruction)
DECLARE_EXCEPTION_CLASS(noncontinuable_exception)
DECLARE_EXCEPTION_CLASS(stack_overflow)
DECLARE_EXCEPTION_CLASS(invalid_disposition)
DECLARE_EXCEPTION_CLASS(guard_page)
DECLARE_EXCEPTION_CLASS(invalid_handle)
DECLARE_EXCEPTION_CLASS(microsoft_cpp)

The original class came from this very useful article:

http://www.codeproject.com/KB/cpp/exception.aspx

Sacksen answered 19/1, 2009 at 15:10 Comment(1)
I see that using a Microsoft compiler is treated the same as an illegal instruction or access violation. Interesting.Harridan
E
3

Not the exception handling mechanism, But you can use the signal() mechanism that is provided by the C.

> man signal

     11    SIGSEGV      create core image    segmentation violation

Writing to a NULL pointer is probably going to cause a SIGSEGV signal

Eliathas answered 20/1, 2009 at 8:29 Comment(1)
@maidamai signal() is part of the posix standard. Windows implements the posix standard (as does Linux and unix)Eliathas
C
0

Yes, all things are possible. Catching CPU exceptions (interrupts) (async exceptions) are possible. I have an example that catches NPE/AV, DivByZero, FPE, Stack Overflow. Tested with Windows and Linux. Unfortunately it does require some specific Windows API on Windows. The signal() function on Windows does not catch all hardware exceptions, and Windows does not support sigaction() yet.

/*

Test : Catching all hardware exceptions.

Windows : cl main.cpp

Linux : gcc -std=c++11 main.cpp -lstdc++ -lm

*/

#include <cstdio>
#include <cstdlib>
#include <setjmp.h>

#ifdef _WIN32
#include <windows.h>
#include <errhandlingapi.h>
#include <malloc.h>
#include <float.h>
#else
#include <signal.h>
#include <math.h>
#include <fenv.h>
#endif

#define count (16)
#define stack (4 * 1024)

extern "C" {

jmp_buf buf;  //NOTE : this should be thread local in a multi-threaded app (and stored in a queue if nested try / catch blocks are used)

#ifdef _WIN32
bool was_stack_overflow = false;  //NOTE : this should be thread local in a multi-threaded app
LONG exception_filter(_EXCEPTION_POINTERS *ExceptionInfo) {
  was_stack_overflow = ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW;
  longjmp(buf, 1);
  return 0;
}
#else
void handler(int sig,siginfo_t *info, void *context) {
  longjmp(buf, 1);
}
#endif

void func1(int z) {
  printf("func1:%d\n", z);
}

void func1f(float z) {
  printf("func1f:%f\n", z);
}

void func2(const char* msg, int z) {
  printf("%s:%d\n", msg, z);
}

void func3() {
  printf("done!\n");
}

int recursive(int val) {
  int data[1024];
  return recursive(val + 1);
}

//after a catch some cleanup is required
void after_catch() {
#ifdef _WIN32
  if (was_stack_overflow) {
    _resetstkoflw();
    //see this website for explaination of _resetstkoflw() https://jeffpar.github.io/kbarchive/kb/315/Q315937/
  }
#else
  //on Linux need to re-enable FPEs after a longjmp()
  feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
#endif
}

}

int main(int argc, const char **argv) {
  int z = 0;
  float fz = 0.0f;
#ifdef _WIN32
  printf("Win32 setup\n");
  SetUnhandledExceptionFilter(exception_filter);
  //enable floating point exceptions
  unsigned int control_word;
  _controlfp_s(&control_word, _MCW_DN, 0xffff);
#else
  printf("Linux setup\n");
  stack_t altstack;
  altstack.ss_sp =  malloc(stack);
  altstack.ss_flags = 0;
  altstack.ss_size = stack;

  sigaltstack(&altstack, NULL);
  //NOTE : sigaltstack with SA_ONSTACK is required to catch stack overflows

  struct sigaction act = { 0 };
  act.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
  act.sa_sigaction = &handler;

  sigaction(SIGSEGV, &act,NULL);
  sigaction(SIGFPE, &act,NULL);
//  sigaction(SIGILL, &act,NULL);
  //enable float point exceptions
  feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
#endif
  int jmp;
  int cntr;

  cntr=0;
  for(int a=0;a<count;a++) {
    jmp = setjmp(buf);
    if (jmp == 0) {
      // try : div by zero
      int x = 123;
      int y = 0;
      z = x / y;
      func1(z);
    } else {
      //catch block
      after_catch();
      cntr++;
    }
  }
  func2("/0", cntr);

  cntr=0;
  for(int a=0;a<count;a++) {
    jmp = setjmp(buf);
    if (jmp == 0) {
      //try : NPE
      int *p = NULL;
      *p = 0;
      func1(z);
    } else {
      //catch block
      after_catch();
      cntr++;
    }
  }
  func2("NPE", cntr);

  cntr=0;
  for(int a=0;a<count;a++) {
    jmp = setjmp(buf);
    if (jmp == 0) {
      //try : FPE
      float fx = 123.0f;
      float fy = 0.0f;
      fz = fx / fy;
      func1f(fz);
    } else {
      //catch block
      after_catch();
      cntr++;
    }
  }
  func2("FPE", cntr);

  cntr=0;
  for(int a=0;a<count;a++) {
    jmp = setjmp(buf);
    if (jmp == 0) {
      //try : stack overflow
      recursive(0);
      func1(z);
    } else {
      //catch block
      after_catch();
      cntr++;
    }
  }
  func2("StackOverflow", cntr);

  func3();

  return 0;
}

C++ Exceptions are "software" exceptions and would not catch these hardware exceptions. There is no "portable" way to achieve this yet. Hopefully the next C++ edition would add support to the standard try / catch but don't hold your breath.

Note : this solution using longjmp() which may cause memory leaks since dtors may not get called. Code inside try blocks should be very small. Was unable to reliably throw a C++ software exception in the handlers.

I think they are called 'async exceptions' because floating point exceptions occur on the floating point unit which runs parallel to the main CPU. An 'fwait' causes the CPU to wait for the FPU to finish processing.

Posting this here since other related questions redirect here.

Thanks,

Culberson answered 28/8, 2023 at 13:54 Comment(0)
H
-1

A violation like that means that there's something seriously wrong with the code, and it's unreliable. I can see that a program might want to try to save the user's data in a way that one hopes won't write over previous data, in the hope that the user's data isn't already corrupted, but there is by definition no standard method of dealing with undefined behavior.

Harridan answered 19/1, 2009 at 15:38 Comment(1)
Recovering from access violation may be possible. Recovering from EIP jump voilation is never possible unless you are dodgy and keep assembly level instruction pointers. However, catching Access violation is good for spawning another process for bug reporting GUI feature.Shaftesbury

© 2022 - 2024 — McMap. All rights reserved.