Finalizer No Exception
ID |
csharp.finalizer_no_exception |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
finalizer, reliability |
Description
Reports throw statements inside a finalizer (~ClassName()). A finalizer is invoked
by the garbage collector on a dedicated thread, with no enclosing call site to handle the
exception. An unhandled exception escaping a finalizer terminates the process in modern
.NET runtimes.
Rationale
The finalizer is the last opportunity to release a native resource that the GC cannot reclaim on its own. Throwing in the middle of that cleanup leaks the resource permanently and abruptly tears down the process, which can also lose pending work elsewhere in the runtime.
Recovery from a finalizer is impossible: there is no caller to catch the exception, and the object that owned the resource is being destroyed. Failures must be logged and swallowed.
public class NativeBuffer
{
private IntPtr handle;
~NativeBuffer()
{
if (handle == IntPtr.Zero)
throw new InvalidOperationException("not allocated"); // FLAW
Release(handle);
}
private static void Release(IntPtr handle) { }
}
public class NativeBufferOk
{
private IntPtr handle;
~NativeBufferOk() // OK
{
try
{
if (handle != IntPtr.Zero) Release(handle);
}
catch (Exception)
{
}
}
private static void Release(IntPtr handle) { }
}