Подобный сбой может произойти по многим причинам, например из-за неисправности аппаратуры. Когда это случилось у одного системного администратора, он обратился в Microsoft, а мы провели анализ дампа. Оказалось, причиной аппаратного сбоя было инвертирование разрядов (bit flip). Инвертирование возникает при копировании информации, если один из разрядов меняет значение, что приводит к сбою. Единица в результате ошибки превращается в ноль или наоборот. Инвертирование приводит к сбою по контролю четности — это обычный метод обнаружения аппаратных сбоев Windows (например, нарушение в работе модулей памяти и перегрев процессора).

Далее я покажу, что такое инвертирование, и расскажу, как обнаружить его, так как процессор при попытке скопировать информацию вызывает сбой системы. Таким образом, если техническая служба Microsoft просматривает дамп памяти и инженер сообщает об обнаружении аппаратного сбоя типа инвертирования разрядов, необходимо понимать, что он имеет в виду. Кроме того, в этой заметке я расскажу о нарушениях прав доступа, регистрах и команде языка ассемблера mov.

Нарушение прав доступа

Сервер нашего администратора создал файл memory.dmp, который был отправлен для анализа в группу Microsoft Global Escalation Services. Я загрузил дамп сбоя в отладчик Windows и начал исследование. Подробную информацию о том, как загрузить файл в отладчик на системе пользователя, можно найти в статье «Administrators’ Intro to Debugging» по адресу http://windowsitpro.com/article/

articleid/101818/administrators-intro-to-debugging.html.

После загрузки дампа в отладчик я ввел команду

! analyze -v

Эта команда дает основную информацию о типе происшедшего сбоя. Выводимые по команде! analyze -v данные показали, что причина ошибки кроется в плохой памяти. Также отладчик показал команду, которую процессор пытался выполнить во время сбоя. Ошибка этого типа обычно возникает, когда указатель дает ссылку на значение, которое нельзя установить. Указатель должен содержать адрес места, где размещается информация в памяти. Если указатель ссылается на неправильное значение, система может дать сбой при попытке считать это значение. Когда возникает такой сбой из-за того, что система наткнулась на неверный адрес, говорят, что сбой вызывает нарушение прав доступа (access violation).

Выводимые в команде! analyze -v данные также содержат информацию о команде языка ассемблера, которая вызвала сбой типа access violation. В этих данных (экран 1) значение 80546944 не является правильным адресом, о чем свидетельствуют вопросительные знаки рядом с ним. Когда код исполняется процессором, он пробует перейти по этому адресу, и срабатывает ловушка на ошибки отсутствия страницы и нарушения прав доступа.

Вывод команды !analyze -v как иллюстрация ошибки нарушения прав доступа

Команда mov

Обратим внимание на команду mov (сокращение от move) в выводимых данных на экране 1. При выполнении команды процессор копирует исходные данные в назначенное место. Эта команда не уничтожает исходные данные, а только копирует информацию.

Для команды mov следует указать, откуда и куда она копирует данные. Эта информация задается в виде операндов. В команде на экране регистр EAX представляет собой первый операнд; этот первый операнд является местом назначения. Второй операнд — dword ptr [esi], он представляет адрес, на который указывает регистр ESI. Откуда известно, что регистр ESI указывает на этот адрес, а не адрес содержится в самом ESI? Потому что отладчик поместил регистр ESI в квадратные скобки. Эти скобки говорят о том, что процессор не использует сам регистр, а задействует содержимое регистра как указатель на реальный адрес, где находятся данные.

Отладчик также выводит dword ptr, и это опять же говорит о том, что регистр ESI будет обрабатываться как указатель с размером dword. Использование указателя в качестве адреса для получения реальных данных называется получением значения по указателю (dereferencing). Подведем итог. Отладчик помог идентифицировать команду, которая исполнялась процессором во время сбоя при попытке копирования данных памяти по адресу, содержавшемуся в регистре ESI, в регистр EAX. Таким образом, у регистров есть имена, но что же такое регистр?

Просмотр содержимого регистров процессора

Регистры являются небольшими ячейками памяти, встроенными в процессор. Доступ к ним осуществляется значительно быстрее, чем в случае доступа к оперативной памяти в слотах материнской платы (например, к модулям DIMM). Каждый из регистров имеет имя, например ESI или EAX.

Можно использовать команду r отладчика Windows для сброса в дамп содержимого регистров. Например, при запуске команды r из приглашения отладчика передается имя регистра ESI. На экране появится информация со значением содержимого этого регистра (0x86f4c658):

r esi

esi=86f4c658

Просмотрите вывод команды! analzye -v на экране. Видно, что это значение в регистре ESI является значением неправильного адреса. Поэтому система дала сбой: в ESI было загружено неправильное значение.

Исследование предыдущей команды языка ассемблера

Для того чтобы понять, почему произошел сбой системы, необходимо обратиться к команде ассемблера, которая выполнялась до появления неправильного значения в ESI, послужившего причиной сбоя. Воспользуемся командой ub (unassembled backwards), чтобы узнать, какие команды языка ассемблера выполнялись ранее. Вот данные, полученные по этой команде:

0: kd> ub. L1

mov esi, dword ptr [edi]

Символ точки (.) говорит о том, что команда рассматривает инструкции в обратном направлении (backward) от текущей инструкции. Операнд L1 свидетельствует о том, что необходимо посмотреть только одну инструкцию.

Из данной информации видно, что мы следим за указателем в EDI, чтобы получить значение, которое загружаем в ESI. Вспомним, что квадратные скобки, в которые заключен EDI, указывают, что мы получаем значение по указателю, то есть следим за указателем, который содержится в регистре EDI. Теперь понятно, как в ESI попало неправильное значение. Мы скопировали это значение из ячейки памяти, на которую указывает EDI. Возьмем теперь команду dd (display memory as dwords), чтобы исследовать информацию, на которую ссылается EDI. Вот команда и ее результаты:

1: kd>dd @edi L1

80566944

Символ @ означает, что EDI — имя регистра, а модификатор L1 — что необходимо проследить только одно двойное слово dword.

Таким образом, если бы аппаратное обеспечение правильно выполнило команды языка ассемблера, которые заданы программно, то значение 80566944 было бы скопировано в регистр ESI. На самом же деле, как было показано, значение в регистре ESI было неправильным: 80546944.

Прыжок к разрядам, чтобы найти инвертирование

Обратим внимание на то, что адрес очень близок к неправильному адресу 80566944, на который ссылается указатель. Теперь используем встроенную команду отладчика. formats, чтобы преобразовать эти два значения в двоичный формат:

1: kd>.formats 80546944

Binary: 10000000 01010100 01101001

01000100

1: kd>.formats 80566944

Binary: 10000000 01010110 01101001

01000100

Из результата видно, что эти два адреса отличаются только одним битом. Такой тип ошибки называется инвертированием. Ошибка кроется в аппаратных средствах и приводит к тому, что один бит был инвертирован, то есть заменен противоположным.

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