2011-03-14 62 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