Arithmetische operation hat einen überlauf verursacht unsafe C#

Hintergrund

Wir haben mit einigen code kopiert wortwörtlich von Joe Duffy ' s "Concurrent Programming on Windows" (Seite 149) in der Produktion für über ein Jahr. Der code (unten) in unserer Asp.Net web-Anwendung zu testen, wenn es genügend Stapelspeicher. Unsere Website ermöglicht es Benutzern, um ein Skript für Ihre eigenen web-Seiten und Kontroll-Logik in eine einfache proprietry scripting Sprache - es ist möglich, einem Benutzer das Skript etwas böses und verursachen eine stackoverflow-exception, so verwenden wir Duffy ' s code-Beispiel beenden Sie die Ausführung des fehlerhaften Skript vor der uneinholbar StackOverflow-exception nimmt sich die ganze IIS-AppPool. Diese arbeitet wirklich gut.

Das problem

Plötzlich an diesem Nachmittag unsere logs gefüllt mit System.OverflowException-Fehler. Wir haben die gleiche Ausnahme bei jeder Anfrage an diesen server. Eine swift-IIS-reset geheilt das problem.

Ausnahme Geben :
System.OverflowException

Ausnahme Meldung :
Arithmetische operation hat einen überlauf verursacht.

Stack-Trace :
System.IntPtr..ctor(Int64-Wert)
bei LiquidHtmlFlowManager.StackManagement.CheckForSufficientStack(UInt64 bytes) in C:\SVN\LiquidHtml\Trunk\LiquidHtmlFlowManager\StackManagement.cs:line 47

Code:

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

HINWEIS: Zeile 47 ist das ein

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

Die Frage:

Welcher Teil des Codes überläuft, ist es die Besetzung aus dem Zeiger auf uint-die "- 4096" Betrieb, oder die Besetzung der Int64?

Irgendwelche Ideen, wie man diese robuster?

Einige weitere Informationen:

Das OS ist 64-bit Windows Server 2008 läuft IIS7 mit einem Intel Zeon (x86) CPU.

Übergebenen parameter mit dem CheckForSufficientStack Funktion:

private const Int32 _minimumStackSpaceLimit = 48 * 1024;

EDIT: Danke für die Antwort. Ich habe aktualisiert, der code zum entfernen der Gipsverbände, und verwenden Sie pointer sized Variablen, so dass es funktioniert in 32 und 64 bit. Hier ist es sollte jemand anderes es wollen:

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);
    }
Schreibe einen Kommentar