简体   繁体   中英

Poor error handling: Throw inside Finally

I have the following code which I am running through fortify. Why it gets marked for poor error handling, throw inside finally?

private String getResourceContent(String fileName) throws IOException {

    try (InputStream resource = ErrorResource.classLoader.getResourceAsStream(fileName)) {
        return new String(resource.readAllBytes(), StandardCharsets.UTF_8);
    } catch (NullPointerException n) {
        throw new ErrorDescriptorException(
                String.format("Error loading Error description data from Resource file [%s].", fileName), n);
    }
}

Explanation

This is explained very well in the official documentation (see Poor Error Handling: Throw Inside Finally ). Let me quickly quote the important sections:

Using a throw statement inside a finally block breaks the logical progression through the try-catch-finally.

In Java, finally blocks are always executed after their corresponding try-catch blocks and are often used to free allocated resources , such as file handles or database cursors. Throwing an exception in a finally block can bypass critical cleanup code since normal program execution will be disrupted .

So you can easily bypass cleanup code by doing that, which leads to resource leaks.

Although not directly visible in your code, you actually have a hidden finally block since you are using try-with-resources which automatically closes the resource in a finally block.

Also see Throwing an exception inside finally where this was already discussed.


Example

Here is an example from the official documentation:

public void processTransaction(Connection conn) throws FileNotFoundException {
    FileInputStream fis = null;
    Statement stmt = null;
    try {
        stmt = conn.createStatement();
        fis = new FileInputStream("badFile.txt");
        ...
    } catch (FileNotFoundException fe) {
        log("File not found.");
    } catch (SQLException se) {
        // handle error
    } finally {
        if (fis == null) {
            // This bypasses cleanup code
            throw new FileNotFoundException();
        }

        if (stmt != null) {
            try {
                // Not executed if the exception is thrown
                stmt.close();
            }
            catch (SQLException e) {
                log(e);
            }
        }
    }
}

The call to stmt.close() is bypassed when the FileNotFoundException is thrown.


Note

Why are you checking for null using a NullPointerException instead of a basic if-else ? There is rarely ever a valid reason to catch a NullPointerException . Just do:

try (InputStream resource = ErrorResource.classLoader.getResourceAsStream(fileName)) {
    if (resource == null) {
        // TODO Throw your exception here
    }
    return new String(resource.readAllBytes(), StandardCharsets.UTF_8);
}

It might also help to improve the error message by telling the exact reason that the resource could not be found.

Beyond the misleading message from your tool, there is actually is poor error handling in your code, for multiple of reasons:

  • catching NPE is really bad practice. Either it is a bug (something that is null and shouldn't), or your code is missing a check if (whatever == null) and the corresponding code to deal with that expected situation
  • assuming that this NPE has exactly that meaning that you express in your new Exception is well, just guessing

In other words: without further information, it is not clear what exactly your tool complains about. But: one doesn't need a tool to understand: this is poor error handling.

Beyond that, such tools typically give some sort of information about their warnings. Meaning: there might be an "error id" coming with that warning, and you should be able to look up that "error id" in the documentation of your tool for further explanations.

Consider the following code, which is loosely based on yours:

String throwing(InputStream inputStream) throws IOException {
    try (InputStream resource = inputStream) {
        return "good";
    } catch (NullPointerException n) {
        return "bad";
    }
}

You see, no exceptions thrown here. Still, you cannot remove the throws IOException bit – how's that? Well, InputStream#close() can throw it, and it will be in the implicit finally block that the try-with-resources statement created. I guess there's not much you can do about it, it looks like a Fortify false positive.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM