算术运算导致不安全的C#溢出 [英] Arithmetic operation resulted in an overflow in unsafe C#

查看:214
本文介绍了算术运算导致不安全的C#溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

一年多来,我们一直在使用Joe Duffy的"Windows并行编程"(第149页)中逐字复制的一些代码.下面的代码在我们的Asp.Net Web应用程序中使用,以探查是否有足够的堆栈空间.我们的网站允许用户使用简单的专有脚本语言编写自己的网页和控制逻辑-用户可能会编写令人讨厌的内容并导致stackoverflow异常,因此我们使用Duffy的代码示例停止执行错误的脚本,然后再执行无法捕获的StackOverflow异常将导致整个IIS AppPool崩溃.效果一直很好.

We've been using some code copied verbatim from Joe Duffy's "Concurrent Programming on Windows" (page 149) in production for over a year. The code (below) is used in our Asp.Net web application to probe if there's enough stack space. Our site allows users to script out their own web pages and control logic in a simple proprietry scripting language - it's possible for a user to script something nasty and cause a stackoverflow exception, so we use Duffy's code example to stop execution of the errant script before the uncatchable StackOverflow exception takes down the whole IIS AppPool. This has been working really well.

问题

今天下午突然,我们的日志充满了System.OverflowException错误.我们对该服务器的每个请求都有相同的异常.快速的IIS重置可以解决此问题.

All of a sudden this afternoon our logs filled with System.OverflowException errors. We got the same exception on every request to that server. A swift IIS reset cured the problem.

异常类型: System.OverflowException

Exception Type : System.OverflowException

异常消息: 算术运算导致溢出.

Exception Message : Arithmetic operation resulted in an overflow.

堆栈跟踪: 在System.IntPtr..ctor(Int64值) 在LiquidHtmlFlowManager.StackManagement.CheckForSufficientStack(UInt64 bytes)在C:\ SVN \ LiquidHtml \ Trunk \ LiquidHtmlFlowManager \ StackManagement.cs:line 47

Stack Trace : at System.IntPtr..ctor(Int64 value) at LiquidHtmlFlowManager.StackManagement.CheckForSufficientStack(UInt64 bytes) in C:\SVN\LiquidHtml\Trunk\LiquidHtmlFlowManager\StackManagement.cs:line 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行就是这个

NOTE: Line 47 is this one

IntPtr currentAddr = new IntPtr((uint)&stackInfo - 4096);

问题:

代码的哪一部分溢出,是从指针到uint的强制转换,-4096"操作还是到Int64的强制转换?

Which part of the code overflows, is it the cast from the pointer to the uint, the "- 4096" operation, or the cast to the Int64?

有什么想法可以使它更强大吗?

Any ideas how to make this more robust?

更多信息:

该操作系统是64位Windows Server 2008,运行带有Intel Zeon(x86)CPU的IIS7.

The OS is 64 bit Windows Server 2008, running IIS7 with an Intel Zeon (x86) CPU.

传递给CheckForSufficientStack函数的参数是:

The parameter passed to the CheckForSufficientStack function is:

private const Int32 _minimumStackSpaceLimit = 48 * 1024;

感谢您的回答.我已经更新了代码,以删除强制类型转换并使用指针大小的变量,以便它可以同时在32位和64位上工作.这是其他人想要的:

Thanks for the answer. I've updated the code to remove the casts and use pointer sized variables so that it works in both 32 and 64 bit. Here it is should someone else want it:

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);
    }

推荐答案

强制转换是错误的. stackinfo的地址是一个64位值.您不能在不冒OverflowException风险的情况下将其转换为uint.减去4096也没有意义,无论如何VirtualQuery()都会找到基址.修复:

The cast is just wrong. The address of stackinfo is a 64-bit value. You cannot cast that to an uint without risking OverflowException. There's no point in subtracting 4096 either, VirtualQuery() will find the base address anyway. Fix:

 IntPtr currentAddr = new IntPtr(&stackInfo);

达菲的代码只能用于32位代码.

Duffy's code can only work for 32-bit code.

这篇关于算术运算导致不安全的C#溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆