Java requires that each method must address every checked exception that can be thrown during its execution either by handling the exception within a try-catch
block or by declaring that the exception can propagate out of the method (via the throws
clause). Unfortunately, there are a few techniques that permit undeclared checked exceptions can to be thrown at runtime. Consequently, callers cannot Such methods foil the ability of caller methods to use the throws
clause to determine the complete set of checked exceptions that could propagate from an invoked method. Consequently such techniques must not be used to throw undeclared checked exceptions.
David: your paragraph about checked vs. unchecked exceptions goes here.
Clients or callers need to know which exceptions the underlying code can throw. Consequently, developers should – and developers of security critical software must — sufficiently document all possible unchecked and undeclared checked exceptions that can be thrown by a method.
Noncompliant Code Example
...
This noncompliant code example is insecure both because it can throw undeclared checked exceptions and also because it uses the sun.misc.Unsafe
class. All sun.*
classes are unsupported and undocumented because their use can cause portability and backward compatibility issues.
...
bgColor | #FFcccc |
---|
...
(
...
java.lang.reflect.
...
Class.newInstance()
)
Noncompliant Code Example
Any checked exception thrown by the default constructor of Class.newInstance()
is propagated to the caller, even though Class.newInstance()
declares that it throws only InstantiationException
and IllegalAccessException
. This noncompliant code example demonstrates one way to use Class.newInstance()
to throw arbitrary exceptions, whether checked or unchecked.
This noncompliant code example also violates ERR13-J. Do not throw RuntimeException and ERR14-J. Do not catch RuntimeException.
Code Block | ||
---|---|---|
| ||
public class BadNewInstanceNewInstance { private static Throwable throwable; private BadNewInstanceNewInstance() throws Throwable { throw throwable; } public static synchronized void undeclaredThrow(Throwable throwable) { // These twoexceptions should not be passed if (throwable instanceof IllegalAccessException || throwable instanceof InstantiationException) { throw new IllegalArgumentException(); // Unchecked, no declaration required } BadNewInstanceNewInstance.throwable = throwable; try { // next line throws the Throwable argument passed in above, // even though the throws clause of class.newInstance fails // to declare that this may happen; see JavaDoc BadNewInstanceNewInstance.class.newInstance(); } catch (InstantiationException e) { /* unreachable */ } } catch (IllegalAccessException e) { /* unreachable */ } } finally { // Avoid memory leak BadNewInstanceNewInstance.throwable = null; } } } public class UndeclaredException { public static void main(String[] args) { // No declared checked exceptions BadNewInstanceNewInstance.undeclaredThrow(new Exception("Any checked exception")); } } |
Noncompliant Code Example (Class.newInstance()
workarounds)
When the programmer wishes to catch and handle the possible undeclared checked exceptions, the compiler refuses to believe that any can be thrown in the particular context. A simple workaround is to catch
The following noncompliant code example attempts to work around the fact that Class.newInstance()
can throw undeclared checked exceptions. It catches Exception
and dynamically check checks whether the caught exception is an instance of the possible checked exception (carefully re-throwing all other exceptions, of course), as shown below. This approach is fragile, because any unanticipated checked exception bypasses the dynamic check. See ERR14-J. Do not catch RuntimeException for more details.
Code Block |
---|
public static void main(String[] args) { try { BadNewInstanceNewInstance.undeclaredThrow(new IOException("Any checked exception")); } catch(Exception e) { if (e instanceof IOException) { System.out.println("IOException occurred"); } else if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { //some other unknown checked exception } } } |
Compliant Solution
...
(java.lang.reflect.Constructor.newInstance()
)
This compliant solution uses the Use method {{ Wiki Markup Constructor.newInstance()
}} method rather than {{Class.newInstance()
}}. The {{java.lang.reflect. Constructor.newInstance()
method wraps any exceptions thrown from within the constructor into a checked exception called InvocationTargetException
.
Code Block | ||
---|---|---|
| ||
public static synchronized void undeclaredThrow( |
...
Throwable throwable) {
// These exceptions should not be passed
if (throwable instanceof IllegalAccessException ||
throwable instanceof InstantiationException ||
throwable instanceof NoSuchMethodException ||
throwable instanceof InvocationTargetException) {
throw new IllegalArgumentException(); // Unchecked, no declaration required
}
NewInstance.throwable = throwable;
try {
Constructor constructor = NewInstance.class.getConstructor( new Class<?>[0] );
constructor.newInstance();
} catch (InstantiationException e) { /* unreachable */
} catch (NoSuchMethodException e) { /* unreachable */
} catch (IllegalAccessException e) { /* unreachable */
} catch (InvocationTargetException e) {
System.out.println("Exception thrat was thrown: " + e.getCause().toString());
} finally { // Avoid memory leak
NewInstance.throwable = null;
}
}
|
Noncompliant Code Example (sun.misc.Unsafe
)
This noncompliant code example is insecure both because it can throw undeclared checked exceptions and also because it uses the sun.misc.Unsafe
class. All sun.*
classes are unsupported and undocumented because their use can cause portability and backward compatibility issues.
Classes that are loaded by the bootstrap class loader have the permissions needed to call the static
factory method Unsafe.getUnsafe()
. Arranging to have an arbitrary class loaded by the bootstrap class loader without modifying the sun.boot.class.path
system property can be difficult. However, an alternative way to gain access is to change the accessibility of the field that holds an instance of Unsafe
through the use of reflection. This works only when permitted by the current security manager (which would violate guideline ENV04-J. Do not grant ReflectPermission with target suppressAccessChecks.) Given access to Unsafe
, a call can throw an undeclared checked exception by calling the Unsafe.throwException()
method.
Code Block | ||
---|---|---|
| ||
import java.io.IOException;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class UnsafeCode {
public static void main(String[] args)
throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe u = (Unsafe) f.get(null);
u.throwException(new IOException("No need to declare this checked exception"));
}
}
|
Noncompliant Code Example (Generic exception)
Code Block | ||
---|---|---|
| ||
// Generic type for a builder used to build any object of type T
public interface Builder<T> {
public T build();
}
|
A client can pass a builder to a method and request the creation of an object. A bounded wildcard type should be used to constrain the builder's type parameter. In the code snippet that follows, a US Dollar (USD) is built from coins of different denomination.
Code Block |
---|
USD buildCurrency(Builder<? extends denomination> currencyBuilder) { /* ... */ }
|
For further details on implementing the builder pattern, refer to guideline OBJ05-J. Prevent access to partially initialized objects. In the example described in that guideline, the Currency.Builder
class must implement the Builder interface highlighted in this recommendation.
...
An unchecked cast of a generic type with parameterized exception declaration can also result in unexpected checked exceptions. The compiler complains unless the warnings are suppressed.
Code Block | ||
---|---|---|
| ||
interface Thr<EXC extends Exception> { void fn() throws EXC; } public class UndeclaredGen { static void undeclaredThrow() throws RuntimeException { @SuppressWarnings("unchecked") // Suppresses warnings Thr<RuntimeException> thr = (Thr<RuntimeException>)(Thr) new Thr<IOException>() { public void fn() throws IOException { throw new IOException(); } }; thr.fn(); } public static void main(String[] args) { undeclaredThrow(); } } |
Noncompliant Code Example (Thread.stop()
)
Wiki Markup |
---|
According to the Java API \[[API 2006|AA. Bibliography#API 06]\], class {{Thread}} documentation |
...
Code Block | ||
---|---|---|
| ||
static void sneakyThrow(Throwable t) { Thread.currentThread().stop(t); } |
Note that Thread.stop()
is deprecated, and so this code also violates MET15-J. Do not use deprecated or obsolete classes or methods).
Noncompliant Code Example (bytecode manipulation)
Wiki Markup |
---|
It is also possible to disassemble a class, remove any declared checked exceptions and reassemble the class so that checked exceptions are thrown at runtime when the class is used \[[Roubtsov 2003|AA. Bibliography#Roubtsov 03]\]. Compiling against a class that declares the checked exception and supplying at runtime a class that lacks the declaration also suffices. Similarly, a compiler other than {{javac}} might handle checked exceptions differently. Undeclared checked exceptions can also be produced through crafted use of the {{sun.corba.Bridge}} class. All these methods are strongly discouraged. |
Compliant Solution
Refrain from employing code (whether legitimate or hostile) that can throw undeclared checked exceptions. When this is unavoidable, explicitly document the behavior. Finally, never use deprecated methods such as Thread.stop()
(as required by MET15-J. Do not use deprecated or obsolete classes or methods).
Risk Assessment
Failure to document undeclared checked exceptions can result in checked exceptions that the caller is unprepared to handle, consequently violating the safety property.
...