Я искал этот вопрос на сайте суперпользователя, но никто не разместил его, поэтому вот мой вопрос: BSOD дает нам 100% точную ошибку?
2 ответа
Коды BSOD - это именно те параметры, которые были переданы в KeBugCheckEx. Первый такой параметр называется "код ошибки". Он переведен на сообщение, которое вы видите на BSOD. Например, код проверки ошибки 0x50 - это PAGE_FAULT_IN_NONPAGED_AREA, 0x44 - это MULTIPLE_IRP_COMPLETE_REQUESTS.
Значения остальных четырех параметров специфичны для конкретного кода баг-чека. Например, в случае PAGE_FAULT_IN_NONPAGED_AREA один из других параметров будет указывать виртуальный адрес, который вышел из строя. Для MULTIPLE_IRP_COMPLETE_REQUESTS один из параметров указывает адрес IRP (пакет запроса ввода / вывода).
Однако: фраза, которую мы часто используем при отладке в режиме ядра, звучит так: "жертва не всегда является виновником". т. е. код, созданный для сбоя, не всегда является виновником (код, который создал обстоятельства, вызвавшие сбой). BSOD только идентифицирует жертву. Даже мини-дамп обычно не имеет достаточно информации, чтобы пойти дальше.
Существует две широкие категории кодов BSOD: те, которые указывают на "ошибку подтверждения", и те, которые происходят из-за необработанного или необработанного исключения, которое возникло в режиме ядра. (В документации по отладчику нет четкого различия между ними, хотя обычно это можно выяснить из описания каждого кода баг-чека.)
"Ошибка подтверждения" аналогична тому, как макрос "assert" обычно используется в программировании на C (хотя Windows не использует макрос "assert" в режиме ядра). Это "встроенный" тест для условия "не должно происходить". Например, NO_MORE_IRP_STACK_LOCATIONS означает, что кто-то создал IRP со слишком небольшим количеством "расположений стека" (примечание: это не то же самое, что "стек", который используется для адресов возврата, локальных переменных и т.д.) Для числа существующих многоуровневых драйверов для данного устройства (или "DevNode").
"Исключение" - это то, что происходит как побочный эффект выполнения инструкции. Некоторые исключения могут быть "обработаны". Например, ошибка страницы является исключением. В большинстве случаев (когда вы находитесь либо в пользовательском режиме, либо в режиме ядра при IRQL <2, а виртуальный адрес, на который делается ссылка, правильно определен и доступен в текущем режиме доступа), пейджер ОС может позаботиться о сбоях страницы.
Но если какое-либо из этих условий не выполняется, ошибка страницы не может быть устранена. В пользовательском режиме это обычно приводит к сбою процесса. В режиме ядра это приведет к BSOD, с любым из нескольких кодов ошибок в зависимости от точных кружков. Общие из них:
- IRQL_NOT_LESS_OR_EQUAL (ошибка страницы произошла на IRQL 2 или выше)
- DRIVER_IRQL_NOT_LESS_OR_EQUAL (похоже, но KeBugCheckEx выяснил, что ошибка страницы возникла внутри драйвера, и изменил код проверки на наличие ошибок)
- KMODE_EXCEPTION_NOT_HANDLED (он был на IRQL 0 или 1, но не может быть решен по какой-то другой причине)
- SYSTEM_SERVICE_EXCEPTION (также в IRQL 0 или 1, но в подпрограмме режима ядра, которая была вызвана из пользовательского режима (не имеет ничего общего с процессами "обслуживания"))
- SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (также при IRQL 0 или 1, но проблема возникла в потоке в "системном" процессе)... и т.д.
Теперь вот в чем проблема:
Код проверки ошибок и другая информация всегда точно указывают обстоятельства, в которых была обнаружена проблема. Но это не обязательно - на самом деле это часто не так - указывает на истинную причину проблемы.
Например, наиболее распространенная причина необратимого сбоя страницы в режиме ядра заключается в том, что адрес, на который ссылаются, неверен. Например, предположим, что я вызываю ExAllocatePool (примерно эквивалентно k-режиму для malloc), и он не может выделить то, что я хочу. В этом случае он вернет мне не адрес выделенного блока, а ноль - "нулевой указатель". Теперь предположим, что я храню этот ноль там, где должен быть указатель. Позже, какой-то другой фрагмент кода, в ОС, а не в моем коде, пытается использовать этот указатель. BSOD! Очевидная информация о BSOD и мини-дампе будет указывать на код, который пытался использовать указатель. Но настоящий виновник - мой код, который не смог проверить нулевой возврат из ExAllocatePool и сохранил его как "указатель". Но к тому времени мой код может быть давно ушел, т.е. больше не выполняется.
Другой пример. Предположим, что я успешно выделил нужный мне пул (кучу), но, хотя я выделяю 120 байтов, мой код по ошибке записывает 140 байтов за пределы адреса, который мне возвращен. Я только что испортил метаданные пула для следующего блока пула, и если этот блок используется, я также повредил данные, принадлежащие тому, кто владеет этим блоком. Это не может вызвать проблемы в течение некоторого времени. Это не сразу вызовет проблемы для меня! Но в конечном итоге, когда тот, кто владеет этим блоком, попытается использовать свои данные, у него возникнут проблемы (может быть ошибка страницы, может быть много вещей). Или, если запрос на освобождение или выделение пула попадет в поврежденные метаданные, это, скорее всего, вызовет какое-то исключение, что приведет к BSOD. И, опять же, я, виновник, скорее всего, нигде не будет очевиден.
При их отладке вы должны выяснить, откуда взялись плохие данные - обычно указатель, а не только кто пытался их использовать.
Точно так же проверка ошибок NO_MORE_IRP_STACK_LOCATIONS никогда не является ошибкой кода в IoCompleteRequest, который его обнаруживает. Вероятно, это ошибка какого-то драйвера, который неправильно настроил наложение драйверов. Простой взгляд на мини-дамп (и, безусловно, на то, что "разбитые" вещи, которые люди продолжают публиковать, выводит), быстро решит, что "проблема в ntoskrnl", потому что это IoCompleteRequest, который вызывает KeBugCheckEx по этому пути, а IoCompleteRequest находится в ntoskrnl. Но реальная проблема в таких случаях неизменно заключается в том, что некоторые драйверы неправильно устанавливают слои объектов устройства (или они были настроены правильно, но позже были повреждены). Какой код драйвера сделал нечестное, вряд ли будет очевидно в файле минидампа.
Иногда можно определить из дампа ядра, какой драйвер был фактическим виновником, но BSOD почти никогда не скажет вам, а мини-дамп обычно не имеет достаточно информации, чтобы сказать вам.
В некоторых случаях код проверки ошибок и другая информация в BSOD, кажется, довольно сильно отделена от реальной проблемы. Например, UNEXPECTED_KERNEL_MODE_TRAP был чем-то, что мы видели бы умеренно часто (как BSODы ... особенно, если вы использовали конкретную звуковую карту, которую я не буду здесь определять; продукт и даже используемый чипсет давно устарели, поэтому не имеет значения сейчас), с параметрами, которые указывают на "двойную ошибку". Многие из них были вызваны кодом, который занимал слишком много места в стеке ядра. Там нет ничего о "UNEXPECTED_KERNEL_MODE_TRAP" или даже "двойная ошибка", которая говорит вам об этом. (Через некоторое время документ отладчика был обновлен и теперь содержит предположение, что "двойная ошибка" может быть вызвана переполнением стека ядра.)
Для получения дополнительной информации обо всех возможных кодах проверки ошибок см. Справку, прилагаемую к WinDbg, раздел "Справочник кодов ошибок". Если вы еще не знакомы с материалами в Windows Internals, написанными Соломоном, Руссиновичем и Ионеску, вам, вероятно, придется обратиться к этому, чтобы понять многие описания. Более подробное объяснение "почему ОС не может просто все исправить и продолжить" вместо сбоя, смотрите в моем ответе здесь.
Нет. Коды ошибок не могут быть основной причиной, но много раз связаны с этим. Например, из-за потери питания может произойти сбой жесткого диска, потому что он вышел из строя из-за сбоя ввода-вывода с диском (до того, как процессор вообще потерял питание). ИЛИ ваш блок питания может быть неисправен и неожиданно отключить питание жесткого диска.
Коды BSOD - всего лишь указатели на то, почему в текущем / предыдущем сеансе произошел сбой окон.