Using unsafe code blocks bypasses the .NET runtime’s safety guarantees and can introduce memory corruption vulnerabilities that are
invisible to the managed runtime.
Why is this an issue?
unsafe code blocks allow developers to use pointers, fixed buffers, function calls through pointers, and manual memory management. The
contents of such blocks are not verified by the Common Language Runtime, so it is up to the programmer to ensure no bugs are introduced.
What is the potential impact?
Errors in manual memory management or pointer arithmetic inside unsafe blocks can introduce memory corruption vulnerabilities such as
buffer overflows and out-of-bounds writes. These vulnerabilities can be exploited by attackers to execute arbitrary code or crash the application.
How to fix it
If unsafe is used solely to improve performance, the Span and Memory APIs provide a similar benefit without leaving the safe
runtime context.
Sometimes the unsafe block cannot be removed, for example when you need raw pointers for native interop, platform-specific code, or an
API that is only available in an unsafe context. In that case, keep the block as short as possible: less code means a smaller surface for
memory-safety mistakes. Within the unsafe code block, make sure that:
- All type casts are correct.
- Memory is correctly allocated and then released.
- Array accesses can never go out of bounds.
Code examples
Noncompliant code example
public unsafe int SubarraySum(int[] array, int start, int end) // Noncompliant
{
var sum = 0;
// Skip array bound checks for extra performance
fixed (int* firstNumber = array)
{
for (int i = start; i < end; i++)
sum += *(firstNumber + i);
}
return sum;
}
Compliant solution
public int SubarraySum(int[] array, int start, int end)
{
var sum = 0;
Span<int> span = array.AsSpan();
for (int i = start; i < end; i++)
sum += span[i];
return sum;
}
Resources
Documentation
- Microsoft Learn - Unsafe code, pointer types, and function pointers