Breaking Java exception-handling rules is easy

Did you know that any Java method can throw arbitrary checked exceptions without declaring them?

February 28, 2003

Q: I am calling an external method in my application and want to intercept any exceptions it can possibly throw. Should I catch java.lang.Exception ?

A: Handling all runtime exceptions and checked exceptions declared by a given method is not sufficient to build the ultimate defense against external failures. In this Java Q&A installment, I show how any Java method can throw an arbitrary exception—checked or unchecked—with relative ease and without employing any native code.

You may have read the recent JavaWorld article, "Java Tip 134: When Catching Exceptions, Don't Cast Your Net Too Wide" by Dave Schweisguth. That excellent article warned against catching java.lang.Exception or java.lang.Throwable as a bad, catch-all practice. Catching the most specific exception you can is important for code maintainability and future refactoring. However, this rule must be bent under special circumstances. Particularly, if you intend not to crash and retain your data structures' exception safety in the presence of callouts to external, potentially hostile code, you must intercept java.lang.Throwable regardless of the actual exception type being thrown.

For a specific example, imagine you have a server application that loads implementations of this interface:

public interface IFoo
{
    /**
     * This method can't throw any checked exceptions...or can it?
     */
    void bar ();
} // End of interface

For argument's sake, let's say you host such a server somewhere, and various IFoo implementations could be uploaded from external sources. You have coded

        try
        {            
            IFoo foo = ... // get an IFoo implementation
            foo.bar ();
        }
        catch (RuntimeException ioe)
        {
            // Handle 'ioe' ...
        }
        catch (Error e)
        {
            // Handle or re-throw 'e' ...
        }

and think you have handled all the possibilities here. You don't need to add any more catch blocks, say, for

java.io.IOException

because, well, no

IFoo

implementation could throw it from

IFoo.bar()

, right? (In fact, as Schweisguth discussed, even if you tried to add a catch for

java.io.IOException

, the compiler will likely reject it as an unreachable catch block.)

Wrong. The bar() implementation in the EvilFoo class I wrote to prove a point will happily throw anything you passed to the class constructor (saved in m_throwthis):

    public void bar ()
    {
        EvilThrow.throwThrowable (m_throwthis);
    }

Run the Main class from the download associated with this article:

public class Main
{
    public static void main (final String[] args)
    {
        // This try/catch block appears to intercept all exceptions that
        // IFoo.bar() can throw; however, this is not true
        try
        {            
            IFoo foo = new EvilFoo (new java.io.IOException ("SURPRISE!"));
            foo.bar ();
        }
        catch (RuntimeException ioe)
        {
            // Ignore ioe
        }
        catch (Error e)
        {
            // Ignore e
        }
    }
} // End of class

You will see an instance of java.io.IOException getting thrown from bar() and not getting caught by any catch blocks:

>java -cp classes Main
Exception in thread "main" java.io.IOException: SURPRISE!
        at Main.main(Main.java:23)

What happened here?

The key observation is that the usual Java rules for checked exceptions are enforced at compile time only. At runtime, a JVM will not ensure that the exception thrown by a method actually matches the set of exceptions declared by the method in its throws declaration (see the JVM specification). Since it is the responsibility of the calling method to intercept and handle all checked exceptions thrown from a called method, anything not declared by the called method will simply not get noticed and will pass up the call stack. This behavior may change in future JVMs, but it has not at the time of this writing.

Having said that, if the normal behavior is compiler-enforced, how did I create EvilFoo? There are at least two ways you can create Java methods that throw checked exceptions they never declared:

  • Thread.stop(Throwable) has been deprecated for some time, but it still works and will inject an arbitrary Throwable into whichever Thread it is called on

  • Separate compilation: you can compile EvilFoo against a temporary version of IFoo that actually declares bar() to throw whichever checked exception type you fancy

I used a variation of the latter option: I compiled a class EvilThrow initially defined as:

public abstract class EvilThrow
{
    public static void throwThrowable (Throwable throwable)
        throws Throwable
    {
        throw throwable;
    }
}

Next, I disassembled the result using

JasminVisitor

from the Byte Code Engineering Library (BCEL), removed the

throws Throwable

declaration from the

throwThrowable()

method in the assembly code, and compiled the new version using Jasmin assembler (see

Resources

for links to both tools). The resulting class is available for your exception throwing pleasure in this article's

download

.

This brings me to the following maxim: if you have a valid reason to code a catch-all construct, it should always catch java.lang.Throwable and not merely java.lang.Exception. It applies whenever you develop a managed runtime application (such as a component container) and must execute external components (i.e., servlets, Enterprise JavaBeans (EJB), and JavaBeans) that might contain buggy or even malicious code. Make sure you catch Throwable and filter out Errors as needed.

The following example illustrates what can happen if you do not follow this advice.

Example: Breaking SwingUtilities.invokeAndWait()

javax.swing.SwingUtilities.invokeAndWait() is a popular utility method for executing a Runnable on the Abstract Windowing Toolkit (AWT) event thread. It is used when an application thread must update the graphical user interface (GUI) and obey all Swing threading rules. The documentation states that an uncaught exception from the Runnable.run() will be caught and rethrown, wrapped in an InvocationTargetException.

The implementation of this in Sun Microsystems' Java 2 Platform, Standard Edition (J2SE) 1.4.1 assumes, somewhat naively, that such uncaught exceptions can only be subclasses of java.lang.Exception. Here is an extract from java.awt.event.InvocationEvent used by SwingUtilities.invokeAndWait() behind the scenes:

    public void dispatch() {
    if (catchExceptions) {
        try {
        runnable.run();
        } 
        catch (Exception e) {
        exception = e;
        }
    }
    else {
        runnable.run();
    }
    if (notifier != null) {
        synchronized (notifier) {
        notifier.notifyAll();
        }
    }
    }

The problem with this code is that if runnable.run() throws a Throwable, the catch block is not entered, and the notifier.notifyAll() line never executes. The calling application thread will then wait on a nonpublic lock object inside java.awt.EventQueue.invokeAndWait() that will never be signaled (lock.wait() will never execute):

    public static void invokeAndWait(Runnable runnable)
             throws InterruptedException, InvocationTargetException {
        class AWTInvocationLock {}
        Object lock = new AWTInvocationLock();
        InvocationEvent event = 
            new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
                               true);
        synchronized (lock) {
            Toolkit.getEventQueue().postEvent(event);
            lock.wait();
        }
        Exception eventException = event.getException();
        if (eventException != null) {
            throw new InvocationTargetException(eventException);
        }
    }

To see the resulting deadlock for yourself, make EvilFoo implement Runnable:

    public void run ()
    {
        bar ();
    }

Then, run it from

Main

as follows:

    SwingUtilities.invokeAndWait (new EvilFoo (new Throwable ("SURPRISE!")));

As you can see, failure to be defensive about exceptions can make untrusted code force your code into execution paths you may not be prepared to handle.

Protect your code

I hope this article stimulated your thinking about what it takes to write truly defensive Java code and provided a new perspective on Java exceptions. I doubt the EvilThrow class will be useful in a real application, but you might find it handy for testing exception safety and defensibility of your designs.

Vladimir Roubtsov has programmed in a variety of languages for more than 13 years, including Java since 1995. Currently, he develops enterprise software as a senior developer for Trilogy in Austin, Texas.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies