Gracefully Handling Corrupted State Exceptions

Gracefully handling corrupted state exceptions

Instead of using <legacyCorruptedStateExceptionsPolicy> it would be better to use [HandleProcessCorruptedStateExceptions] (and [SecurityCritical]) as stated here:

https://msdn.microsoft.com/en-us/magazine/dd419661.aspx

Following that, your Main method should look something like this:

[HandleProcessCorruptedStateExceptions, SecurityCritical]
static void Main(string[] args)
{
try
{
...
}
catch (Exception ex)
{
// Log the CSE.
}
}

But be aware that this doesn't catch the more serious exceptions like StackOverflowException and ExecutionEngineException.

Also finally of involved try blocks will not be executed:

https://csharp.2000things.com/2013/08/30/920-a-finally-block-is-not-executed-when-a-corrupted-state-exception-occurs/

For other unhandled appdomain exceptions you can use :

  • AppDomain.CurrentDomain.UnhandledException
  • Application.Current.DispatcherUnhandledException
  • TaskScheduler.UnobservedTaskException

(Please do a search for the details when a specific handler is appropriate for your situation. TaskScheduler.UnobservedTaskException for example is a bit tricky.)

If you don't have access to the Main method, you can also mark your AppDomain exception handler to catch the CSE:

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

...

[HandleProcessCorruptedStateExceptions, SecurityCritical]
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// AccessViolationExceptions will get caught here but you cannot stop
// the termination of the process if e.IsTerminating is true.
}

The last line of defense could be an unmanaged UnhandledExceptionFilter like this:

[DllImport("kernel32"), SuppressUnmanagedCodeSecurity]
private static extern int SetUnhandledExceptionFilter(Callback cb);
// This has to be an own non generic delegate because generic delegates cannot be marshalled to unmanaged code.
private delegate uint Callback(IntPtr ptrToExceptionInfo);

And then somewhere at the beginning of your process:

SetUnhandledExceptionFilter(ptrToExceptionInfo =>
{
var errorCode = "0x" + Marshal.GetExceptionCode().ToString("x2");
...
return 1;
});

You can find more information about the possible return codes here:

https://msdn.microsoft.com/en-us/library/ms680634(VS.85).aspx

A "specialty" of the UnhandledExceptionFilter is that it isn't called if a debugger is attached. (At least not in my case of having a WPF app.) So be aware of that.

If you set all the appropriate ExceptionHandlers from above, you should be logging all exceptions that can be logged. For the more serious exceptions (like StackOverflowException and ExecutionEngineException) you have to find another way because the whole process is unusable after they happened. A possible way could perhaps be another process that watches the main process and logs any fatal errors.

Additional hints:

  • In the AppDomain.CurrentDomain.UnhandledException you can safely cast the e.ExceptionObject to Exception without having to worry - at least if you don't have any IL code that throws other objects than Exception: Why is UnhandledExceptionEventArgs.ExceptionObject an object and not an Exception?
  • If you want to suppress the Windows Error Reporting dialog, you can take a look here: How to terminate a program when it crashes? (which should just fail a unit test instead of getting stuck forever)
  • If you have a WPF application with multiple dispatchers you can also use a Dispatcher.UnhandledException for the other dispatchers.

Corrupted State Exception types other than AccessViolationException

I'm not sure if there even is a finite set of exceptions (regardless of CLR versions, but in general).

At least native code (or the CLR itself) could raise any exception, designating it as corrupting state, if you look at this CLR function.

   // Signature simplified for purposes of that answer, check link above for actual signature.
void RealCOMPlusThrow(OBJECTREF throwable, CorruptionSeverity severity = NotCorrupting);

That function (i.e. the macro COMPlusThrow that wraps it) is called in multiple places in the (Core) CLR.

The function IsProcessCorruptedStateException function seems to be ultimately used to determine if an exception is considered state corrupting. This function has two "overloads".

One is rather helpful, because it lists the following exception codes:

    STATUS_ACCESS_VIOLATION
STATUS_STACK_OVERFLOW
EXCEPTION_ILLEGAL_INSTRUCTION
EXCEPTION_IN_PAGE_ERROR
EXCEPTION_INVALID_DISPOSITION
EXCEPTION_NONCONTINUABLE_EXCEPTION
EXCEPTION_PRIV_INSTRUCTION
STATUS_UNWIND_CONSOLIDATE

At least paritially they map to .NET exception objects.

However, the other one "simply" checks if the exception object (native, not managed) has been marked was state corrupting.

Now, I'm far far far away from being an expert with the CLR's code, so YMMV.

One can surely spend hours in the CLR code to figure out how corrupted state handling works and what consequences that has for handling those in C# code.
But depending on what you are really trying to achieve by your question that
might lead to some serious yack shaving. ;-)

Corrupted state exceptions (CSE) across AppDomain

In the context of a corrupted state exception, in general, you cannot assume anything to be true anymore. The point of these exceptions is that something has happened, usually due to buggy unmanaged code, that has violated some core assumption that Windows or the CLR makes about the structure of memory. That means that, in theory, the very structures that the CLR uses to track which app domains exist in memory could be corrupted. The kinds of things that cause CSEs are generally indicative that things have gone catastrophically wrong.

Having said all that, off-the-record, in some cases, you may be able to make a determination that it is safe to continue from a particular exception. An EXCEPTION_STACK_OVERFLOW, for example, is probably recoverable, and an EXCEPTION_ACCESS_VIOLATION usually indicates that Windows caught a potential bug before it had a chance to screw anything up. It's up to you if you're willing to risk it, depending on how much you know about the code that is throwing the CSEs in the first place.

Handling process corrupted state exceptions using Async.Catch

This is a tricky problem - inside normal function, you can catch the ThreadAbortException and do something in reaction, but you cannot really handle it, because it will be automatically rethrown (and it will eventually kill the thread).

In F# async workflow, the exception is handled and F# async runtime stores it so that it can report it via a continuation, but before it gets a chance to do this, the exception is rethrown by .NET and it kills the thread (thus RunSynchronously hangs).

The problem is - to report exceptions, F# async needs to make some call. The call cannot be made on the current thread (which is being cancelled). If you're expecting the exception, you can start the work in a thread pool and handle it yourself. (F# cannot do that automatically, because it would be too much overhead).

You can use the following helper:

type Microsoft.FSharp.Control.Async with
static member CatchAbort<'R>(f : unit -> 'R) : Async<'R> =
async { let hndl = new AutoResetEvent(false)
let result = ref (Choice3Of3())
ThreadPool.QueueUserWorkItem(fun _ ->
try
result := Choice1Of3 (f())
hndl.Set() |> ignore
with
| e ->
// This handler runs correctly even for ThreadAbort
result := Choice2Of3 e
hndl.Set() |> ignore) |> ignore
let! _ = Async.AwaitWaitHandle(hndl)
match !result with
| Choice1Of3(res) -> return res
| Choice2Of3(exn) ->
// Wrap or rethrow the exception in some way
return raise exn
| Choice3Of3 _ -> return failwith "unexpected case" }

This starts the specified function (which is not asynchronous) on a thread pool thread. After the function completes or throws, it reports the result back to the original thread, which can resume the workflow.

To adapt your example, this should behave as expected:

let a = async {
let! value = Async.CatchAbort(fun () ->
System.Threading.Thread.CurrentThread.Abort()
"not aborted")
return value }

let b = async {
try let! m = a
printfn "ok"
with e -> printfn "Caught: %A" e }

AccessViolationException not caught despite HandleProcessCorruptedStateExceptions

You forgot to insert a try/catch around table.Start().

[HandleProcessCorruptedStateExceptions] definitely needs a try/catch to catch that AccessViolationException.

So your code should be:

[HandleProcessCorruptedStateExceptions]
private static void StartTable(Table table) {
try
{
table.Start();
}
catch (AccessViolationException)
{
// Ignore
}
}

You can take a look at here or here for references.



Related Topics



Leave a reply



Submit