他们在 4.0 中修复了它,在 2.0 中仍然损坏:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication13
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
// For .NET 4.0
//[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
static unsafe void Main(string[] args)
{
IntPtr ptr = VirtualAlloc(
IntPtr.Zero,
(IntPtr)(4096 * 2),
0x1000 /* MEM_COMMIT */ | 0x2000 /* MEM_RESERVE */,
0x04 /* PAGE_READWRITE */);
IntPtr page1 = ptr;
IntPtr page2 = (IntPtr)((long)ptr + 4096);
uint oldAccess;
bool res = VirtualProtect(page2, 4096, 0x01 /* PAGE_NOACCESS */, out oldAccess);
try
{
Marshal.WriteByte(page1, 1);
Console.WriteLine("OK");
}
catch (AccessViolationException)
{
Console.WriteLine("KO");
}
try
{
Marshal.WriteByte(page2, 1);
Console.WriteLine("KO");
}
catch (AccessViolationException)
{
Console.WriteLine("OK");
}
try
{
byte b1 = Marshal.ReadByte(page1);
Console.WriteLine("OK");
}
catch (AccessViolationException)
{
Console.WriteLine("KO");
}
try
{
byte b2 = Marshal.ReadByte(page2);
Console.WriteLine("KO");
}
catch (AccessViolationException)
{
Console.WriteLine("OK");
}
for (int i = 0; i < 4096; i++)
{
Marshal.WriteByte(page1, i, (byte)'A');
}
sbyte* ptr2 = (sbyte*)page1;
try
{
var st1 = new string(ptr2, 0, 4096);
Console.WriteLine("OK");
}
catch (ArgumentOutOfRangeException)
{
Console.WriteLine("KO");
}
}
}
}
您必须取消注释 .NET 4.0 中的一行。请注意,这段代码不会释放它分配的内存,但这不是什么大问题,因为当进程结束时,内存会被操作系统回收。
这个程序是做什么的?它使用VirtualAlloc 分配8192 字节(2 页)。通过使用VirtualAlloc,这两个页面是页面对齐的。它禁止访问第二页(使用VirtualProtect)。然后它用'A' 填充第一页。然后它尝试从第一页创建一个string。在 .NET 2.0 上,string 构造函数尝试读取第二页的第一个字节(即使您告诉它该字符串只有 4096 个字节)。
中间有一些测试检查页面是否可以读/写。
通常很难检查这种情况,因为很难有一块内存恰好位于分配的可读内存空间的末尾。