Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch block is not being evaluated when exceptions are thrown from finallys

This question came about because code that worked previously in .NET 4.0 failed with an unhandled exception in .NET 4.5, partly because of try/finallys. If you want details, read more at Microsoft connect. I used it as the base for this example, so it might be helpful to reference.

The code

For the people who chose to not read about the details behind this question, here is a very quick look at the conditions where this happened:

using(var ms = new MemoryStream(encryptedData))
using(var cryptoStream = new CryptoStream(encryptedData, decryptor, CryptoStreamMode.Read))
using(var sr = new StreamReader(cryptoStream))

This issue is that there are exceptions thrown from the Dispose method of CryptoStream (since they are inside a using statement, these exceptions happen to be thrown from two different finally blocks). When cryptoStream.Dispose() is called by the StreamReader, the CryptographicException is thrown. The second time cryptoStream.Dispose() is called, in its using statement, it throws a ArgumentNullException

The following code removes most of the unnecessary code from the link provided above, and unwinds the using statements into try/finallys to clearly show that they are being throw in finally blocks.

using System;
using System.Security.Cryptography;
namespace Sandbox
{
    public class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                try
                {
                    try
                    {
                        Console.WriteLine("Propagate, my children");
                    }
                    finally
                    {
                        // F1
                        Console.WriteLine("Throwing CryptographicExecption");
                        throw new CryptographicException();
                    }
                }
                finally
                {
                    // F2
                    Console.WriteLine("Throwing ArgumentException");
                    throw new ArgumentException();
                }
            }
            catch (ArgumentException)
            {
                // C1
                Console.WriteLine("Caught ArgumentException");
            }
            // Same behavior if this was in an enclosing try/catch
            catch (CryptographicException)
            {
                // C2
                Console.WriteLine("Caught CryptographicException");
            }
            
            Console.WriteLine("Made it out of the exception minefield");
        }
    }}

Note: The try/finally correspond to expanded using statements from the referenced code.

Output:

    Propagate, my children
    Throwing CryptographicExecption
    Throwing ArgumentException
    Caught ArgumentException
    Press any key to continue . . .

It doesn't appear that the CryptographicException catch block is ever executed. However, removing that catch block causes the exception to terminate the runtime.

A little more information

EDIT: This was updated to the newest revision of the specification. The one I happened to grab off of MSDN had older wording. Lost has been updated to terminated.

Diving into the C# spec, sections 8.9.5 and 8.10 discuss exception behavior:

  • When an exception is thrown, including from inside a finally block, control is transferred to the first catch clause in an enclosing try statement. This continues up try statements until a suitable one is found.
  • If an exception is thrown during execution of a finally block, and an exception was already being propagated, that exception is terminated

"Terminated" makes it seem that the first exception would forever be hidden by the second thrown exception, though it doesn't seem to be what is happening.

I'm sure the question is in here somewhere

For the most part, it's easy to visualize what the runtime is doing. The code executes to the first finally block (F1) where an exception is thrown. As the exception propagates, the second exception is thrown from the second finally block (F2).

According to the spec, the CryptographicException thrown from F1 is now terminated, and the runtime is looking for a handler for the ArgumentException. The runtime finds a handler, and executes the code in the catch block for the ArgumentException (C1).

Here is where it gets foggy: the spec says that the first exception would be terminated. However, if the second catch block (C2) is removed from the code, the CryptographicException that was supposedly lost, is now an unhandled exception that terminates the program. With the C2 present, the code will not terminate from an unhandled exception, so on the surface it appears to be handling the exception, but the actually exception handling code in the block is never executed.

Questions

The questions are basically the same, but re-worded for specificity.

  1. How is it that the CryptographicException becomes terminated due to the ArgumentException exception thrown from the enclosing finally block, as removing the catch (CryptographicException) block causes the exception to go unhandled and terminate the runtime?

  2. Since the runtime seems to be handling the CryptographicException when the catch (CryptographicException) block is present, why is the code inside of the block not executing?


Extra informational Edit

I'm still looking into the actual behavior of this, and many of the answers have been particularly helpful in at least answering parts of the above questions.

Another curious behavior, that happens when you run the code with the catch (CryptographicException) block commented out, is the difference between .NET 4.5 and .NET 3.5. .NET 4.5 will throw the CryptographicException and terminate the application. .NET 3.5, however, seems to behave more according to the C# spec where the exception.

Propagate, my children
Throwing CryptographicExecption

Unhandled Exception: System.Security.Cryptography.CryptographicException [...]
ram.cs:line 23
Throwing ArgumentException
Caught ArgumentException
Made it out of the exception minefield

In .NET 3.5, I see what I read in the spec. The exception becomes "lost", or "terminated", since the only thing that ever needs to get caught is the ArgumentException. Because of that the program can continue execution. I only have .NET 4.5 on my machine, I wonder if this happens in .NET 4.0?

like image 811
Christopher Currens Avatar asked Aug 27 '12 23:08

Christopher Currens


People also ask

What happens if exception thrown in catch block?

Answer: When an exception is thrown in the catch block, then the program will stop the execution. In case the program has to continue, then there has to be a separate try-catch block to handle the exception raised in the catch block.

What happens if an exception is thrown from the finally or catch block in Java?

Methods invoked from within a finally block can throw an exception. Failure to catch and handle such exceptions results in the abrupt termination of the entire try block.

Can you throw an exception from a catch or a finally block?

Some resource cleanup, such as closing a file, needs to be done even if an exception is thrown. To do this, you can use a finally block. A finally block always executes, regardless of whether an exception is thrown. The following code example uses a try / catch block to catch an ArgumentOutOfRangeException.

Which exception Cannot be caught in catch block?

Exceptions that Can't be Caught One such exception is the limit exception ( System. LimitException ) that the runtime throws if a governor limit has been exceeded, such as when the maximum number of SOQL queries issued has been exceeded.


2 Answers

Exception processing in .NET has 3 distinct stages:

  • stage 1 kicks in gear as soon as a throw statement executes. The CLR goes looking for a catch block that's in scope that advertizes that it is willing to handle the exception. At this stage, in C#, no code executes. Technically it is possible to execute code but that capability is not exposed in C#.

  • stage 2 starts once the catch block is located and the CLR knows where execution resumes. It can then reliably determine what finally blocks need to be executed. Any method stack frames are unwound as well.

  • stage 3 starts once all finally blocks are completed and the stack is unwound to the method that contains the catch statement. The instruction pointer is set to the first statement in the catch block. If this block contains no futher throw statements, execution resumes as normal at the statement past the catch block.

So a core requirement in your code snippet is that there is a catch (CryptographicException) in scope. Without it, stage 1 fails and the CLR doesn't know how to resume execution. The thread is dead, usually also terminating the program depending on exception handling policy. None of the finally blocks will execute.

If in stage 2 a finally block throws an exception then the normal exception handling sequence is immediately interrupted. The original exception is "lost", it never gets to stage 3 so cannot be observed in your program. Exception handling starts back at stage 1, now looking for the new exception and starting at the scope of that finally block.

like image 86
Hans Passant Avatar answered Sep 28 '22 12:09

Hans Passant


If an exception is thrown during execution of a finally block, and an exception was already being propagated, that exception is lost

Basically, what's happening when you execute:

  • CryptographicException is thrown in inner finally.
  • Outer-scope finally executes, and throws ArgumentException. Since "CryptographicException" was "being propogated" at this point in time, it is lost.
  • Final catches occur, and ArgumentException is caught.

... and it wouldn't make sense for the first exception to simply disappear into the ether, just because there was another exception thrown from a different finally block.

This is exactly what happens, based on the C# language specification you quoted. The first exception (CryptographicException) effectively disappears - it's "lost".

You can only reach this state by explicitly using finally, though, so I believe the assumption is that you're providing the error handling with this expectation or possibility in mind (as you're using try at that point, which means you've accepted you may have an exception).

This is basically explained in detail in the spec in 8.9.5 (the text in 8.10 you quoted refers to this section):

If the finally block throws another exception, processing of the current exception is terminated.

The first exception, in your case the ArgumentException, basically "disappears".

like image 31
Reed Copsey Avatar answered Sep 28 '22 12:09

Reed Copsey