Pregunta

Fondo

Hemos estado utilizando algún código copiado literalmente de la "Programación concurrente de Joe Duffy en Windows" (página 149) en producción durante más de un año. El código (a continuación) se utiliza en nuestra aplicación web ASP.NET para sondear si hay suficiente espacio de pila. Nuestro sitio permite a los usuarios escribir sus propias páginas web y controlar la lógica en un lenguaje de secuencia de comandos de propiedad simple: es posible que un usuario escriba algo desagradable y cause una excepción de StackOverFlow, por lo que usamos el ejemplo de código de Duffy para detener la ejecución del script errante antes La excepción de StackOverflow de Uncatchable elimina todo el IIS Apppool. Esto ha funcionado muy bien.

El problema

De repente, esta tarde, nuestros registros se llenan de System.OverflowException Errores. Obtuvimos la misma excepción en cada solicitud a ese servidor. Un rápido reinicio IIS curó el problema.

Tipo de excepción: System.OverflowException

Mensaje de excepción: la operación aritmética dio como resultado un desbordamiento.

Trace de pila: en System.intptr..Ctor (valor int64) en Liquidhtmlflowmanager.stackmanagement.CheckForSuffImentStack (uint64 bytes) en c: svn liquidhtml trunk liquidmlflowmanager line 47

El código:

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: la línea 47 es esta

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

La pregunta:

¿Qué parte del código se desborda, es el elenco del puntero al UINT, la operación "- 4096" o el elenco al Int64?

¿Alguna idea de cómo hacer esto más robusto?

Más información:

El sistema operativo es de 64 bits Windows Server 2008, que ejecuta IIS7 con una CPU Intel Zeon (x86).

El parámetro pasado a la función checkForSuftIntack es:

private const Int32 _minimumStackSpaceLimit = 48 * 1024;

Editar: Gracias por la respuesta. He actualizado el código para eliminar los moldes y usar variables del tamaño de un puntero para que funcione en 32 y 64 bits. Aquí está si alguien más lo quiere:

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);
    }
¿Fue útil?

Solución

El elenco está mal. La dirección de StackInfo es un valor de 64 bits. No puedes lanzar eso a un UINT sin arriesgar OverflowException. Tampoco tiene sentido restar 4096, VirtualQuery () encontrará la dirección base de todos modos. Arreglar:

 IntPtr currentAddr = new IntPtr(&stackInfo);

El código de Duffy solo puede funcionar para un código de 32 bits.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top