强化finally内的抛出错误漏洞

huangapple go评论110阅读模式
英文:

Fortify throw inside finally bugs

问题

我遇到了以下强化问题:
错误处理不当:在 finally 内部抛出异常
我没有在 finally 块内抛出异常,但仍然显示这个错误。
请查看下面的代码:

catch (IOException | JSONException | URISyntaxException e) {
    if (instream == null) {
        throw new IOException("InputStream not valid");
    }
    return e.getMessage();
} finally {
    if (instream != null) {
        instream.close();
    }
    if (urlConnection != null) {
        urlConnection.disconnect();
    }
}
英文:

I am getting fortify issue as :
Poor Error Handling: Throw Inside Finally
I am not throwing exception inside finally block, but still it is showing this error.
Please find the code below.`

catch( IOException | JSONException | URISyntaxException e)
	{
		if(instream == null) {
			throw new  IOException("InputStream not valid");
		}
		return e.getMessage();
	}finally {
		if(instream != null ) {
			instream.close();
		}
		if(urlConnection != null ) {
			urlConnection.disconnect();
		}
	}

答案1

得分: 1

不是,是这样的:inStream.close() 可能会抛出异常。

通常来说,实际上“治理”这个警告可能会让你的代码变得更糟。问题在于:

如果 try 块抛出了一个未捕获的异常

在这种情况下,finally 块会被执行。假设 finally 块运行了 in.close();。特别是如果我们之所以进入这里是因为 in 被断开连接并开始抛出 IOEx,那么这很可能也会抛出一个 IOEx。从 finally 块抛出的任何异常都会“覆盖”导致我们进入这里的异常,而来自该关闭调用的堆栈跟踪则不太有用。

所以这是不好的

我们可以通过将 finally 块中的 in.close() 放在 try { .. } catch (Exception ignore) {} 中来修复它。但接下来,另一种情况就变得很糟糕:

如果我们以“正常”的方式到达这里

可能会出现这样的情况:一个输入流可以完美地工作,然后在关闭它时抛出异常。很长一段时间以来,常见的智慧是“嗯,无论如何,谁在乎呢,我已经得到了我的数据”,但这不是一个明智的想法:如果输入流的关闭操作最终抛出异常,那可能意味着您实际上还没有获取到所有数据,否则,为什么会发生这种情况呢?因此,现在,悄无声息地吞咽任何异常都是一个坏主意,我们希望将其抛出。

在不费力地弯腰前提下,使用一个布尔值来跟踪我们如何进入 finally 块,并根据它来决定是否吞咽该异常,这是__不可能的__。

由于这很棘手,try-with-resources 实际上是做得很好的,它会生成必要的样板代码。

因此,真正的解决方案是:如果你在 finally 块中关闭资源,那就不要这样做。改用 try (ResourceType r = new ResourceType()) { ... } 语法来代替。

如果你确实不能这样做,而且你也不能通过创建一个 AutoClosable 的包装器来使其正常工作,比如如果资源为 null 则什么都不做,那么你基本上被迫告诉你的代码检查工具停止对此进行抱怨。

英文:

No, you are: inStream.close() can throw.

Generally, 'curing' the warning is actually going to make your code worse. This is the problem:

IF the try block throws an uncaught exception

In this case, the finally block is executed. Let's say that the finally block runs in.close();. Especially if the entire reason we got here is that in got disconnected and started throwing IOEx, this is likely to also throw an IOEx. Any exceptions thrown out of a finally block 'overwrite' the exception that caused us to be here, and the stack trace from that close call is far less useful.

So this is bad.

We can fix it by wrapping the in.close() in your finally block in a try { .. } catch (Exception ignore) {}. But then, the other scenario is really bad:

IF we get here 'normally'

It's possible for an input stream to work perfectly, and then when you close it, it throws. For a good long while common wisdom was 'eh, whatever, who cares, I got my data', but that's not a sensible idea: If an input's close ends up throwing, that probably means you didn't actually get all data yet, otherwise, why did it do that? So, now, silently swallowing any exceptions is a bad idea, and we want to throw it.

Without bending over backwards, using a boolean to track how we got to the finally block and depending on it, swallowing that exception or not, it is not possible to do it right.

Because this is so tricky, try-with-resources actually does it right and generates the requisite boilerplate.

Thus, the REAL solution: If you are closing resources in a finally block, don't. Use the try (ResourceType r = new ResourceType()) { ... } syntax instead.

If you truly can't do that, and you also can't make it work by creating a wrapper that is AutoClosable and will for example do nothing if the resource is null, then you're basically forced to tell your linter to stop complaining about this.

huangapple
  • 本文由 发表于 2020年10月1日 12:59:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/64149320.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定