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

23
elmah.io logo 33

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 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.

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 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];

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.

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 trial
Free 21-day trial No credit card required

Related exceptions

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.

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.

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.

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:

AI chat
Find the most frequent AccessViolationException in my production log and show me the line that throws it.

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:

  1. Recompile as a .NET 3.5 assembly and run it in .NET 4.0.

  2. Add a line to your application's config file under the configuration/runtime element: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. Decorate the methods you want to catch these exceptions in with the HandleProcessCorruptedStateExceptions attribute. 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.