2011-03-14 22 views
4

背景溢出算術運算導致不安全的C#

我們已經使用一些代碼從生產達菲喬的「在Windows併發編程」(第149頁)逐字複製了一年多了。下面的代碼用於我們的Asp.Net Web應用程序來探測是否有足夠的堆棧空間。我們的網站允許用戶使用簡單的專有腳本語言編寫自己的網頁和控制邏輯 - 用戶可能會編寫一些令人討厭的代碼並導致一個堆棧溢出異常,所以我們使用Duffy的代碼示例來停止執行錯誤的腳本不可捕獲的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?

任何想法如何使這更強大?

一些詳細信息:

操作系統是64位Windows Server 2008,與英特爾吉恩(x86)的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位值。您不能將其轉換爲uint而不冒着OverflowException的風險。減去4096也沒有意義,VirtualQuery()無論如何都會找到基地址。修復:

IntPtr currentAddr = new IntPtr(&stackInfo); 

達菲的代碼只能用於32位代碼。

+0

非常感謝。當你說「Duffy's」代碼只能用於32位代碼時「 - 我想我需要改變MEMORY_BASIC_INFORMATION結構,這取決於它是32位還是64位平臺,稍後將取消uint轉換。我會花一些時間寫這個,然後可能會問另外一個問題,看看我是否正確 – 2011-03-14 20:38:20

+1

對,這也是錯誤的。VirtualQuery只是失敗。未經測試的代碼從未在第一次嘗試:) – 2011-03-14 20:40:36