Ahmed
Ahmed

Reputation: 7238

Catching access violation exceptions?

Example

int *ptr;
*ptr = 1000;

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

Upvotes: 102

Views: 147552

Answers (9)

Peter Quiring
Peter Quiring

Reputation: 1706

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,

Upvotes: 1

Volodymyr Frytskyy
Volodymyr Frytskyy

Reputation: 1283

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

Upvotes: 74

Michael
Michael

Reputation: 2414

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();
};

Upvotes: 15

Juxta
Juxta

Reputation:

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");

}

Upvotes: 121

Loki Astari
Loki Astari

Reputation: 264361

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

Upvotes: 4

David Thornley
David Thornley

Reputation: 57036

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.

Upvotes: -1

Damien
Damien

Reputation: 297

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

Upvotes: 9

JaredPar
JaredPar

Reputation: 754575

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.

Upvotes: 11

unwind
unwind

Reputation: 399753

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.

Upvotes: 48

Related Questions