Оригинал.
5.01.2018
Не каждый месяц, даже не каждый год дарит ошибки аппаратного свойства — то есть такие, в которых виноват не софт, а «железо». Ещё реже такие ошибки способны заметно повлиять на рядового пользователя. И в этом смысле 2018-й оказался на редкость «удачным»: первые же дни его принесли известие именно о таком баге (и даже целом семействе!) в массовых микропроцессорах. Собственно события продолжают развиваться прямо сейчас, так что вероятно появление в следующие часы и дни свежей информации. Но и того, что уже известно, достаточно, чтобы рассмотреть — неожиданную, нежданную! — проблему в подробностях.
Чтобы быстро ввести вас в курс дела: в современных процессорах (главным образом от Intel, но отчасти и AMD, а также ARM-совместимых) обнаружилась уязвимость, позволяющая обойти аппаратную защиту памяти. Попросту говоря, любая программа — начиная от написанной в машинных кодах и заканчивая яваскриптом — некоторым хитрым образом манипулируя процессором, способна теперь заглянуть в те участки оперативной памяти, в которые ей заглядывать не позволено: в память ядра операционной системы или соседних приложений. К счастью, не изменить, не стереть, не испортить — только заглянуть. Но и этого, как вы понимаете, достаточно: ведь можно утащить пароли, криптоключи, рандомные сиды и прочее, прочее...
Остановитесь на секунду, задумайтесь. Благополучие всей цифровой техники на протяжении последней трети века зиждется в том числе на защите памяти — на идее о том, что разные процессы не могут помешать друг другу, не могут даже видеть друг друга, без разрешения операционной системы. И вот теперь этот камень из фундамента выбит!
Чтобы понять, как такое стало возможным, необходимо копнуть глубже, вспомнить, как работает типичный современный микропроцессор, а точнее, как именно он исполняет программы. Начиная с 90-х годов типичный процессор не просто выполняет инструкции одна за другой, а умеет делать это не по порядку: он считывает несколько инструкций разом и старается одновременно исполнить даже те из них, очередь которых ещё не подошла, если только они не зависят от предыдущих в очереди. Такой механизм называют суперскалярным, а в последнее время всё чаще «спекулятивным». И придуман он, конечно, ради повышения производительности.
Спекулятивное исполнение очень эффективно ещё и потому, что микропроцессоры Intel и AMD, например, хоть по-прежнему и программируются операциями из набора x86, то есть формально остаются процессорами CISC, на самом деле имеют RISC-сердцевину и разбивают каждую x86-инструкцию на инструкции ещё более низкоуровневого, своего личного, внутреннего, языка, называемого микрокодом. Примерно так же работают и чипы семейства ARM.
Процессор считывает из оперативной памяти несколько команд, разбивает каждую из них на микрокодовые инструкции, и исполняет уже весь этот микрокодовый поток вперемешку, надеясь, что сумел предугадать порядок исполнения верно. Если интересно, пару лет назад здесь был рассказ о последних тенденциях в этом направлении: как для более точного предсказания порядка стали даже применяться нейросети (см. «Зачем процессору нейроны?»).
В контексте сегодняшнего разговора нам, впрочем, не столько важна производительность, сколько другой аспект «внутрипроцессорных спекуляций», а именно очерёдность проверок на то, имеет ли конкретная операция право исполнить то, о чём она просит. Вообразите, например, следующую простую программу из двух инструкций. Первая инструкция пусть запрашивает данные из области памяти, на доступ к которой у неё нет прав — скажем, из области, занятой ядром операционной системы. Следующая инструкция пусть полученные данные использует. Что произойдёт, если мы запустим такую программу?
Теория утверждает, что ничего страшного случиться не должно. На первой же инструкции, поняв, что программа пытается превысить свои полномочия, процессор выбросит так называемое «исключение», то есть сообщит об ошибке. Так что вторая инструкция теряет смысл, поскольку требуемых данных не получит. Однако практически, то есть на уровне микрокода, микроархитектуры, всё сложнее.
Микрокод и спекулятивное исполнение внесут в нарисованную нами картину две поправки. Во-первых, обе инструкции будут исполнены одновременно. Во-вторых, как оказалось, в процессорах Intel — как минимум после 2010-го года, но есть данные, что и во всех, начиная с 95-го, за исключением Itanium и Atom до 2013-го — процессор выдаст ошибку не сразу после обращения к запрещённому участку памяти, а лишь после того, как запрошенные данные загрузит в кэш. То есть вторая операция, исполняясь одновременно, их увидит.
Чтобы было проще это понять, вообразите следующую сценку (автор не я, только перескажу и слегка переиначу). Боб просит Алису показать страницу из её дневника. Получив такой запрос, Алиса, разумеется, должна в первую очередь сообразить, что прав на доступ к её личной информации у Боба недостаточно и ответить отказом. Вот только Алиса, будучи девушкой недалёкой и порывистой, сперва предъявляет дневник, и лишь потом спохватывается: да есть ли у тебя, Боб, право? Ну, а Боб, если только он понимает, с кем имеет дело, конечно, уже успел схватить глазом всё важное. Именно так и работают микропроцессоры Intel.
Почему это так — вопрос открытый. Как уверяют критики, Intel, страдая от того, что упустила рынок мобильных устройств, в последние пять лет сильно изменила порядок внутренней приёмки новых проектов — и это ударило по качеству. С другой стороны, проблема утечки данных через спекулятивное исполнение свойственна не только интеловским чипам. Микропроцессоры Intel подвержены самой опасной и самой простой из атак: её (или соответствующую ей уязвимость) называют Meltdown. Но существует также более труднореализуемая и потому (пока) менее опасная атака Spectre — которой подвержены уже не только чипы Intel, но и AMD, и всё ARM-семейство.
Обе атаки опасны и для рядовых пользователей (один из сценариев: сомнительный сайт, пока вы его просматриваете, с помощью скриптов сможет украсть данные из памяти ОС или других запущенных программ; неудивительно, что обеспокоены даже разработчики браузеров!), но особенно для «облачных» сервисов и их клиентов. Ведь «облако» — суть, общий компьютер, где пользователи изолированы друг от друга в том числе механизмом защиты памяти. Вот только теперь оказывается, что защита эта бесполезна!
Лекарство, впрочем, существует — но непростое и недешёвое. Смена микрокода, очевидно, не поможет, иначе Intel бы первая это и сделала. Требуется внесение исправлений в ядра операционных систем. На текущий момент патчи против Meltdown уже выпущены для всех популярных ОС — и, если вы обновляетесь автоматически и используете Linux или Android, MS Windows или OS X, вы в безопасности. Однако нужно понимать, какой ценой это далось. Чтобы гарантировать, что информация не просочится, теперь при каждой передаче управления от программы к системе и обратно (а это случается тысячи раз в секунду на любом компьютере), операционка вынуждена производить значительные изменения в таблицах, определяющих распределение памяти. В результате даже если компьютер исполняет всего одну программу, потеря производительности составит несколько процентов. Если же на машине работает «тяжёлый» софт, вроде базы данных, защита от Meltdown уронит производительность на десятки процентов (и это не теория, а проверенный факт)!
Конечно, со временем защитный механизм будет оптимизирован, а то и придуман новый. А позже Intel, конечно, внесёт исправления и в саму архитектуру своих микропроцессоров — то есть, чипы, которые будут выпускаться через год или два, уже должны быть неуязвимы для Meltdown. Но до тех пор лучшим решением будет избегать интеловских процессоров вовсе! Ничего удивительного, что прогнозы относительно курса акций Intel сейчас самые мрачные. И тот факт, что даже глава Intel нынче осенью распродал все принадлежавшие ему акции, оставив лишь требуемый правилами компании минимум, эти прогнозы подтверждает: эксперименты с брешью в механизме защиты памяти шли почти весь прошлый год — и логично предположить, что Кржанич понимал, чем они закончатся...
Однако, повторюсь, и процессоры AMD, и ARM-семейство тоже в некоторой степени подвержены той же проблеме: вспомните про Spectre. К счастью, защищаться от Spectre дешевле, чем от Meltdown: экспериментальный патч для Linux уже существует (ключевое слово: retpoline) и даёт сокращение производительности лишь на полтора процента. Но и работа в этом направлении только начинается: Spectre — более сложная, но и более многообещающая разновидность атак (можно сказать, что Meltdown — её частный случай), на изучение которой требуется время.
Общим же результатом скандала может стать переосмысление спекулятивного исполнения: идеи, пришедшей ещё из 60-х годов. Конечно, от него не откажутся. Но, вероятно, утечка данных сквозь аппаратные барьеры отсрочит дальнейшее усложнение спекулятивных движков. В частности, начавшуюся было гибридизацию их с искусственными нейросетями.
микропроцессор,железо,ZDV,Meltdown,Spectre,Intel,спекулятивное_исполнение,микрокод,KPTI,retpoline,KAISER