Обнаружение повреждения кучи ПЕРЕД сборкой мусора

Я использую CDB (Microsoft Console Debugger) и WinDbg, чтобы попытаться принудительно прервать работу при повреждении кучи. происходит с помощью P/Invoke в ReadFile. Я прочитал из текстового файла гораздо больше байтов, чем я выделил массиву chBuf. Отладчик не видит нарушения прав доступа до тех пор, пока не произойдет GC.Collect, что для меня слишком поздно. Перед запуском моей программы я запускаю

gflags -p /enable testheap.exe /unaligned

Эффект кажется бесполезным. Я написал эту маленькую тестовую программу, чтобы применить то, что я нашел, для отладки гораздо более крупной коммерческой программы, которая имеет проблемы с повреждением кучи.

Я также попробовал DebugDiag с Application Verifier и обратный вызов MDAOnCollectedDelegate безуспешно. Разве мое использование gflags не должно обнаруживать повреждение кучи сразу после ReadFile?

Код:

    namespace TestHeap

    public partial class Form1 : Form
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
            uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
            uint dwFlagsAndAttributes, IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadFile(SafeFileHandle hFile, [Out] byte[] lpBuffer,
            uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);

        string fileName = "testHeap.txt";
        const uint GENERIC_READ = 0x80000000;
        const uint OPEN_EXISTING = 3;
        SafeFileHandle sh;
        byte[] chBuf = new byte[8];

        public Form1()
        {
            InitializeComponent();
        }

        private void testBtn_Click(object sender, EventArgs e)
        {
            bool nStat;
            uint bytesToRead = 1025;
            uint bytesRead = 0;

            if (!(nStat = ReadFile( sh, chBuf, bytesToRead, out bytesRead, IntPtr.Zero)))
                Debug.Print("testBtn_Click error in ReadFile, nStat = {0}", nStat);
            MessageBox.Show(string.Format("After ReadFile, bytesToRead = {0},\n bytes read = {1}", bytesToRead, bytesRead));
            GC.Collect();
            MessageBox.Show("testBtn_Click end, after GC.Collect");
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            sh = CreateFile(fileName, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
        }
    }
}

person Tech_Dr    schedule 31.10.2011    source источник
comment
Не уверен, что вы подразумеваете под отладчиком, который не видит нарушения прав доступа до GC.Collect. Именно в этот момент возникает нарушение прав доступа. Как отладчик может обнаружить нарушение прав доступа до того, как оно произойдет? Если вы имеете в виду, что повреждение не обнаружено до GC.Collect, тогда вам просто нужно искать повреждение раньше, например, путем явного вызова HeapValidate.   -  person Raymond Chen    schedule 31.10.2011
comment
Рэймонд, нарушение прав доступа происходит до GC.Collect. Если я сломаю и !verifyheap перед GC, это покажет, что управляемая куча уже повреждена. Установка gflags должна поймать это. См. идентификатор статьи поддержки Microsoft: 286470. Это просто не работает для меня, как рекламируется. В моей коммерческой программе сборка мусора может происходить спустя долгое время после повреждения кучи. Разве не должно быть выброшено исключение во время повреждения кучи? Почему тогда отладчик его не ловит?   -  person Tech_Dr    schedule 31.10.2011
comment
Кроме того, Win32 HeapValidate не кажется полезным для проверки проблем с управляемой кучей. Как бы вы привели правильные аргументы в пользу этого?   -  person Tech_Dr    schedule 31.10.2011
comment
HeapValidate проверяет только кучи, созданные с помощью HeapCreate. Неуправляемая куча не была создана таким образом, поэтому вам придется найти соответствующую функцию проверки для неуправляемой стороны. (Из описания проблемы не было ясно, было ли повреждение в управляемой или неуправляемой куче, хотя я должен был подумать об этом немного больше.) Я не знаю, есть ли соответствующий, пожалуйста, отсканируйте управляемую кучу для коррупционная функция.   -  person Raymond Chen    schedule 31.10.2011
comment
gflags и pageheap предназначены для отладки куч, созданных с помощью HeapCreate. Они ничего не знают о пользовательских кучах, таких как неуправляемая куча.   -  person Raymond Chen    schedule 31.10.2011
comment
Используйте команду SOS VerifyHeap.   -  person Hans Passant    schedule 26.01.2013


Ответы (1)


Просто предположение, но я считаю, что неожиданное поведение gflags вызвано этой строкой:

byte[] chBuf = new byte[8];

Поскольку chBuf управляется средой CLR, gflags не может помещать шаблон заполнения после него для обнаружения переполнения буфера. Попробуйте изменить это на:

IntPtr chBuf = Marshal.AllocHGlobal(8);

Так что вы будете выделять в неуправляемой куче. Gflags должен уметь с этим работать. Кроме того, вам может потребоваться изменить подпись ReadFile, чтобы это работало:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(SafeFileHandle hFile, [Out] IntPtr lpBuffer,
uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
person Ilian    schedule 31.10.2011
comment
Ильян, спасибо за предложение. Я пробовал это, и никаких исключений никогда не выбрасывалось. Если в собственной куче произойдет переполнение буфера, ReadFile просто вернет 0 прочитанных байтов. Однако, учитывая, что мой большой коммерческий код полностью написан на C#, этот подход, вероятно, пропустит многие управляемые родные пинвоки в моем коде — например, в сторонних элементах управления, настройке связи USB, не говоря уже о переходах в коде Microsoft. - person Tech_Dr; 31.10.2011
comment
P/Invoked ReadFile, вероятно, проглатывает прерывание отладки, созданное нативной стороной. Попробуйте запустить пример проекта под отладчиком, чтобы проверить, может ли он обнаружить переполнение буфера во время вызова ReadFile... Только что проверил этот подход на моей машине, и он работает. Хотя я не знаю, возможно ли это с вашим более крупным проектом. - person Ilian; 01.11.2011
comment
Илиан, пожалуйста, расскажите подробнее о том, как это сработало для вас. Вы запускали пример проекта выше? Я попробовал ваше предложение, запустив конфигурацию отладки и выпуска в Visual Studio 2005. Включил неуправляемую отладку проекта. Пункт меню Отладка/Исключения: установить прерывание при нарушении прав доступа Win32 и CLR/System.AccessViolationException. Я запрашиваю 513 символов из текстового файла с размером chBuf 8. ReadFile сообщает о прочтении 513 символов и продолжает. При попытке выполнения инструкции GC.Collect программа зависает. Спасибо. - person Tech_Dr; 04.11.2011