TechEd session DEV320: .NET Framework: Best Practices for Avoiding Common Exception Coding Mistakes (Jonathan Keljo, MS)
published: Fri, 10-Jun-2005 | updated: Fri, 10-Jun-2005
A good session this: I learned quite a few things I didn't know before. OK, so there was a lot I already knew, but all in all it was an informative session.
I hadn't heard of Jonathan Keljo before, but he was very good at his subject and communicated his subject well.
I've written in the past about exceptions (recently here, here, and here), so I won't go into those details again. Instead here at the snippets I didn't know before.
Unhandled exceptions in secondary threads
In .NET Framework 2.0, the behavior of an exception in a secondary thread has changed, In version 1.1, if you let an exception escape from a secondary thread (i.e., not the main thread) it would silently disappear. Poof, gone. The main thread wouldn't know anything about it.
This behavior has changed (and for the better, IMHO) for the new Framework. Now a thread that throws an uncaught exception will not only terminate the thread, it will also terminate the main application as well. You'll get a crash, if you like. So instead of silently suppressing evidence of a problem in your application, you'll be made fully aware instead <g>. This follows the principle of "fail quickly" which I must write about one day.
So beware: make sure that your threads aren't letting exceptions escape. Clean up your code now, before you move to .NET 2.0.
Exception Handling and plugging a security loophole
I then learned how exception handling worked in the .NET runtime. Now, I have the vague suspicion that I knew this before, but to be honest I'm not really sure. Let's say I didn't.
There is a two-pass system for handling exceptions. Suppose an exception is thrown. The runtime takes over. For the first pass it looks through the stack, going through the various callers until it finds a catch handler that can handle this exception type (or until it determines that there is no such handler) Note that during this pass it does not unwind the stack: it is merely searching for a handler for this particular exception type. It makes a note of the stack frame where this handler is found (if no handler is found, the runtime will act as the ultimate exception catcher).
Now comes the second pass: the unwinding of the stack. The runtime returns from each caller, running any finally blocks it finds until the marked catch block is reached. At this point the catch handler is finally run.
There is an intriguing security hole in this scheme. In Visual Basic you can write a catch handler with a condition (this feature is not available in C#, although it is present in IL -- obviously, since VB makes use of it). This type of catch handler is known as a filter handler or exception filter.
Private Function SomeCondition(ex as Exception) as Boolean .. work out whether we can deal with ex .. .. and return True/False .. End Function Sub DoWork() Try .. do the work, which might throw .. Catch ex As Exception When SomeCondition(ex) .. do some handling .. End Try End Sub
Anyway, the runtime must evaluate the conditional expression for the filter handler to determine whether the catch block can catch the exception. The code shown above in SomeCondition will execute during the first pass of the exception handling code.
Unfortunately no finally blocks have yet run (that's pass 2, remember). So, assuming there is a try..finally protecting a privilege elevation (plus some work dependent on that elevation) and the finally block removes the privilege elevation in the finally block, then if an exception is thrown the catch handler's condition will be executed with the elevated privilege.
WindowsImpersonationContext context = identity.Impersonate(); try { DoWork(); // this throws an exception } finally { context.Undo(); // a filter handler might be run before this executes }
In .NET 2.0, there is a way to write a try..finally block such that
the finally block is executed before the first pass, avoiding this
loophole. It uses a special Framework method and an anonymous delegate
(check out the RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup()
method in the System.Runtime.CompilerServices
namespace).
When catch (Exception ex)
is (just about) acceptable
Heh. After I rant about never doing this, the man at the CLR says it's OK in certain cases.
1. When you want to log the exception. Of course then you should rethrow the exception. This should only be done at the outermost layer of your call stack; you shouldn't do it for every method in your call stack.
2. Calling user plugins. Here the rule is that you shouldn't let the exception escape: it's likely that the software you're p[lugging into (e.g., Office or IE, etc) can't handle managed exceptions anyway. Perhaps one thing you should do is to unplug yourself in the case of unexpected exceptions.
3. Reverting sensitive state. This is one way at present of saving yourself from the security loophole above.
4. Wrapping exceptions. This is for those cases when the exception is
unexpected, yet passing that on to the user would be confusing. Better
would be to catch the exception, create a new exception (a more
general "the system is in an unstable state" type exception) and pass
the original as the InnerException
of the new exception.
Finally throw the new exception instance.