Domanda

Sfondo

Stiamo usando un po 'di codice copiato pari pari da "programmazione concorrente su Windows" di Joe Duffy (pagina 149) in produzione per oltre un anno. Il codice (di seguito) viene utilizzato nella nostra applicazione web Asp.Net per sondare se c'è abbastanza spazio di stack. Il nostro sito consente agli utenti allo script fuori le proprie pagine web e la logica di controllo in un semplice linguaggio proprietry scripting - è possibile per un utente di script di qualcosa di brutto e la causa un'eccezione StackOverflow, in modo da utilizzare ad esempio il codice di Duffy all'esecuzione fermata dello script errante prima l'eccezione StackOverflow imprendibile porta giù l'intero IIS AppPool. Ciò sta funzionando molto bene.

Il problema

tutto ad un tratto questo pomeriggio i nostri tronchi pieni di System.OverflowException errori. Abbiamo avuto la stessa eccezione su ogni richiesta al server. Un rapido ripristino IIS curato il problema.

Tipo di eccezione: System.OverflowException

Messaggio di eccezione: operazione aritmetica ha comportato un trabocco.

Analisi dello stack: a System.IntPtr..ctor (valore Int64) a LiquidHtmlFlowManager.StackManagement.CheckForSufficientStack (UInt64 byte) in C: \ SVN \ LiquidHtml \ Tronco \ LiquidHtmlFlowManager \ StackManagement.cs: linea 47

Il codice:

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

NOTA: Linea 47 è questo uno

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

La domanda:

Quale parte del codice trabocca, è il cast del puntatore al uint, il "- 4096" il funzionamento, o il cast al Int64

?

Tutte le idee come rendere questo più robusto?

Qualche informazione in più:

Il sistema operativo è a 64 bit di Windows Server 2008, in esecuzione IIS7 con una CPU Intel Zeon (x86).

Il parametro passato alla funzione CheckForSufficientStack è:

private const Int32 _minimumStackSpaceLimit = 48 * 1024;

EDIT: Grazie per la risposta. Ho aggiornato il codice per rimuovere i calchi e le variabili di dimensioni uso del puntatore in modo che funzioni sia a 32 e 64 bit. Qui si tratta di qualcun altro dovrebbe volerlo:

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);
    }
È stato utile?

Soluzione

Il cast è semplicemente sbagliato. L'indirizzo del stackinfo è un valore a 64 bit. Non si può lanciare quella a un uint senza rischiare OverflowException. Non ha senso nel sottrarre 4096 o, VirtualQuery () troverà l'indirizzo di base in ogni caso. Fix:

 IntPtr currentAddr = new IntPtr(&stackInfo);

Codice di Duffy può funzionare soltanto per codice a 32 bit.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top