System.AccessViolationException
An AccessViolationException is thrown when code reads or writes memory that it is not allowed to use. The message is Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
It does not come from ordinary C# code. Managed code cannot touch memory by accident, so it means that unsafe code, a call to a native library, or a bug in the runtime or in a native dependency is involved. In many cases the process crashes instead of throwing an exception that you can catch.
Minimum version: >= 2.0 >= Core 2.0
Statistics
Common causes
Memory that is not managed is where it starts. These are the usual cases.
Reading or writing through a bad pointer
Unsafe code and the Marshal methods work on addresses. If the address is zero, freed or outside what was allocated, the operating system stops the access.
var value = Marshal.ReadInt32(new IntPtr(8)); // AccessViolationException
Only use addresses that you got from an allocation that is still alive, and check what you got back from native code:
var pointer = Marshal.AllocHGlobal(4);
try
{
Marshal.WriteInt32(pointer, 42);
var value = Marshal.ReadInt32(pointer);
}
finally
{
Marshal.FreeHGlobal(pointer);
}
A P/Invoke declaration that does not match the native function
A wrong parameter type, a wrong calling convention, a structure with another layout or an 32-bit integer where the function expects a pointer make the native code use wrong memory. The failure can come on the next call, not on the one that was wrong.
Compare the declaration with the header of the function. Use IntPtr or nint for pointers and handles, set StructLayout on structures, and generate the declaration with LibraryImport or the CsWin32 source generator.
Memory that was freed too early
A native library keeps a pointer to a buffer or a delegate that the garbage collector moved or collected. A callback that is passed to native code must be kept alive for as long as the library can call it.
Pin buffers for the time native code uses them, and keep the delegate in a field. Prefer SafeHandle over raw IntPtr for handles that must be released.
A bug in a native library or a mismatch of platforms
A 32-bit library loaded by a 64-bit process fails to load, but a library for a different version can load and crash on the first use. Drivers, imaging libraries and database clients are typical.
Check that the version and the platform of the native files match the application. Update the library, and test with a minimal call to see which function fails.
How to fix it and prevent it
Prefer safe managed code
Span<T>, stackalloc in a Span and the System.Buffers types give you speed without raw pointers, and the runtime checks the bounds.
Span<int> numbers = stackalloc int[4];
numbers[0] = 42;
var first = numbers[0];
Generate and review native interop
Use the LibraryImport attribute, which generates the marshalling code at build time and validates the signature better than DllImport. Review every parameter against the native header.
Isolate native code
If a native library is unstable, run it in a separate process and talk to it over a pipe or HTTP. A crash then takes down the helper, and not the application.
How to read the stack trace
The trace shows the managed code that was running when the access failed:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at System.Runtime.InteropServices.Marshal.ReadInt32(IntPtr ptr, Int32 ofs)
at Shop.Services.Native.Read() in C:\src\Shop\Services\Native.cs:line 8
at Program.<Main>$(String[] args) in C:\src\Shop\Program.cs:line 4
The top frame is Marshal.ReadInt32, called from line 8 of Native.cs. The address passed was 8, which is inside the first page of memory, which is never mapped. If the top frames are inside a native library, the failure may be several calls earlier, so look at the calls that came before it, and at the arguments you passed to the native code.
Should you catch it?
Not in a useful way. After a real access violation the memory of the process can be damaged, and the runtime does not let you continue, apart from the cases where managed code raises it, such as Marshal. [HandleProcessCorruptedStateExceptions] does not work in .NET Core and later.
Log it, and let the process restart. Spend the time on finding the unsafe or native code behind it.
Find AccessViolationException before your users do
elmah.io logs every unhandled exception in your .NET application with its stack trace and request details, groups identical errors and notifies you when a new one appears.
Start free trialRelated exceptions
- NullReferenceException is the managed version of using an object that is not there.
- StackOverflowException is another failure that ends the process.
- TypeLoadException is thrown when a type cannot be loaded, for example because of mismatching versions.
Frequently asked questions
Why do I get AccessViolationException in safe C# code?
The safe code calls something that is not safe, such as a native library, a COM object or a driver. The access violation happens in that code, and shows up in the managed code that called it.
Can I catch AccessViolationException?
Only in a few cases where the managed runtime throws it. After a real access violation, the process crashes. Fix the cause instead of catching it.
How do I find the cause?
Look at the native libraries and the P/Invoke calls near the failure. Reproduce with the smallest possible code, and use a crash dump with WinDbg or Visual Studio, with native debugging turned on.
What is the difference from NullReferenceException?
A NullReferenceException is the runtime telling you that a managed reference is null. An AccessViolationException is about memory that is not valid, and normally means that unsafe code is involved.
Let your AI agent track it down
Connect Claude Code, Cursor, VS Code or Visual Studio to the elmah.io MCP server and ask your agent to look into AccessViolationException for you. For example:
The agent reads the stack trace and request details from elmah.io, finds the code in your repository and proposes a fix. In Claude Code, add the server with one command:
claude mcp add --transport http --client-id claudecode elmahio https://mcp.elmah.io/mcp
The MCP server is included on every plan and is currently in beta. Set up the MCP server.
Further reading
YouTube videos
Answers from Stack Overflow
EDIT (3/17/2021)
Disclaimer: This answer was written in 2011 and references the original .NET Framework 4.0 implementation, NOT the open-source implementation of .NET.
In .NET 4.0, the runtime handles certain exceptions raised as Windows Structured Error Handling (SEH) errors as indicators of Corrupted State. These Corrupted State Exceptions (CSE) are not allowed to be caught by your standard managed code. I won't get into the why's or how's here. Read this article about CSE's in the .NET 4.0 Framework:
http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035
But there is hope. There are a few ways to get around this:
Recompile as a .NET 3.5 assembly and run it in .NET 4.0.
Add a line to your application's config file under the configuration/runtime element:
<legacyCorruptedStateExceptionsPolicy enabled="true|false"/>Decorate the methods you want to catch these exceptions in with the
HandleProcessCorruptedStateExceptionsattribute. See http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 for details.
EDIT
Previously, I referenced a forum post for additional details. But since Microsoft Connect has been retired, here are the additional details in case you're interested:
From Gaurav Khanna, a developer from the Microsoft CLR Team
This behaviour is by design due to a feature of CLR 4.0 called Corrupted State Exceptions. Simply put, managed code shouldnt make an attempt to catch exceptions that indicate corrupted process state and AV is one of them.
He then goes on to reference the documentation on the HandleProcessCorruptedStateExceptionsAttribute and the above article. Suffice to say, it's definitely worth a read if you're considering catching these types of exceptions.
By villecoder. Read the original answer on Stack Overflow.
You cannot pass a C++ std::string across an interop boundary. You cannot create one of those in your C# code. So your code can never work.
You need to use interop friendly types at the interop boundary. For instance, null-terminated arrays of characters. That works well when you allocate and deallocate the memory in the same module. So, it's simple enough when passing data from C# to C++.
C++
void foo(const char *str)
{
// do something with str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(string str);
....
foo("bar");
In the other direction you would typically expect the caller to allocate the buffer, into which the callee can write:
C++
void foo(char *str, int len)
{
// write no more than len characters into str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(StringBuilder str, int len);
....
StringBuilder sb = new StringBuilder(10);
foo(sb, sb.Capacity);
By David Heffernan. Read the original answer on Stack Overflow.
I had the same problem after upgrading from .NET 4.5 to .NET 4.5.1. What fixed it for me was running this command:
netsh winsock reset
By Cocowalla. Read the original answer on Stack Overflow.
I'm fairly certain you're being blocked by DEP. The x_CPUIDy_INSNS byte arrays are in a segment of memory marked as data and non-executable.
EDIT:
That being said, I've gotten a version that compiles and runs, but I don't think gets the right values. Perhaps this will get you along your way.
EDIT 2:
I think I have the right values coming back now. Feel free to validate.
namespace CPUID
{
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
internal static class Program
{
[Flags]
private enum AllocationTypes : uint
{
Commit = 0x1000,
Reserve = 0x2000,
Reset = 0x80000,
LargePages = 0x20000000,
Physical = 0x400000,
TopDown = 0x100000,
WriteWatch = 0x200000
}
[Flags]
private enum MemoryProtections : uint
{
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
NoAccess = 0x01,
ReadOnly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
GuartModifierflag = 0x100,
NoCacheModifierflag = 0x200,
WriteCombineModifierflag = 0x400
}
[Flags]
private enum FreeTypes : uint
{
Decommit = 0x4000,
Release = 0x8000
}
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
private unsafe delegate void CPUID0Delegate(byte* buffer);
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
private unsafe delegate void CPUID1Delegate(byte* buffer);
private static void Main()
{
Console.WriteLine("CPUID0: {0}", string.Join(", ", CPUID0().Select(x => x.ToString("X2", CultureInfo.InvariantCulture))));
Console.WriteLine("CPUID0: {0}", new string(ASCIIEncoding.ASCII.GetChars(CPUID0())));
Console.WriteLine("CPUID1: {0}", string.Join(", ", CPUID1().Select(x => x.ToString("X2", CultureInfo.InvariantCulture))));
Console.ReadLine();
}
private static unsafe byte[] CPUID0()
{
byte[] buffer = new byte[12];
if (IntPtr.Size == 4)
{
IntPtr p = NativeMethods.VirtualAlloc(
IntPtr.Zero,
new UIntPtr((uint)x86_CPUID0_INSNS.Length),
AllocationTypes.Commit | AllocationTypes.Reserve,
MemoryProtections.ExecuteReadWrite);
try
{
Marshal.Copy(x86_CPUID0_INSNS, 0, p, x86_CPUID0_INSNS.Length);
CPUID0Delegate del = (CPUID0Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID0Delegate));
fixed (byte* newBuffer = &buffer[0])
{
del(newBuffer);
}
}
finally
{
NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
}
}
else if (IntPtr.Size == 8)
{
IntPtr p = NativeMethods.VirtualAlloc(
IntPtr.Zero,
new UIntPtr((uint)x64_CPUID0_INSNS.Length),
AllocationTypes.Commit | AllocationTypes.Reserve,
MemoryProtections.ExecuteReadWrite);
try
{
Marshal.Copy(x64_CPUID0_INSNS, 0, p, x64_CPUID0_INSNS.Length);
CPUID0Delegate del = (CPUID0Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID0Delegate));
fixed (byte* newBuffer = &buffer[0])
{
del(newBuffer);
}
}
finally
{
NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
}
}
return buffer;
}
private static unsafe byte[] CPUID1()
{
byte[] buffer = new byte[12];
if (IntPtr.Size == 4)
{
IntPtr p = NativeMethods.VirtualAlloc(
IntPtr.Zero,
new UIntPtr((uint)x86_CPUID1_INSNS.Length),
AllocationTypes.Commit | AllocationTypes.Reserve,
MemoryProtections.ExecuteReadWrite);
try
{
Marshal.Copy(x86_CPUID1_INSNS, 0, p, x86_CPUID1_INSNS.Length);
CPUID1Delegate del = (CPUID1Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID1Delegate));
fixed (byte* newBuffer = &buffer[0])
{
del(newBuffer);
}
}
finally
{
NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
}
}
else if (IntPtr.Size == 8)
{
IntPtr p = NativeMethods.VirtualAlloc(
IntPtr.Zero,
new UIntPtr((uint)x64_CPUID1_INSNS.Length),
AllocationTypes.Commit | AllocationTypes.Reserve,
MemoryProtections.ExecuteReadWrite);
try
{
Marshal.Copy(x64_CPUID1_INSNS, 0, p, x64_CPUID1_INSNS.Length);
CPUID1Delegate del = (CPUID1Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID1Delegate));
fixed (byte* newBuffer = &buffer[0])
{
del(newBuffer);
}
}
finally
{
NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
}
}
return buffer;
}
private static class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
UIntPtr dwSize,
AllocationTypes flAllocationType,
MemoryProtections flProtect);
[DllImport("kernel32")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool VirtualFree(
IntPtr lpAddress,
uint dwSize,
FreeTypes flFreeType);
}
#region ASM
private static readonly byte[] x86_CPUID0_INSNS = new byte[]
{
0x53, // push %ebx
0x31, 0xc0, // xor %eax,%eax
0x0f, 0xa2, // cpuid
0x8b, 0x44, 0x24, 0x08, // mov 0x8(%esp),%eax
0x89, 0x18, // mov %ebx,0x0(%eax)
0x89, 0x50, 0x04, // mov %edx,0x4(%eax)
0x89, 0x48, 0x08, // mov %ecx,0x8(%eax)
0x5b, // pop %ebx
0xc3 // ret
};
private static readonly byte[] x86_CPUID1_INSNS = new byte[]
{
0x53, // push %ebx
0x31, 0xc0, // xor %eax,%eax
0x40, // inc %eax
0x0f, 0xa2, // cpuid
0x5b, // pop %ebx
0xc3 // ret
};
private static readonly byte[] x64_CPUID0_INSNS = new byte[]
{
0x49, 0x89, 0xd8, // mov %rbx,%r8
0x49, 0x89, 0xc9, // mov %rcx,%r9
0x48, 0x31, 0xc0, // xor %rax,%rax
0x0f, 0xa2, // cpuid
0x4c, 0x89, 0xc8, // mov %r9,%rax
0x89, 0x18, // mov %ebx,0x0(%rax)
0x89, 0x50, 0x04, // mov %edx,0x4(%rax)
0x89, 0x48, 0x08, // mov %ecx,0x8(%rax)
0x4c, 0x89, 0xc3, // mov %r8,%rbx
0xc3 // retq
};
private static readonly byte[] x64_CPUID1_INSNS = new byte[]
{
0x53, // push %rbx
0x48, 0x31, 0xc0, // xor %rax,%rax
0x48, 0xff, 0xc0, // inc %rax
0x0f, 0xa2, // cpuid
0x5b, // pop %rbx
0xc3 // retq
};
#endregion
}
}
By Jesse C. Slicer. Read the original answer on Stack Overflow.
I decided to improve your answer. It does not need unsafe to compile anymore, and it only needs two assembly blocks in order to be able to read out any and all cpuid blocks, because it just writes eax/ebx/ecx/edx to a 16-byte byte array.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
namespace CpuID
{
public class CpuID : IDisposable
{
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public delegate void CpuIDDelegate(int level, byte[] buffer);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
UIntPtr dwSize,
AllocationType flAllocationType,
MemoryProtection flProtect
);
[DllImport("kernel32")]
private static extern bool VirtualFree(
IntPtr lpAddress,
UInt32 dwSize,
UInt32 dwFreeType
);
[Flags()]
public enum AllocationType : uint
{
COMMIT = 0x1000,
RESERVE = 0x2000,
RESET = 0x80000,
LARGE_PAGES = 0x20000000,
PHYSICAL = 0x400000,
TOP_DOWN = 0x100000,
WRITE_WATCH = 0x200000
}
[Flags()]
public enum MemoryProtection : uint
{
EXECUTE = 0x10,
EXECUTE_READ = 0x20,
EXECUTE_READWRITE = 0x40,
EXECUTE_WRITECOPY = 0x80,
NOACCESS = 0x01,
READONLY = 0x02,
READWRITE = 0x04,
WRITECOPY = 0x08,
GUARD_Modifierflag = 0x100,
NOCACHE_Modifierflag = 0x200,
WRITECOMBINE_Modifierflag = 0x400
}
private CpuIDDelegate cpuIdDelg;
private IntPtr codePointer;
// void x86CpuId(int level, byte* buffer)
// {
// eax = level
// cpuid
// buffer[0] = eax
// buffer[4] = ebx
// buffer[8] = ecx
// buffer[12] = edx
// }
private byte[] x86CodeBytes =
{
0x55, // push ebp
0x8B, 0xEC, // mov ebp,esp
0x53, // push ebx
0x57, // push edi
0x8B, 0x45, 0x08, // mov eax, dword ptr [ebp+8] (move level into eax)
0x0F, 0xA2, // cpuid
0x8B, 0x7D, 0x0C, // mov edi, dword ptr [ebp+12] (move address of buffer into edi)
0x89, 0x07, // mov dword ptr [edi+0], eax (write eax, ... to buffer)
0x89, 0x5F, 0x04, // mov dword ptr [edi+4], ebx
0x89, 0x4F, 0x08, // mov dword ptr [edi+8], ecx
0x89, 0x57, 0x0C, // mov dword ptr [edi+12],edx
0x5F, // pop edi
0x5B, // pop ebx
0x8B, 0xE5, // mov esp,ebp
0x5D, // pop ebp
0xc3 // ret
};
private byte[] x64CodeBytes =
{
0x53, // push rbx this gets clobbered by cpuid
// rcx is level
// rdx is buffer.
// Need to save buffer elsewhere, cpuid overwrites rdx
// Put buffer in r8, use r8 to reference buffer later.
// Save rdx (buffer addy) to r8
0x49, 0x89, 0xd0, // mov r8, rdx
// Move ecx (level) to eax to call cpuid, call cpuid
0x89, 0xc8, // mov eax, ecx
0x0F, 0xA2, // cpuid
// Write eax et al to buffer
0x41, 0x89, 0x40, 0x00, // mov dword ptr [r8+0], eax
0x41, 0x89, 0x58, 0x04, // mov dword ptr [r8+4], ebx
0x41, 0x89, 0x48, 0x08, // mov dword ptr [r8+8], ecx
0x41, 0x89, 0x50, 0x0c, // mov dword ptr [r8+12], edx
0x5b, // pop rbx
0xc3 // ret
};
public CpuID()
{
Compile();
}
~CpuID()
{
Dispose(false);
}
private void Compile()
{
byte[] codeBytes;
if (IntPtr.Size == 4)
{
codeBytes = x86CodeBytes;
}
else
{
codeBytes = x64CodeBytes;
}
this.codePointer = VirtualAlloc(
IntPtr.Zero,
new UIntPtr((uint)codeBytes.Length),
AllocationType.COMMIT | AllocationType.RESERVE,
MemoryProtection.EXECUTE_READWRITE
);
Marshal.Copy(codeBytes, 0, this.codePointer, codeBytes.Length);
this.cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(this.codePointer, typeof(CpuIDDelegate));
}
public void Invoke(int level, byte[] buffer)
{
GCHandle handle = default(GCHandle);
if (buffer.Length < 16)
{
throw new ArgumentException("buffer must be at least 16 bytes long");
}
try
{
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
this.cpuIdDelg(level, buffer);
}
finally
{
if (handle != default(GCHandle))
{
handle.Free();
}
}
}
public void Dispose()
{
Dispose(true);
}
public void Dispose(bool disposing)
{
if (this.codePointer != IntPtr.Zero)
{
VirtualFree(this.codePointer, 0, 0x8000);
this.codePointer = IntPtr.Zero;
}
}
}
}
By antiduh. Read the original answer on Stack Overflow.
Source: Stack Overflow