Programmers often suppress checked exceptions. That is, they often catch exceptions with a catch block, with the catch body doing nothing or something trivial, such as printing the stack trace. Often the catch body will have a comment stating that the exception is explicitly being ignored.
Exceptions must be handled appropriately. There are few valid reasons for suppressing exceptions; the most common are cases where the client cannot be expected to recover from the underlying problem. In these cases, it is good practice to allow the exception to propagate outwards rather than to catch and suppress the exception.
Catching and suppressing exceptions is considered bad practice for several reasons. Exceptions disrupt the expected control flow of the application. For example, statements in the try
block that follow the statement that caused the exception are skipped.
Each catch
block must ensure that the program continues only with valid invariants. Consequently, the catch
block must either recover from the exceptional condition, re-throw the exception to allow a higher level of abstraction to attempt recovery, or throw an exception that is appropriate to the context of the catch
block. When recovery is possible, any instructions inside the the try
block whose execution is required must be moved outside the try
block to ensure that they are executed.
Noncompliant Code Example
This noncompliant code example catches IOException
but fails to handle the exception.
try { //... } catch (IOException ioe) { // Ignore }
Noncompliant Code Example
Printing the exception's stack trace can be useful for debugging purposes but results in program execution that is equivalent to suppressing the exception. Printing the stack trace can also result in unintentionally leaking information about the structure and state of the process to an attacker.
try { //... } catch (IOException ioe) { ioe.printStacktrace(); }
Note that even though the application reacts to the exception by printing out a stack trace, it proceeds as though the exception were not thrown. That is, the future behavior of the application is unaffected by the throwing of the exception, other than the fact that statements in the try block after the statement that caused the exception are skipped. The IOException
indicates that an I/O operation attempted by the application failed; it is unlikely that assuming that the attempted operation succeeded will permit the application to operate correctly.
Compliant Solution
This compliant solution attempts to recover from a FileNotFoundException
by forcing the user to specify another file when a particular file cannot be found in the user-specific directory.
boolean volatile validFlag = false; do { try { // If requested file does not exist, throws FileNotFoundException // If requested file exists, sets a Boolean flag validFlag to true validFlag = true; } catch (FileNotFoundException e) { // Ask the user for a different filename } } while (validFlag != true); // Use the file
The user is allowed to access only files in a user-specific directory. This prevents any other IOException
that escapes the loop from leaking potentially sensitive file system information. See guideline ERR06-J. Do not allow exceptions to expose sensitive information for additional information.
Noncompliant Code Example
If a thread is interrupted while sleeping or waiting, it causes a java.lang.InterruptedException
to be thrown. But the run()
method of interface Runnable
cannot throw a checked exception, and so it must handle InterruptedException
. This noncompliant code example catches and suppresses InterruptedException
.
class Foo implements Runnable { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore } } }
This code prevents callers higher up the call stack from determining that an interrupted exception occurred; consequently, they are unable to act on the exception [[Goetz 2006]]. Likewise, if this code was called in its own thread, it prevents the calling thread from knowing that this thread was interrupted.
Compliant Solution
This compliant solution catches the InterruptedException
and restores the interrupted status by calling the interrupt()
method on the current thread.
class Foo implements Runnable { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Reset interrupted status } } }
Consequently, code that is higher up on the call stack (or code from a calling thread) can see that an interrupt was issued [[Goetz 2006]].
Exceptions
EXC00-EX0: An exception that occurs during the freeing of a resource may be suppressed. Typically resources are freed in catch or finally blocks, with the resource never being used again; therefore the exception has no influence on future program behavior. Examples of freeing resources include closing files, network sockets, shutting down threads, etc.
EXC00-EX1: When recovery from an exceptional condition is impossible at a particular abstraction level, code at that level should avoid handling that exceptional condition. In such cases, an appropriate exception must be thrown so that higher level code can catch the exceptional condition and can attempt recovery. The most common implementation for this case is to omit a catch
block and consequently allow the exception to propagate normally, as shown below.
// When recovery is possible at higher levels private void doSomething() throws FileNotFoundException { // Requested file does not exist; throws FileNotFoundException // Higher level code can handle it by displaying a dialog box and asking // the user for the file name }
Some APIs may limit the permissible exceptions thrown by particular methods. In such cases, it may be necessary to catch an exception and either wrap it in a permitted exception or translate it to one of the permitted exceptions.
Alternatively, when higher level code is also unable to recover from a particular exception, the checked exception may be wrapped in an unchecked exception and re-thrown.
try { // Requested file does not exist // User is unable to supply the file name } catch (FileNotFoundException e) { throw new IOException(e); }
EXC00-EX2: "The only situation in which it is acceptable to swallow an interrupt is when you are extending Thread and therefore control all the code higher up on the call stack" [[Goetz 2006]]. In such cases InterruptedException
may be caught and suppressed. A interruption request may also be suppressed by code that implements a thread's interruption policy [[Goetz 2006, pg 143]].
Risk Assessment
Ignoring or suppressing exceptions violates the fail-safe criteria of an application.
Guideline |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
EXC00-J |
low |
probable |
medium |
P4 |
L3 |
Automated Detection
Detection of suppressed exceptions is straightforward. Sound determination of which specific cases represent violations of this guideline, and which represent permitted exceptions to the guideline is infeasible. Heuristic approaches may be effective.
Related Vulnerabilities
Bibliography
[[Bloch 2008]] Item 65: "Don't ignore exceptions", Item 62: "Document all exceptions thrown by each method"
[[Goetz 2006]] 5.4 Blocking and interruptible methods
[[JLS 2005]] Chapter 11, Exceptions
[[MITRE 2009]] CWE ID 390 "Detection of Error Condition Without Action"
06. Exceptional Behavior (ERR) 06. Exceptional Behavior (ERR) ERR01-J. Use a class dedicated to reporting exceptions