Double Free in Aspnet (Csharp)
Double Free in Aspnet with Csharp — how this specific combination creates or exposes the vulnerability
In ASP.NET applications written in C#, a double free vulnerability typically arises when unmanaged resources are managed incorrectly through interop scenarios, such as using Marshal.AllocHGlobal or GCHandle.Alloc with GCHandleType.Pinned, followed by multiple calls to Marshal.FreeHGlobal or GCHandle.Free on the same pointer or handle. While managed code in C# is generally protected from memory corruption by the garbage collector, unsafe code blocks or native interop can expose the application to double free conditions, especially when handling buffers passed to or from native APIs via P/Invoke.
For example, consider an ASP.NET Core middleware that processes image uploads by delegating to a native image processing library through P/Invoke. If the native function allocates memory and returns a pointer, and the C# code incorrectly assumes ownership and frees it more than once—perhaps due to exception handling paths or retry logic—a double free can occur. This may lead to heap corruption, which an attacker could exploit to achieve arbitrary code execution or denial of service, particularly if the application runs with elevated privileges.
Although ASP.NET itself does not directly manage raw memory, the combination of C#'s unsafe contexts, manual memory management via System.Runtime.InteropServices, and the high-throughput, request-driven nature of web applications increases the risk when developers mishandle resource lifetimes. middleBrick can detect such risky patterns indirectly by identifying exposed endpoints that process untrusted input through native interfaces, flagging potential input validation or improper handling issues that could lead to memory corruption vulnerabilities like double free.
Csharp-Specific Remediation in Aspnet — concrete code fixes
To prevent double free vulnerabilities in ASP.NET applications using C#, developers must ensure that each allocation has exactly one corresponding free, especially when working with unmanaged memory. The System.Runtime.InteropServices.SafeHandle class (or its derivatives like SafeFileHandle) should be used to encapsulate unmanaged resources, as it guarantees deterministic cleanup via critical finalization and prevents double release.
Below is a corrected example of handling unmanaged memory in an ASP.NET Core service using SafeHandle:
using System;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Mvc;
public class ImageProcessorService : IDisposable
{
private class BufferHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public BufferHandle() : base(true) { }
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
}
private BufferHandle _buffer;
public ImageProcessorService(int size)
{
_buffer = new BufferHandle();
_buffer.SetHandle(Marshal.AllocHGlobal(size));
}
public IntPtr DangerousGetHandle() => _buffer.DangerousGetHandle();
public void Dispose()
{
_buffer?.Dispose();
}
}
// Usage in ASP.NET Core controller
[ApiController]
[Route("api/[controller]")]
public class ImageController : ControllerBase
{
[HttpPost("process")]
public IActionResult Process([FromForm] IFormFile file)
{
using var processor = new ImageProcessorService(file.Length * 4); // Allocate buffer
try
{
// Pass pointer to native library via P/Invoke
// NativeProcessImage(processor.DangerousGetHandle(), file.Length);
// No manual free needed — SafeHandle ensures single release
return Ok("Image processed");
}
catch (Exception ex)
{
// Log exception; buffer still disposed safely via using
return StatusCode(500, "Processing failed");
}
}
}
This approach eliminates the risk of double free by ensuring the unmanaged buffer is freed exactly once, even if exceptions occur. Developers should avoid manual Marshal.FreeHGlobal calls and instead rely on SafeHandle or CriticalFinalizerObject for reliable resource management. In ASP.NET Core, wrapping such resources in using statements or dependency injection scopes further ensures timely disposal.
Frequently Asked Questions
Can double free vulnerabilities occur in fully managed ASP.NET C# code without unsafe blocks or P/Invoke?
Marshal.AllocHGlobal, or similar interop mechanisms where manual memory management is required.