Они зафиксировали его в 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. Обратите внимание, что этот код не освобождает память, которую он выделяет, но это не большая проблема, потому что когда процесс заканчивается, память восстанавливается ОС.
Что делает эта программа? Он выделяет 8192 байта (2 страницы), используя VirtualAlloc
. Используя VirtualAlloc
, две страницы выравниваются по страницам. Он отключает доступ ко второй странице (с VirtualProtect
). Затем он заполняет первую страницу 'A'
. Затем он пытается создать string
с первой страницы. В .NET 2.0 конструктор string
пытается прочитать первый байт второй страницы (даже если вы сказали, что строка длиной всего лишь 4096 байт).
В центре есть несколько тестов, которые проверяют, могут ли страницы быть прочитаны/записаны.
Обычно трудно проверить это условие, потому что трудно иметь блок памяти, который находится точно в конце выделенного читаемого пространства памяти.