배경에 오버 플로우 결과산술 연산이 안전하지 않은 C#을
위브 년 이상 생산 조 더피의 "Windows에서 동시 프로그래밍"(149 페이지)에서 그대로 복사 한 몇 가지 코드를 사용하고. 코드 (아래)는 Asp.Net 웹 응용 프로그램에서 스택 공간이 충분한 지 확인하는 데 사용됩니다. 우리 사이트에서는 사용자가 자신의 웹 페이지를 스크립팅하고 간단한 소유권 스크립팅 언어로 로직을 제어 할 수 있습니다. 사용자가 더러운 스크립트를 작성하여 stackoverflow 예외가 발생할 수 있으므로 Duffy의 코드 예제를 사용하여 오류가있는 스크립트의 실행을 중지합니다 uncatchable StackOverflow 예외는 전체 IIS AppPool을 제거합니다. 이것은 정말 잘 작동하고 있습니다.
문제
System.OverflowException이 오류로 채워진 우리의 로그를 오늘 오후에 갑자기
. 해당 서버에 대한 모든 요청에 대해 동일한 예외가 발생했습니다. 신속한 IIS 재설정으로 문제가 해결되었습니다.예외 유형 : System.OverflowException이
예외 메시지 : 산술 연산 오버플로가 발생했습니다.
스택 추적 : System.IntPtr..ctor (Int64 값)에 LiquidHtmlFlowManager.StackManagement.CheckForSufficientStack (UINT64 바이트)에서의 C : \ SVN \ LiquidHtml 트렁크 \ \ 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 stack overflow (using an X86/X64
//page-size, not IA64). That's 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 로의 형 변환입니까?
아이디어를 더 강력하게 만드는 방법은 무엇입니까?
일부 자세한 내용 :
OS가 64 비트 Windows Server 2008이며, 인텔 제온 (86) CPU와 함께 IIS7을 실행.
CheckForSufficientStack 함수에 전달 된 매개 변수는 다음과 같습니다
private const Int32 _minimumStackSpaceLimit = 48 * 1024;
편집 : 답변 감사합니다. 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);
}
대단히 감사합니다."Duffy의"코드가 32 비트 코드에서만 작동한다고 말하면 "32 비트 또는 64 비트 플랫폼인지 여부에 따라 MEMORY_BASIC_INFORMATION 구조를 변경해야하고 추후에 UIT 캐스트를 꺼내야합니다. 이 글을 쓰고 나서 다른 질문을 통해 내가 맞는지 알아보기 위해 –
VirtualQuery가 실패합니다. 테스트되지 않은 코드는 첫 번째 시도에서 결코 작동하지 않습니다 :) –