5

Is there any way to convert a NULL pointer access into a C++ exception under Linux ? Something similar to the NullPointerException in Java. I hope the following program would return successfully, instead of crash (assume the compiler cannot figure out this NULL pointer access during compile time):

class NullPointerException {};

void accessNullPointer(char* ptr) {
    *ptr = 0;
}

int main() {
    try {
        accessNullPointer(0);
    } catch (NullPointerException&) {
        return 1;
    }
    return 0;
}

I'm not expecting any standard way of doing it, since NULL pointer access under C++ is undefined-behavior, just want to know how to get it done under x86_64 Linux/GCC.

I did some very primitive research in this, it might be possible:

  1. When a NULL pointer is access under Linux, a SIGSEGV will be generated.
  2. Inside the SIGSEGV handler, the program's memory and register information will be available (if sigaction() is used to register the signal handler). The instruction which caused the SIGSEGV is also available if the program is disassembled.
  3. Modify the program's memory and/or register, and create/fake an exception instance (maybe by invoking the low level unwind library functions, like _Unwind_RaiseException, etc.)
  4. Finally return from the signal handler, hope the program would start a C++ stack unwinding process like a normal exception was thrown.

Here's a quote from GCC's man page (-fnon-call-exceptions):

Generate code that allows trapping instructions to throw exceptions. Note that this requires platform-specific runtime support that does not exist everywhere. Moreover, it only allows trapping instructions to throw exceptions, i.e. memory references or floating point instructions. It does not allow exceptions to be thrown from arbitrary signal handlers such as "SIGALRM".

It seems this "platform-specific runtime" is exactly what I want. Anyone knows such a runtime for Linux/x86_64 ? Or give me some information on how to implement such a runtime if no such runtime already exists ?

I want the solution to work in multi-threaded program as well.

user416983
  • 974
  • 3
  • 18
  • 28
  • 2
    [This](http://stackoverflow.com/questions/9225415/linux3-gcc46-fnon-call-exceptions-which-signals-are-trapping-instructions) might be helpful. (I'm not sure what you think "runtime" means, but I'm fairly sure that it doesn't mean what you think it means.) – molbdnilo Jun 07 '16 at 07:27
  • 2
    Unless you are forced to use raw pointers, it would be an option to write your own smart pointer class (similar to e.g. [`std::shared_ptr`](http://en.cppreference.com/w/cpp/memory/shared_ptr) or [`std::unique_ptr`](http://en.cppreference.com/w/cpp/memory/unique_ptr)) which throws an exception when accessing an uninitialized pointer. – mindriot Jun 07 '16 at 07:30
  • I think what you're trying to do is not a good idea, since it appears that it will be non-portable (both to other compilers and other hardware). – Walter Jun 07 '16 at 07:49
  • @molbdnilo That link demonstrate exactly what I want ! I'm surprised GCC already implemented the feature. Not sure how it works in a multi-threaded program. – user416983 Jun 07 '16 at 08:01
  • @mindriot It's impossible to replace every raw pointer with smart pointer in a large project, even if the project is written from scratch, so, yes, I'm forced to use raw pointers. – user416983 Jun 07 '16 at 08:03
  • Regarding the multi-threading part of the question, [this article](http://stackoverflow.com/questions/11679568/signal-handling-with-multiple-threads-in-linux) may be helpful. – mindriot Jun 07 '16 at 08:39
  • @mindriot Thanks for the info. I have googled that link before, Linux signal handling seems to be messy in this case. – user416983 Jun 07 '16 at 08:49
  • [This answer](http://stackoverflow.com/a/11679770/3233921) on that article indicates that SIGSEGV is thread-directed and should therefore be received by the thread that triggered it. So I would expect (no 100% guarantees though :)) that the approach linked by @molbdnilo would work in multi-threaded situations as well. – mindriot Jun 07 '16 at 09:17
  • Can I assume that the SEGV is caused by a coding error? If so isn't the real problem how to track down the bugs? My experience with attempting to continue after a detected program bug is that quality of the software suffers in the long run. – Barry Scott Jun 07 '16 at 09:50
  • @BarryScott SEGV is probably caused by coding error. Sometimes due to "non-technical reasons" it's necessary to keep a system going instead of shutting down even if we're sure it's in some trouble. Traditionally, techniques like master-slave server, `fork()` based server, watchdog, etc. are used to satisfy this requirement. I'm trying a new technique (with its own pros & cons) to satisfy this requirement, thus asked this question. – user416983 Jun 08 '16 at 04:32
  • I am pretty sure this "solution" will produce more problems that it would solve, and I know that from practice. If your program in bad state keep system going will not give you anything than more problems. – Slava Aug 03 '16 at 21:57
  • Possible duplicate of [Throwing an exception from within a signal handler](https://stackoverflow.com/questions/1717991/throwing-an-exception-from-within-a-signal-handler) – EvilTeach Aug 17 '17 at 18:02
  • Working example here : https://stackoverflow.com/a/76993447/1305332 – Peter Quiring Aug 28 '23 at 14:09

2 Answers2

2

No, there's no good way to do that, and there shouldn't be. Exceptions are thrown by a throw statement in source code. That's important for reasoning about exception safety: you can look at the code and see the places where exceptions can be thrown and, perhaps more important, you can look a the code and see the places where exceptions will not be thrown. If pretty much anything you do can throw an exception it becomes very difficult to write exception-safe code without cluttering it with catch clauses. Microsoft tried this in their early C++ compilers: they piggybacked C++ exception handling on top of their OS's structured exceptions, and the result was a disaster.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • If GCC option `-fnon-call-exceptions` is used, it will assume a simple memory access may throw C++ exception. I don't care about other platforms, BTW. – user416983 Jun 08 '16 at 04:24
  • "the result was a disaster" - depends who you ask I guess; recent versions of mingw-w64 for example have the option to use SEH to implement C++ exceptions and apparently it works well – M.M Aug 03 '16 at 22:34
  • Microsoft C++ uses structured exception handling to achieve the same effect as -fnon-call-exceptions (probably using the same underlying hardware features). In both cases, you pay a slight performance penalty for a significant benefit in supportability. I.e. when errors happen at the customer site, you at least can log a stack trace where the SEGV occurred. Time to problem resolution is quick. Without it, it can take ages if core dumps are not enabled (e.g. embedded or appliance type products). – Charlie Reitzel Feb 10 '22 at 01:07
0

Register an alternative signal stack with signalaltstack().

The 3rd argument to a signal handler handler registered with the SA_SIGINFO is a pointer to a ucontext_t which contains the saved register. The signal handler should adjust this to simulate a call to a function. That function can then throw the exception.

Potential complications include the need to preserve the value of callee saved registers, the red-zone on x86-64 (which can be disabled) and the return address register on some ISAs, such as ARM.

Timothy Baldwin
  • 3,551
  • 1
  • 14
  • 23