2011-03-14 6 views
4

배경에 오버 플로우 결과산술 연산이 안전하지 않은 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); 
    } 

답변

6

캐스트가 잘못되었습니다. stackinfo의 주소는 64 비트 값입니다. OverflowException을 위험에 빠뜨리지 않고도이를 캐스팅 할 수 없습니다. 4096 중 하나를 뺄 때 아무런 문제가 없습니다. VirtualQuery()는 기본 주소를 찾을 것입니다. 수정 :

IntPtr currentAddr = new IntPtr(&stackInfo); 

더피의 코드는 32 비트 코드에서만 작동합니다.

+0

대단히 감사합니다."Duffy의"코드가 32 비트 코드에서만 작동한다고 말하면 "32 비트 또는 64 비트 플랫폼인지 여부에 따라 MEMORY_BASIC_INFORMATION 구조를 변경해야하고 추후에 UIT 캐스트를 꺼내야합니다. 이 글을 쓰고 나서 다른 질문을 통해 내가 맞는지 알아보기 위해 –

+1

VirtualQuery가 실패합니다. 테스트되지 않은 코드는 첫 번째 시도에서 결코 작동하지 않습니다 :) –

관련 문제