Question

Arrière-plan

Nous avons utilisé un code copié mot pour mot de « Programmation concurrente sur Windows » Joe Duffy (page 149) dans la production depuis plus d'un an. Le code (ci-dessous) est utilisé dans notre application Web Asp.Net pour sonder s'il y a assez d'espace de pile. Notre site permet aux utilisateurs de scripter leurs propres pages Web et la logique de contrôle dans un langage de script simple proprietry - il est possible pour un utilisateur de scripter quelque chose de méchant et la cause d'une exception StackOverflow, donc nous utilisons l'exemple de code de Duffy à l'exécution d'arrêt du script errante avant l'exception uncatchable StackOverflow prend sur la toute IIS AppPool. Cela travaille vraiment bien.

Le problème

Tout d'un coup cet après-midi nos journaux remplis d'erreurs System.OverflowException. Nous avons eu la même exception à chaque requête à ce serveur. Un rapide IIS reset guéri le problème.

Type d'exception: System.OverflowException

Message d'exception: opération arithmétique a donné lieu à un trop-plein.

Trace de la pile: à System.IntPtr..ctor (valeur Int64) à LiquidHtmlFlowManager.StackManagement.CheckForSufficientStack (UInt64 octets) dans C: \ SVN \ LiquidHtml \ Trunk \ LiquidHtmlFlowManager \ StackManagement.cs: ligne 47

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

NOTE: La ligne 47 est celui-ci

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

La question:

Quelle partie du code déborde, est-il la distribution du pointeur vers le uint, la - opération « 4096 », ou la distribution du Int64

?

Toutes les idées comment rendre plus robuste?

Quelques informations supplémentaires:

Le système d'exploitation est de 64 bits de Windows Server 2008, en cours d'exécution IIS7 avec un processeur Intel Zeon (x86).

Le paramètre transmis à la fonction CheckForSufficientStack est:

private const Int32 _minimumStackSpaceLimit = 48 * 1024;

EDIT: Merci pour la réponse. Je l'ai mis à jour le code pour supprimer les moulages et les variables de taille pointeur de l'utilisation de telle sorte que cela fonctionne dans les deux 32 et 64 bits. Ici, il est quelqu'un d'autre devrait le vouloir:

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);
    }
Était-ce utile?

La solution

Le casting est tout simplement faux. L'adresse de StackInfo est une valeur de 64 bits. Vous ne pouvez pas lancer ce à un uint sans risquer OverflowException. Il ne sert à soustraire 4096 soit, VirtualQuery () trouveront l'adresse de base de toute façon. Correction:

 IntPtr currentAddr = new IntPtr(&stackInfo);

Code de Duffy ne peut fonctionner pour le code 32 bits.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top