Впредыдущем номере в статье «Диагностика повреждений памяти ядра» мы прошлись по азам работы с пулом памяти ядра. На этот раз я продолжу рассматривать Special Pool — основной инструмент, используемый командой поддержки Идея состоит в том, чтобы поймать драйверы до нарушения ими границ выделенной памяти, принуждая их записывать данные в защитные страницы, что сразу приводит к сбою системы с нарушителем на верхушке стека.

Использование Special Pool для поимки нарушителя

Для того чтобы проделать все описанное выше, нужно кое-что поменять в модели памяти. Когда работу драйвера контролирует Special Pool, выделяемые им сегменты памяти не разделяются на 4 Kбайт страницы. Вместо этого вся страница помещается целиком в отведенное место с буфером драйвера, расположенным внизу страницы. Цель состоит в том, чтобы позже поймать драйвер, читающий или пишущий за выделенные границы и попадающий на защитную страницу. Ситуация переполнения буфера затрагивает защитную страницу, вызывая «голубой экран» с сообщением Bug Check 0xCD: PAGE_FAULT_BEYOND_END_OF_ALLOCATION. Просмотр дампа памяти должен показать драйвер, который записал данные вне границ.

Остаток страницы, содержащий буфер, заполняется произвольными комбинациями битов, которые на первый взгляд кажутся бессмысленными, однако они выполняют очень важную задачу. Когда память освобождается, происходит сканирование всех битовых шаблонов, и ищутся изменения в сигнатурах. Если сигнатура была переписана хотя бы в одном бите, менеджер памяти останавливает систему с сообщением Bug Check 0xC1: SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION. Поиск порчи в этих битовых шаблонах может указать на ситуацию недостаточного наполнения буфера; в этом случае мы можем рекомендовать использовать специальный флаг для активации мониторинга недостаточного наполнения. В этой модели схема отображается зеркально, с буфером драйвера, перемещенного наверх страницы, за которым следует битовый шаблон, продолжающийся до конца страницы. Цель состоит в том, чтобы поймать считывание или запись, сделанные слишком рано и попадающие на защитную страницу перед распределением памяти.

Установка ловушки: методы активации Special Pool

Special Pool может быть активирован несколькими способами. Обычно можно активировать Special Pool, просто редактируя системный реестр. Добавьте параметр PoolTagOverruns со значением 1 в реестр, в раздел HKEY_LOCAL_MACHINESYSTEMCurrentControlSet

ControlSession ManagerMemory Management — активируется процесс выявления переполнения. Изменение значения на 0 рекомендуется для работы с недостаточным наполнением. В том же самом месте реестра параметр PoolTag указывает тег для отслеживания. Его значения произвольны, включая использование групповых символов.

В том случае когда мы не можем определить, какой тег вызывает повреждение, обычно рекомендуют использовать значение 0x2a (в ASCII является эквивалентом *) в шестнадцатеричной системе исчисления для выбора тегов всех драйверов. Важно отметить, что не все распределения памяти будут выделены из памяти Special Pool, потому что у него ограниченный ресурс. Кроме того, это очень дорогостоящий ресурс, потому что менеджер памяти распределяет полную страницу памяти в 4 Kбайт для буфера и двух дополнительных виртуальных недоступных защитных страниц; также не рекомендуется работать в этом режиме после того, как вы определили причину порчи памяти.

Другой инструмент для активации Special Pool — утилита Global Flags (Gflags), которая включена в инструментарий Windows Debugging. Gflags поставляется с графической оболочкой и в версии для командной строки и включает обширный файл Help. Special Pool активируется на вкладке System Registry в графической оболочке Gflags, как показано на экране 1, когда вводится тег, который нужно отслеживать. Предусмотрен режим для отслеживания переполнения путем выбора переключателя Verify End, а также режим для отслеживания недостаточного наполнения — Verify Start. Как и другие инструменты, Gflags по умолчанию выполняет мониторинг переполнения.

Использование утилиты Global Flags для активации Special Pool

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

Оба метода требуют перезагрузки системы, что может быть неудобно, если ваш производственный сервер нельзя освободить от работы для обслуживания. В версии ядра Windows, начинающейся с 6.0 (то есть Windows Vista и более новые), можно использовать флаги ядра для активации Special Pool, таким образом, необходимость в перезагрузке отпадает. Однако такое изменение после перезагрузки не сохраняется.

Если вы используете утилиту командной строки Gflags для установки флага ядра, можно активировать Special Pool, указывая /k вместе с переключателем Special Pool вида +spp. Вот пример использования команды для слежения за сегментами размером 30 до следующей перезагрузки:

Gflags/k +spp 0x30

Обратитесь к файлу Help утилиты Global Flags, чтобы найти полную информацию об использовании инструмента.

Driver Verifier — еще один инструмент, который служба поддержки Microsoft прописывает для активации Special Pool. Это мой любимый инструмент, потому что он предлагает более детальные параметры, чем другие инструменты и пошаговые мастера. Упомянутые выше средства ограничиваются отдельными драйверами и групповыми шаблонами, тогда как Verifier дает возможность выбирать несколько драйверов из списка.

Приведу несколько обычных шагов, которые я выполняю, когда использую Driver Verifier вместе с Special Pool для исследования проблемы порчи памяти ядра.

  1. Откройте Driver Verifier, запустив verifer.exe из командной строки.
  2. Когда инструмент откроется, укажите Create custom settings (for code developers) и нажмите Next.
  3. Из общего списка выберите вариант Select individual settings from a full list и нажмите Next.
  4. Выберите режим Special Pool. После того как вы нажмете Next, вам будет предложено несколько вариантов (экран 2), среди которых нужно выбрать вариант Select driver names from a list. Этот вариант позволит вам задействовать более точный метод, таким образом, вы сможете указать только специфические драйверы для отслеживания.

Применение Driver Verifier для отслеживания специфических драйверов с помощью Special Pool

После перезагрузки сервер будет продолжать работать, пока Special Pool не поймает драйвер, который портит пул. Дамп памяти укажет плохой драйвер в стеке. Завершив свое исследование, не забудьте отключить Verifier, используя параметр, доступный в мастере.

Несколько предупреждений

Special Pool может очень пригодиться в решении проблем порчи памяти, но он несовершенен. В некоторых случаях активация Special Pool может изменять согласование по времени и вызывать проблему прекращения воспроизведения ошибки. В других случаях Special Pool может ловить «преступников», пока система загружается, что вызывает фатальный сбой до появления экрана регистрации. Помните, что это и есть работа Special Pool — вызывать сбой в системе и указание «преступника» в стеке. Если это случается преждевременно, аварийная система может не дать пользователю заблокировать Special Pool, что меняет динамику сценария на вариант с постоянно не загружающейся системой. Если вы оказались в такой ситуации, режим загрузки Last Known Good заблокирует Special Pool, возвращая настройки реестра в первоначальное состояние.

Обратная сторона инструмента — эффект «белой обезьяны». Special Pool может легко раскрыть ошибки памяти в других драйверах, которые в нормальном состоянии не вызвали бы нестабильность в работе. Это неплохо, однако может привести к тому, что вы поверите, будто решили проблему, до того, как на самом деле обнаружите виновника.

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

Рон Сток (ronsto@microsoft.com) — инженер по разработке в группе Global Escalation Services компании Microsoft. Специализируется на диагностике сложных ошибок и проблемах, влияющих на производительность Windows