Skip to main content

ERR00-G: Do Not Suppress Exceptions


Introduction

Developers have the ability to suppress exceptions by catching them and doing nothing with them (an empty catch block), logging trivial messages, or simply printing a stack trace of the exception message. However, these practices can lead to unintended consequences. If exceptions are ignored, it can cause the program to enter into an unrecoverable state. When recovering from an exception, the developer needs to programmatically answer questions related to the occurrence of the exception, such as why the exception happened, whether any data has been affected, whether any data should be reset and whether it is safe to continue running the program. Unhelpful log messages only complicate issues and don't provide solutions. Additionally, exposing a stack trace can also reveal information about the structure and state of the process to an attacker.

To ensure the program continues to operate with valid invariants, it is the developer's responsibility to make sure that the catch routing for the code block either recovers from the exceptional condition, re-throws the exception to allow the next nearest enclosing catch clause of a try statement to recover, or throws an exception that is appropriate to the context of the catch block. This resolution strategy is crucial to maintain the program's stability and continuity.

Non-Compliant Code Example

This non-compliant code example simply prints the exception's stack trace:

// Noncompliant Code Example

function printFileNCE(filename: String) {
try {
printFile(filename)
}
catch (ioe: IOException) {
print("FileNotFoundException")
ioe.printStackTrace()
}
}

Printing the exception's stack trace can be helpful for debugging purposes, but the resulting program execution is equivalent to suppressing the exception. Printing the stack trace can also leak information about the structure and state of the process to an attacker (see Rule ERR03-G Do Not Throw Exceptions That Expose Sensitive Information for more details). Note that even though this non-compliant code example reacts to the exception by printing a stack trace, it proceeds as though the exception were not thrown. The application's behaviour is unaffected by the exception being thrown, except that any expressions or statements that occur in the try block after the point from which the exception is thrown are not evaluated.

Compliant Solution

This compliant solution handles a FileNotFoundException by requesting that the user specify another file name:

// Compliant Solution

function printFileCS(filename: String) {
var quit : boolean = false
do {
try {
printFile(filename)
// If requested file does not exist, throws FileNotFoundException
// If requested file exists, sets valid flag to true
quit = true
} catch (e: FileNotFoundException) {
// Ask the user for a different file name
print("Please enter new file name:")
quit = true
}
} while (not quit)
}

To comply with ERR01-J. Do not allow exceptions to expose sensitive information, the user should only be allowed to access files in a user-specific directory. This prevents any other IOException that escapes the loop from leaking sensitive file system information.

Non-Compliant Code Example

The isName() function in this non-compliant code example accepts a string and returns "true" if the string consists of two substrings separated by white space, each starting with an uppercase letter. The function also throws a RuntimeException when passing a null string argument.

// Noncompliant Code Example

function isName(s: String) : boolean {
if (s == null) return false
var names : String[] = s.split(" ");
if (names.length != 2) return false
return (Character.isUpperCase(names[0].codePointAt(1)) &&
Character.isUpperCase(names[1].codePointAt(1)))
}

Unfortunately, the isName() method also returns "false" when "s" is a null pointer. This suppresses the exception by turning the error into a valid response from the function, making it indistinguishable from deciding a non-null string did not meet the criteria established by this function of being a name.

Compliant Solution

This compliant solution throws NullPointerException when "s" is a null pointer:

// Compliant Solution

function isName(s: String) : boolean {
if (s == null) throw new NullPointerException()
var names: String[] = s.split(" ");
if (names.length != 2) return false
return (Character.isUpperCase(names[0].codePointAt(1)) &&
Character.isUpperCase(names[1].codePointAt(1)))
}

Exceptions

ERR00-G-EX1: An InterruptedException may be caught and suppressed when extending class Thread [Goetz 2006]. An interruption request may also be suppressed by code implementing a thread's interruption policy [Goetz 2006, p. 143].

Risk Assessment

Ignoring or suppressing exceptions can result in an inconsistent program state.

RuleSeverityLikelihoodRemediation CostPriorityLevel
ERR00-GLowLikelyMediumL4L3

Additional resources