2

In Windows, I have some threads. Two of them terminate with exception (null pointer dereference, for example). I have SetUnhandledExceptionFilter(...) which starts dump generating on the first exception. On the second exception, the whole program dies. Is there any way to handle such situations? All critical errors except first shall be ignored.

pseudo code:

  void job()
  {
   ...
   RaiseException(someCode, someFlags, 0, nullptr); // or doing something wrong, like nullptr dereference
  }

  int main() {
    SetUnhandledExceptionFilter(getDump);
    std::thread t1(job), t2(job); 
    ...
  }

UPD: replace misunderstanded string *nullptr = 0xbad;

UPD2: forget about nullptr

UPD3: so far i came to this workaround

#include <stdio.h>
#include <windows.h>  // for EXCEPTION_ACCESS_VIOLATION
#include <excpt.h>

#include <mutex>


LONG __stdcall HandleException(EXCEPTION_POINTERS* exinfo)
{
    static HANDLE mutex = CreateMutex(nullptr, FALSE, __TEXT("HandleException"));
    while(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0);

    HANDLE event = CreateEvent(nullptr, TRUE, FALSE, __TEXT("Doing Dump"));
    puts("Doing Dump");
    WaitForSingleObject(event, 5000); // do dump routine
    puts("Done Dump");

    return EXCEPTION_EXECUTE_HANDLER;
}

int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
    puts("in filter.");
    return HandleException(ep);
}

void Job()
{
    puts("hello");
    int *p = 0x00000000;  // pointer to NULL
    *p = 13;  // causes an access violation exception;
}

void safeJob(void (*job)())
{
    __try {
         job();
    } __except (filter(GetExceptionCode(), GetExceptionInformation())) {
        exit(-1);
    }
}
int main()
{

    SetUnhandledExceptionFilter(HandleException);
    std::thread t1(std::bind(safeJob, Job));
    std::thread t2(std::bind(safeJob, Job));
    t1.join();
    t2.join();
    return 0;
}
user5821508
  • 332
  • 2
  • 10
  • 2
    Sounds like a bug in `getDump`. Please show your actual code. – Remy Lebeau Jun 22 '17 at 15:20
  • `nullptr` dereference does not necessarily throw an exception, it's undefined behavior. – François Andrieux Jun 22 '17 at 15:24
  • I'm not aware of any system where dereferencing `nullptr` throws an exception. It either crashes with a segfault or does nothing (though it can do anything as it's UB). – Kevin Jun 22 '17 at 15:28
  • 1
    Dereferencing a `std::nullptr_t` is a compile error, should be something like `*(int*)nullptr = 0xbad` – Passer By Jun 22 '17 at 16:12
  • 3
    @Kevin: That system is called "Windows". The winapi tag should have hinted that. Actually, this system is called Intel x86. What you call "segfault" on Intel (and compatibles) is the CPU generating an *exception* (GPF if I recall correctly), and Windows actually keeps that terminology. – conio Jun 22 '17 at 18:14
  • @user5821508: You might want to add the [tag:seh] tag. – conio Jun 22 '17 at 18:15
  • This question needs a [mcve], as well as a clearer problem statement. Do you want to set up an unhandled exception filter, or do you want to actually catch those SEH exceptions? – IInspectable Jun 23 '17 at 08:01

3 Answers3

3

Per Remy's comment, an access violation is a Windows "Structured Exception," not a C++ exception, and it can be handled with Microsoft-specific extensions such as a try-except statement.

https://msdn.microsoft.com/en-us/library/s58ftw19.aspx

Example:

__try
{
 // doing something wrong
 *nullptr = 0xbad;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
  // exception quashed!
}
Dan Korn
  • 1,274
  • 9
  • 14
  • 1
    Could you add an explanation to this answer? – Kevin Jun 22 '17 at 16:44
  • 3
    On Windows, dereferencing a NULL pointer raises an SEH `EXCEPTION_ACCESS_VIOLATION` exception, not a C++ exception. `__try/__except` can be used to handle SEH exceptions. See [Structured Exception Handling](https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657.aspx) on MSDN for more details. – Remy Lebeau Jun 22 '17 at 18:31
0

There is a misconception that "doing something bad" will throw exceptions. That is not correct. If you are doing things that are causes for undefined behavior, you should not expect any predictable behavior. If your platform throws an exception, that's great but realize that you can't count on it.

Your best bet is to avoid writing such code.

More on undefined behavior can be found at Undefined, unspecified and implementation-defined behavior.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 3
    In general terms, this is good advice, but it doesn't really apply to the OPs case, because he *already knows he is getting an exception* and is trying to troubleshoot the problem. – Harry Johnston Jun 22 '17 at 23:53
  • Yes, exactly. Null pointer dereference caused by access to zero size container, not by doing that explicitly – user5821508 Jun 23 '17 at 07:20
  • 1
    The programming language (C++) makes no guarantees about what should happen, if a program dereferences a `nullptr`. The platform (Windows), on the other hand, does. It predictably raises an access violation exception. – IInspectable Jun 23 '17 at 07:45
  • @IInspectable, yes, but as I expect you already know, there's a catch: the guarantee only applies if the dereference actually takes place. The compiler is still entitled to generate code in which something else happens - I believe the most common variant is for the part of the code in which the null dereference would have occurred to be treated as a dead branch and removed entirely. (Doesn't apply in this *particular* case, of course.) – Harry Johnston Jun 24 '17 at 23:17
  • @HarryJohnston: Sure, but recall also that there's a difference between a null pointer and a pointer to address zero. They need not be the same. Sure, accessing a pointer to an invalid address is undefined behavior too, but one that's is harder to analyze at compile-time. For example: `auto p = VirtualAlloc(nullptr, BIG_ENOUGH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); auto i = *reinterpret_cast(p);` given `BIG_ENOUGH` is big enough on your system. In this case the value of `p` is *not* a null pointer value, and I'd like to see the compiler that figures out that p is 0. – conio Jun 27 '17 at 10:36
0

*nullptr = 0xbad;

You've caused undefined behavior. You can't even expect that code before this has run correctly. Undefined behavior can have effects in time before it's hit due to compiler optimizations and the rights it has to assume UB won't happen.

There is zero way to protect yourself from UB. You have to implement code standards that discourage or eliminate the possibility for the kinds of errors that cause it. That is the only thing you can do to protect yourself from UB--don't create it.

This is why every team should have a language lawyer who can tell what's UB. The effects of UB can be very off the wall and happen clear across the program at some later or previous date. This person should be familiar with modern, idiomatic C++ and in charge of creating, or at least playing a huge advisory roll, to the teams creation of coding standards.

Second requirement should be implementation lawyer. Someone who can tell you what to expect from UB. This is a much more advanced...more wizardry than science kind of thing. What they know doesn't always work that way and the domain of UB activity is HUGE! These guys don't grow on trees--I don't qualify except at a very minimalistic level.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • 2
    I've only heard about legends of these UB gurus – Passer By Jun 22 '17 at 16:14
  • _Undefined behavior can have effects in time before it's hit_ ... _more wizardry_ ... They must be true wizards if they can affect the present by manipulating the past! Time traveling code, it's amazing! – zett42 Jun 22 '17 at 18:23
  • @zett42 - the compiler is allowed to assume code that causes UB won't happen. It can then re-order operations based on that because UB isn't considered in the "must act as if..." limitations on the optimizer. Thus UB can be activated before the event that "causes it." The real point here is that generating UB renders your complete program and everything it might do at any time as undefined. And yeah, the people who can track that shit down and do more than just barely understand when they find it are wizards. – Edward Strange Jun 22 '17 at 18:29
  • 2
    In general terms, this is good advice, but it doesn't really apply to the OPs case, because he *already knows he is getting an exception* and is trying to troubleshoot the problem. – Harry Johnston Jun 22 '17 at 23:53
  • 1
    More to the point, he knows he is getting a "Structured Exception" - Which is a winapi thing, NOT a c++ exception. – Chris Becke Jun 23 '17 at 05:46
  • 1
    The programming language (C++) makes no guarantees about what should happen, if a program dereferences a nullptr. The platform (Windows), on the other hand, does. It predictably raises an access violation exception. – IInspectable Jun 23 '17 at 07:47
  • @IInspectable, yes, but as I expect you already know, there's a catch: the guarantee only applies if the dereference actually takes place. The compiler is still entitled to generate code in which something else happens - I believe the most common variant is for the part of the code in which the null dereference would have occurred to be treated as a dead branch and removed entirely. (Doesn't apply in this *particular* case, of course.) – Harry Johnston Jun 24 '17 at 23:17
  • @HarryJohnston: Obviously, I wasn't referring to a dereference, that (potentially) doesn't happen, as in `*nullptr;`. Neither was the question, nor this proposed answer. – IInspectable Jun 24 '17 at 23:27
  • @IInspectable, I understand that wasn't what you meant; it was just that your comment, taken at face value, might confuse other readers. In other words, my reply was intended for their benefit, not yours. :-) (I think you're wrong about this answer, though; I believe it *is* talking about the possibility that the compiler might generate code that behaves unexpectedly. This would even be good advice if the OP had in fact been *deliberately* attempting to generate an exception.) – Harry Johnston Jun 24 '17 at 23:35
  • @HarryJohnston: This might be good advice, *in general*. It turns bad in context of this Q&A, though. In context of this Q&A, this proposed answer is simply wrong (or at least very misleading). While the expression `*p = 13` yields undefined behavior as far as the C++ Standard goes, it exhibits predictable and reliable behavior on Windows. You don't need a language lawyer on your team. You need a Windows developer. – IInspectable Jun 26 '17 at 09:46
  • 1
    @IInspectable, but it *doesn't* exhibit predictable and reliable behavior on Windows. For example, a Windows compiler could still behave in the way [described here](http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html). The first example shows a case where you would expect an exception and not get one; the second example shows a case where you would get an exception you weren't expecting. (Visual Studio 2015, in release mode, does the former, but there's no rule that says a Windows compiler can't do the latter either.) – Harry Johnston Jun 26 '17 at 21:15