Дефекты проектирования Intel Core 2 Duo

ОГЛАВЛЕНИЕ

Процессоры Intel Core 2 Duo (и не только они одни!) содержат множество ошибок, приводящих к сбоям программного обеспечения, зависаниям операционной системы и даже возможности удаленного захвата управления компьютером! Часть ошибок обходится программным путем, часть – обновлением микрокода ЦП или прошивки BIOS, оставшиеся – неисправимы и требуют смены процессора. Насколько реальны эти угрозы? Попробуем разобраться!

Критикуя Windows (и отчасти Linux) за большое количество программных ошибок, мы «по умолчанию» закладываемся на непорочность аппаратного обеспечения, проектировщики которого ничем не отличаются от разработчиков операционных систем. До тех пор, пока процессоры были простыми (относительно операционных систем), выходили редко и тестировались тщательно – ошибки «кремниевых коней» носили единичный характер и учитывались разработчиками компиляторов. Сейчас они представляют разве что познавательный интерес. Легендарный «Hamarsoft's 86BUGS list», насчитывающий свыше сотни ошибок, недокументированных машинных команд и особенностей их поведения, последний раз обновлялся в 1994 году, после чего отправился на свалку истории, захлебнувшись в потоке дефектов, обнаруженных в первых моделях Pentium-процессоров, причем ни один из этих дефектов (за исключением знаменитой ошибки деления, описанной в одноименной врезке) до широкой общественности так и не дошел, ограничившись кругом производителей материнских плат, прошивок BIOS, разработчиков операционных систем/компиляторов и прочей технической элитой.

Возьмем, к примеру, инструкцию битового сканирования «BSF dst, src», копирующую в dst индекс первого установленного бита в src. Как вам нравится тот факт, что если src равен нулю, то содержимое dst становится неопределенным, то есть там может оказаться любой мусор, что серьезно осложняет отладку программ. Допустим, на машине разработчика установлен ЦП, оставляющий dst неизменным, и разработчик (или его компилятор) закладывается именно на такое поведение ЦП. А вот у конечного пользователя dst может сбрасываться в нуль, нарушая работоспособность программы и заставляя разработчика теряться в догадках, с какого момента программа пошла  разнос. Обычно в таких случаях все списывается на Windows или «у вас на компьютере вирусы, переустановите операционную систему… ах, вы уже ее переустановили?! ну тогда мы не знаем, разбирайтесь со своей машиной сами».

Кстати, это уже давно не ошибка, а вполне документированная особенность. Intel даже приводит псевдокод команды BSF во втором томе «Instruction Set Reference»:

Листинг 1. Псевдокод команды BSF с «узаконенной» ошибкой

IF SRC = 0
    THEN
           ZF := 1;
           DEST is undefined;
    ELSE
           ZF := 0;
           temp := 0;
    WHILE Bit(SRC, temp) = 0
    DO
           temp := temp + 1;
           DEST := temp;
    OD;
FI;

С такими «особенностями» можно еще и смириться, но куда прикажете девать эпизодически возникающие исключения на 486+, возникающие при загрузке регистра CR3 (указатель на каталог страниц) в регистр общего назначения? Конечно, непредвиденные исключения можно и подавить, что делает Linux и OpenBSD. Делать-то она это делает, но… местами. А местами не делает. Обращения к регистру CR3 происходят из многих функций ядра, а ядро пишет целая армия разработчиков, часть из которых осведомлена об этом дефекте, а часть – даже не подозревает. В результате мы имеем нестабильно работающую систему.

Windows не пытается сражаться с этим. А зря. Запрещение кэширования на запись в некоторых моделях процессоров разрушает содержимое кэша, откуда процессор продолжает брать ранее скэшированные данные (уже разрушенные), и чтобы программа не «грохнулась», кэш необходимо очистить программным образом, последовательно считывая ячейки памяти – неважно какие – лишь бы заполнить его. Поскольку считываемые данные не были модифицированы, то при последующем поступлении данных в кэш ранее прочитанные ячейки памяти не будут вытесняться ни в кэш вышестоящего уровня, ни в оперативную память, замещаясь новыми данными. На прикладном уровне такая ситуация, конечно, маловероятна, да и нельзя на прикладном уровне управлять кэшированием, но вот драйверы совсем другое дело! Если некоторый регион памяти используется для обмена данными с внешним устройством (видеоконтроллером или платой телеметрии), владельцы «неправильных» процессоров окажутся очень «рады» всевозможным артефактам на экране монитора и неверным телеметрическим результатам. Стоит ли удивляться, что x86 не используются в критических инфраструктурах, где работают простые и тщательно протестированные микроконтроллеры?

Впрочем, мы отвлеклись, хотя это весьма полезное отвлечение, позволяющие читателю понять, что далеко не все странности поведения программного обеспечения имеют программную природу и дефекты процессоров в спонтанно вспыхивающих «голубых экранах смерти» играют далеко не последнюю роль.

Производители ЦП ведут с дефектами проектирования ожесточенную борьбу, прогоняя каждую команду (а то и комбинации команд) через серию агрессивных тестов, результаты которых так или иначе отражаются в документации или же «specification updates». В частности, последние обновления спецификации на Intel Core 2 Duo в любой момент можно скачать с официального сайта Intel: http://www.intel.com/design/core2duo/documentation.htm#specupdt, раздел erraata которого на момент написания этих строк (май 2008) насчитывает 126 ошибок, многие из которых критические и затрагивают не только ядерный, но и прикладной уровень.

Немногим лучше обстоит ситуация с серверными процессорами: Intel Xeon Quad-Core 5400 и его младший собрат Xeon Dual-Core 5100 насчитывают по 54 официально признанных дефекта каждый. И даже Itanium 9000, рекомендованный фирмой Intel для критических инфраструктур (massive, mission-critical computing), хранит в своих недрах 85 «жуков», способных обрушить сервер в любой момент. А ведь это одно из самых дорогих и тщательно протестированных произведений Intel, ориентированное на корпоративный сегмент рынка, который ошибок не прощает и на котором помимо Intel имеются и другие игроки, впрочем, сталкивающиеся с теми же самыми проблемами.

Мир не совершенен, никто из нас не без греха. Обновлять микрокод ЦП, прошивку BIOS (а в критических случаях – и сами процессоры!) нужно так же регулярно, как накладывать заплатки на операционные системы и прочее программное обеспечение. Ах да, операционные системы… С них-то все и началось!

Intel Core 2 Duo со всеми ошибками, которые в нем только есть

…и грянул гром

Ознакомившись с очередной errata на Core 2 Duo, Тео де Раадт (Theo de Raadt), ведущий разработчик операционной системы OpenBSD, славящейся своей надежностью и защищенностью, пришел в ярость, и набросился на производителей Core 2 Duo с обличительными заявлениями в стиле: «Как дальше жить и что нам делать?!», тут же подхваченными прессой и ставшими достоянием широкой общественности, постепенно начинающей осознавать, что помощи нет и не будет. Ситуация очень серьезна – достаточно большое число ошибок не только приводит к краху системы, но и (теоретически) допускает возможность удаленного захвата управления, поскольку некоторые инструкции при определенных обстоятельствах выполняются не так, как ожидалось, например, программа, написанная на JavaScript, интенсивно работающая с кэшем, может вызывать направленный удар по памяти, искажая адрес возврата или другие указатели на функции.

Ряд ошибок процессора «сотрудничает» с ошибками программного переполнения. Широко распространена ошибка неявного приведения типов (кастинга) – signed int в unsigned int. Огромное количество программ, написанных на Си, хранят длину копируемого блока памяти в переменной типа signed int, передавая ее функции типа memcpy() и выполняя при этом проверку «if (len > MAX) return -1», забывая о том, что если len < 0, то данная проверка проходит на «ура», а вот в процессе передачи аргумента len функции memcpy() происходит неявное преобразование типов в unsigned int и, поскольку на x86-процессорах знаковый бит является старшим битом, то memcpy пытается скопировать по меньшей мере 2 Гб памяти (при 32-разрядном int). Вот именно, что «пытается». В ходе копирования функция непременно «врезается» в регион, недоступный на запись, и генерируется исключение, приводящее к аварийному завершению программы (хотя в Windows-системах есть надежда, что в процессе копирования удастся перезаписать адрес обработчика исключений до наступления исключения, перехватывая управления при его генерации, но техника SafeSEH предотвращает такой вариант развития событий). Однако в процессоре Core 2 Duo имеется целый класс ошибок, приводящих к преждевременному прерыванию копирования блока памяти, когда его размер превышает размеры адресного пространства. В нормальных программах такие ошибки никак не проявляются, поскольку никто из программистов не копирует блоки памяти по 4 Гб или более того, но вот хакеры… им преждевременное прерывание копирования очень на руку – отличное средство для «дозированного» переполнения буфера в условиях неявного преобразования signed int в unsigned int.

Разработчики OpenBSD попытались заткнуть самые крупные дыры, переписав код ядра так, чтобы исключить или хотя бы снизить вероятность событий, ведущих к проявлению ошибок, но вот остальные программистские коллективы, выражаясь образным языком, даже не почесались, и в первую очередь это относится к новомодному Server 2008, для которого заплаток нет и не предвидится. Что же тогда говорить о «морально устаревшем», но все еще работающем парке Windows 2000, XP, Server 2003?!

Но вернемся к исходному сообщению Тео де Раадта, опубликованному в конце июня 2007 года, когда обнаруженных ошибок было вдвое меньше, чем сейчас: http://marc.info/?l=openbsd-misc&m=118296441702631.

Сенсация или реальная угроза?

Интересно разобраться, что же так напугало Тео де Раадта и насколько велика вероятность атаки на Core 2 Duo, тем более, что за время, прошедшее с момента публикации, количество обнаруженных ошибок удвоилось. Анализировать ошибки мы будем в порядке, обозначенном де Раадтом, с учетом специфики операционных систем семейства NT (Windows 2000, XP, Server 2003/2008, Vista), Linux и линейки BSD – Free, Net и Open, приводя официальную информацию из errata со всеми выкладками и рассуждениями, а местами – сценариями реализации атаки.