Фон
Мы использовали некоторый код, скопированный дословно из Joe Duffy "Параллельное программирование в Windows" (страница 149) в производстве более года. Код (ниже) используется в нашем веб-приложении Asp.Net для проверки наличия достаточного пространства стека. Наш сайт позволяет пользователям script выводить свои собственные веб-страницы и управлять логикой на простом языке скриптов, что позволяет пользователю script что-то неприятное и вызвать исключение stackoverflow, поэтому мы используем пример кода Duffy, чтобы остановить выполнение errant script, прежде чем исключение StackOverflow исключает весь IIS AppPool. Это работает очень хорошо.
Проблема
Сегодня днем наши журналы заполнены ошибками System.OverflowException. Мы получили то же исключение при каждом запросе на этот сервер. Быстрый IIS reset устранял проблему.
Тип исключения: System.OverflowException
Сообщение об исключении: Арифметическая операция привела к переполнению.
Трассировка стека: в System.IntPtr..ctor(значение Int64) в LiquidHtmlFlowManager.StackManagement.CheckForSequStack(UInt64 bytes) в C:\SVN\LiquidHtml\Trunk\LiquidHtmlFlowManager\StackManagement.cs: строка 47
Код:
public static class StackManagement
{
[StructLayout(LayoutKind.Sequential)]
struct MEMORY_BASIC_INFORMATION
{
public uint BaseAddress;
public uint AllocationBase;
public uint AllocationProtect;
public uint RegionSize;
public uint State;
public uint Protect;
public uint Type;
};
//We are conservative here. We assume that the platform needs a
//whole 16 pages to respond to Qaru (using an X86/X64
//page-size, not IA64). That 64KB, which means that for very
//small stacks (e.g. 128kb) we'll fail a lot of stack checks (say in asp.net)
//incorrectly.
private const long STACK_RESERVED_SPACE = 4096 * 16;
/// <summary>
/// Checks to see if there is at least "bytes" bytes free on the stack.
/// </summary>
/// <param name="bytes">Number of Free bytes in stack we need.</param>
/// <returns>If true then there is suffient space.</returns>
public unsafe static bool CheckForSufficientStack(ulong bytes)
{
MEMORY_BASIC_INFORMATION stackInfo = new MEMORY_BASIC_INFORMATION();
//We subtract one page for our request. VirtualQuery rounds up
//to the next page. But the stack grows down. If we're on the
//first page (last page in the VirtualAlloc), we'll be moved to
//the next page which is off the stack! Note this doesn't work
//right for IA64 due to bigger pages.
IntPtr currentAddr = new IntPtr((uint)&stackInfo - 4096);
//Query for the current stack allocation information.
VirtualQuery(currentAddr, ref stackInfo, sizeof(MEMORY_BASIC_INFORMATION));
//If the current address minus the base (remember: the stack
//grows downward in the address space) is greater than the
//number of bytes requested plus the unreserved space at the end,
//the request has succeeded.
System.Diagnostics.Debug.WriteLine(String.Format("CurrentAddr = {0}, stackInfo.AllocationBase = {1}. Space left = {2} bytes.", (uint)currentAddr.ToInt64(),
stackInfo.AllocationBase,
((uint)currentAddr.ToInt64() - stackInfo.AllocationBase)));
return ((uint)currentAddr.ToInt64() - stackInfo.AllocationBase) > (bytes + STACK_RESERVED_SPACE);
}
[DllImport("kernel32.dll")]
private static extern int VirtualQuery(IntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
}
ПРИМЕЧАНИЕ. Строка 47 - это
IntPtr currentAddr = new IntPtr((uint)&stackInfo - 4096);
Вопрос:
Какая часть переполнения кода, это листинг из указателя на uint, операцию "-4096" или приведение в Int64?
Любые идеи, как сделать это более надежным?
Дополнительная информация:
ОС - это 64-разрядная версия Windows Server 2008, работающая на IIS7 с процессором Intel Zeon (x86).
Параметр, передаваемый функции CheckForSequStack:
private const Int32 _minimumStackSpaceLimit = 48 * 1024;
EDIT: Спасибо за ответ. Я обновил код, чтобы удалить ролики и использовать переменные размера указателя, чтобы он работал как на 32, так и на 64 бит. Здесь нужно, чтобы кто-то еще этого захотел:
public static class StackManagement
{
[StructLayout(LayoutKind.Sequential)]
struct MEMORY_BASIC_INFORMATION
{
public UIntPtr BaseAddress;
public UIntPtr AllocationBase;
public uint AllocationProtect;
public UIntPtr RegionSize;
public uint State;
public uint Protect;
public uint Type;
};
private const long STACK_RESERVED_SPACE = 4096 * 16;
public unsafe static bool CheckForSufficientStack(UInt64 bytes)
{
MEMORY_BASIC_INFORMATION stackInfo = new MEMORY_BASIC_INFORMATION();
UIntPtr currentAddr = new UIntPtr(&stackInfo);
VirtualQuery(currentAddr, ref stackInfo, sizeof(MEMORY_BASIC_INFORMATION));
UInt64 stackBytesLeft = currentAddr.ToUInt64() - stackInfo.AllocationBase.ToUInt64();
System.Diagnostics.Debug.WriteLine(String.Format("CurrentAddr = {0}, stackInfo.AllocationBase = {1}. Space left = {2} bytes.",
currentAddr,
stackInfo.AllocationBase,
stackBytesLeft));
return stackBytesLeft > (bytes + STACK_RESERVED_SPACE);
}
[DllImport("kernel32.dll")]
private static extern int VirtualQuery(UIntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
}