Uncaught error

Consider for VS2022:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main() {
	int i {};
	auto pi { &i };

	try {
		delete pi;
	} catch (...) {
		std::cout << "Caught!\n";
	}
}


This is test code. Yes, I know you can't do the delete. But why isn't the exception caught by the try/catch block? and how can you catch this?
The only C++ exception that a delete expression can throw is from the destructor of the thing being deleted. You have UB.

I believe the Windows software/hardware-level exceptions are called Structured Exceptions, though I might be getting the terminology wrong.
See: https://learn.microsoft.com/en-us/windows/win32/debug/structured-exception-handling?redirectedfrom=MSDN

Try (pun intended) using __try and __except
https://learn.microsoft.com/en-us/windows/win32/debug/using-an-exception-handler

I have no idea what guarantees MSVC makes about the use of these exception handlers.
Last edited on
Unfortunately __try/__except doesn't. They will trap say divide by 0 but not this case. I found this:
https://cplusplus.com/forum/general/34338/

and that sort of fits my circumstance. The code I'm working on isn't mine and I'm getting severe pains in my left side diodes trying to understand it - never mind sorting out the code mess! It's heavily multi-threaded (using Win32 with threads starting other threads... all with sync controlled with a variety of mutexs and events scattered throughout) with new/delete all over the place with raw pointers to base classes (yeah, the classes are polymorphic) strewn around functions like confetti! Everyone previously has been far too frightened to touch it as it's very fragile code and the author has long left. So this mess has finally landed on my desk. AAhhh.... It really could do with a complete rewrite - once I get some deeper understanding as to what it's supposed to do and how. There's a couple of hundred source files with an average of about 4000 lines per source file. It's obvious that it hasn't been tested properly when it was written as this try/delete/catch is all over the place. I knew I was being 'set-up' for something when a senior manager came to see me and brought a bottle of whiskey...
To my understanding, try/catch only catches C++ exceptions. Those may be thrown from your own code, from third-party C++ libraries, or, in some cases, from the C++ standard library. Any C++ code that explicitly invokes a throw statement. But there are many "error conditions" – such as access violation or division by zero – that do not trigger a C++ exception, and therefore will not be caught by try/catch.

Microsoft Windows uses structured exceptions to report certain error conditions, including access violation, division by zero, illegal instruction and so on. Those are totally separate from C++ exceptions and they work in plain C and even other languages. They are more like signals in Linux/Unix. You can catch structured exceptions by using the MSVC-specific __try/__except mechanism (doesn't work in GCC). Or by using SetUnhandledExceptionFilter(), which will set a callback that is called for all "unhandeled" structured exceptions.

https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record#members

Anyway, if you do anything in your program that is undefined behavior, then you may happen to trigger, for example, an access violation – which in turn may trigger a structured exception (at least on Windows). But there is absolutely no guarantee that undefined behavior will trigger an "error", e.g. an exception or abnormal program termination, at all! It could, just as well, happen to "work". Or, even worse, your program may continue running without any observable errors but produce a "bad" (unexpected) result at the end... 🙄


Using delete on a pointer to an object not allocated with new gives unpredictable results.

So, delete'ing a non-null memory address that wasn't allocated with new before, or that has already been freed, is undefined behavior. This means that the delete would be "allowed" to crash your application at this point, e.g. via access violation – but it certainly doesn't have to! It is possible, just as well, that the "bad" delete happens to do nothing. Or that it doesn't crash right away, but messes up the heap space in such a way that future new invocations, possibly at a totally unrelated place, will crash unexpectedly; kind of a worst case scenario.

A tool like Dr. Memory (Windows) or Valgrind (Linux) can be very helpful here:
https://drmemory.org/page_types.html
Last edited on
Yeah.

that the delete would be "allowed" to crash your application at this point, e.g. via access violation


but SEH doesn't catch the access violation when it happens! Ahh...

As I said, this isn't my code - I've just been lumbered with it to sort out. One bottle isn't going to cut it though...
but SEH doesn't catch the access violation when it happens!

Maybe the access violation doesn't happen where (e.g. in the thread) that you think/expect it does? It's possible that the "bad" delete (or whatever "undefined behavior" exists in the code) messes up some data structures in the memory but does not cause a "crash" (e.g. access violation) immediately. The "crash" could then happen latter, at a seemingly unrelated code, in a perfectly valid call.

Have you tried setting up an UnhandledExceptionFilter routine, via SetUnhandledExceptionFilter()? Unlike __try/__except(), this should catch all structured exceptions, from all threads, that haven't been caught before. It's like a global "last resort" error handler.

Again, Dr. Memory can be a great help to find the source of memory-related errors. Specifically "Invalid Heap Argument":
https://drmemory.org/page_invarg.html
Last edited on
Maybe the access violation doesn't happen where (e.g. in the thread) that you think/expect it does


See the test code above which produces the problem. No, I haven't tried SetUnhandledExceptionFilter(). Thanks. I'll look into it. This is only for a quick stop-gap sticking plaster solution as the design is wrong and once I understand what's going on it's going to get a complete re-write. The issue only now arises infrequently when a set of conditions occurs and the program has been used for some years with the program just being restarted when this arises.
This is only for a quick stop-gap sticking plaster solution
What exactly are you trying to accomplish? First, I'm not sure it is possible for the program to catch its own access violations; I think only a separate debugger process can do that. Second, once an access violation has happened there's pretty much nothing you can do safely, as there's no telling what the state of the program is anymore. Your handling code might have been overwritten, for example.
What exactly are you trying to accomplish?


The way the code was written by a *%*%* in some edge cases it tries to delete stack memory - which is obviously a no-no - and terminates. Senior management wanted a 'quick fix' whilst it was being rewritten. I was trying to apply a sticking-plaster to prevent the run-time error terminating the program. Following further code investigations, comments (thanks) and research I've given up on this and am now proceeding with a total re-write. When I informed senior management they just said 'oh well, it was just a thought'. What they know about programming wouldn't fill the head of a pin...

At least I've got some understanding of the program through my investigations so the time hasn't been a complete waste...
Tangentially related, I've looked at old C++ code from the early 2000s, and it was absolutely riddled with if (blah) delete blah; calls.

It's like, sure, it doesn't hurt anything, but it worries me that you think if (blah) is actually a good defensive measure against deleting a junk pointer.
It's like, sure, it doesn't hurt anything, but it worries me that you think if (blah) is actually a good defensive measure against deleting a junk pointer.


I don't. It was only going to be a 'temp quick fix kludge' as wanted by senior management whilst the program was re-coded. I've now abandoned that idea and am now straight into the re-code with a new design and modern C++ practices - dynamic memory management, RAII etc etc.
My 'you' was referring to the person who wrote the code 20 years ago in my example.
It's like, sure, it doesn't hurt anything, but it worries me that you think if (blah) is actually a good defensive measure against deleting a junk pointer.
It's more likely that they just didn't know that delete does nothing for null pointers, or for whatever reason they didn't trust it.
That's probably true.
helios wrote:
It's more likely that they just didn't know that delete does nothing for null pointers, or for whatever reason they didn't trust it.


Or a pattern learned and internalised from the C days of using free(). As someone who spent years programming in C before moving on to C++, it took me a long time to unlearn the pattern for using delete.
freeing a null pointer is fine, too :)
https://stackoverflow.com/a/6084233

malloc/calloc can return a null pointer, however, so that's one place where you'd want null checks before dereferencing.
Last edited on
a null pointer is fine, too
0x0000000000000000: I'm done for!
Topic archived. No new replies allowed.