Call GC.KeepAlive when using native resources
TypeName |
CallGCKeepAliveWhenUsingNativeResources |
CheckId |
CA2115 |
Category |
Microsoft.Security |
Breaking Change |
NonBreaking |
Cause
A method declared in a type with a finalizer references a System.IntPtr or System.UIntPtr field, but does not call System.GC.KeepAlive(System.Object).
Rule Description
Garbage collection finalizes an object if there are no more references to it in managed code. Unmanaged references to objects do not prevent garbage collection. This rule detects errors that might occur because an unmanaged resource is being finalized while it is still being used in unmanaged code.
This rule assumes that IntPtr and UIntPtr fields store pointers to unmanaged resources. Because the purpose of a finalizer is to free unmanaged resources, the rule assumes that the finalizer will free the unmanaged resource pointed to by the pointer fields. This rule also assumes that the method is referencing the pointer field to pass the unmanaged resource to unmanaged code.
How to Fix Violations
To fix a violation of this rule, add a call to KeepAlive to the method, passing the current instance (this in C# and C++) as the argument. Position the call after the last line of code where the object must be protected from garbage collection. Immediately after the call to KeepAlive, the object is again considered ready for garbage collection assuming that there are no managed references to it.
When to Exclude Warnings
This rule makes certain assumptions that can lead to false positives. You can safely exclude a warning from this rule if:
The finalizer does not free the contents of the IntPtr or UIntPtr field referenced by the method.
The method does not pass the IntPtr or UIntPtr field to unmanaged code.
Carefully review other messages before excluding them. This rule detects errors that are difficult to reproduce and debug.
Example
In this example, BadMethod
violates the rule. GoodMethod
contains the corrected code.
using System;
namespace SecurityRulesLibrary
{
class IntPtrFieldsAndFinalizeRequireGCKeepAlive
{
private IntPtr unmanagedResource;
IntPtrFieldsAndFinalizeRequireGCKeepAlive()
{
GetUnmanagedResource (unmanagedResource);
}
// The finalizer frees the unmanaged resource.
~IntPtrFieldsAndFinalizeRequireGCKeepAlive()
{
FreeUnmanagedResource (unmanagedResource);
}
// Violates rule:CallGCKeepAliveWhenUsingNativeResources.
void BadMethod()
{
// Call some unmanaged code.
CallUnmanagedCode(unmanagedResource);
}
// Satisfies the rule.
void GoodMethod()
{
// Call some unmanaged code.
CallUnmanagedCode(unmanagedResource);
GC.KeepAlive(this);
}
// Methods that would typically make calls to unmanaged code.
void GetUnmanagedResource(IntPtr p)
{
// Allocate the resource ...
}
void FreeUnmanagedResource(IntPtr p)
{
// Free the resource and set the pointer to null ...
}
void CallUnmanagedCode(IntPtr p)
{
// Use the resource in unmanaged code ...
}
}
}
See Also
Reference
Implementing Finalize and Dispose to Clean Up Unmanaged Resources
System.GC.KeepAlive(System.Object)
System.IntPtr
System.Object.Finalize
System.UIntPtr