Как именно работает Meltdown
Уже третий день у всех на слуху слова Meltdown и Spectre, свеженькие уязвимости в процессорах. К сожалению, сходу найти что либо про то, как именно работают данные уязвимости (для начала я сосредоточился на Meldown, она попроще), у меня не удалось, пришлось изучать оригинальные публикации и статьи: оригинальная статья, блок Google Project Zero, статья аж из лета 2017. Несмотря на то, что на хабре уже есть перевод введения из оригинальной публикации, хочется поделиться тем, что мне удалось прочитать и понять.
Честно утащено с Хабра - https://habrahabr.ru/post/346078/
Как работает процессор
Последние десятилетия, начиная с 1992 года, когда появился первый Pentium, Intel развивала суперскалярную архитектуру своих процессоров. Суть в том, что компании очень хотелось сделать процессоры быстрее, сохраняя при этом обратную совместимость. В итоге современные процессоры — это очень сложная конструкция. Просто представьте себе: компилятор изо всех сил трудится и упаковывает инструкции так, чтобы они исполнялись в один поток, а процессор внутри себя дербанит код на отдельные инструкции, и начинает исполнять их параллельно, если это возможно, при этом ещё и переупорядочивает их. А всё из-за того, что аппаратных блоков для исполнения команд в процессоре много, каждая же инструкция обычно задействует только один их них. Подливает масла в огонь и то, что тактовая частота процессоров росла сильно быстрее, чем скорость работы оперативной памяти, что привело к появлению кешей 1, 2 и 3 уровней. Сходить в оперативную память стоит больше 100 процессорных тактов, сходить в кэш 1 уровня — уже единицы, исполнить какую нибудь простую арифметическую операцию типа сложения — пара тактов.
В итоге, пока одна инструкция ждёт получения данных из памяти, освобождения блока работы с floating point, ну или ещё чего нибудь, процессор спекулятивно отрабатывает следующие. Современные процессоры могут таким образом параллельно обрабатывать порядка сотни инструкций (97 в Sky Lake, если быть точным). Каждая такая инструкция работает со своими копиями регистров (это происходит в reservation station), и они, в момент исполнения, друг на друга не влияют. После того, как инструкция выполнена, процессор пытается выстроить результат их выполнения в линию в блоке retirement, как если бы всей этой магии суперскалярности не было (компилятор то про неё ничего не знает и думает, что там последовательное исполнение команд — помните об этом?). Если по какой-то причине процессор решит, что инструкция выполнена неправильно, например, потому, что использовала значение регистра, которое на самом деле изменила предыдущая инструкция, то текущая инструкция будет просто выкинута. То же самое происходит и при изменении значения в памяти, или если предсказатель переходов ошибся.
Кстати, тут должно стать понятно, как работает гипертрединг — добавляем второй Register allocation table, и второй блок Retirement register file — и вуаля, у нас уже как бы два ядра, практически бесплатно.
Память в Linux
В 64-битном режиме работы у каждого приложения есть свой выделенный кусочек доступной для чтения и записи памяти, который собственно и является userspace памятью. Однако, на самом деле память ядра тоже присутствует в адресном пространстве процесса (подозреваю, что сделано было с целью повышения производительности работы сисколов), но защищена от доступа из пользовательского кода. Если он попытается обратиться к этой памяти — получит ошибку, это работает на уровне процессора и его колец защиты.
Side-channel атаки
Когда не получается прочитать какие либо данные, можно попробовать воспользоваться побочными эффектами от работы объекта атаки. Классический пример: измеряя с высокой точностью потребление электричества можно различить операции, которые выполняет процессор, именно так был взломан чип для автосигнализаций KeeLoq. В случае Meltdown таким побочным каналом является время чтения данных. Если байт данных, содержится в кэше, то он будет прочитан намного быстрее, чем если он будет вычитываться из оперативной памяти и загружаться в кэш.
Соединяем эти знания вместе
Собственно, суть атаки то очень проста и достаточно красива:
- Сбрасываем кэш процессора.
- Читаем интересную нам переменную из адресного пространства ядра, это вызовет исключение, но оно обработается не сразу.
- Спекулятивно делаем чтение из массива, который располагается в нашем, пользовательском адресном пространстве, на основе значения переменной из пункта 2.
- Последовательно читаем массив и аккуратно замеряем время доступа. Все элементы, кроме одного, будут читаться медленно, а вот элемент, который соответствует значению по недоступному нам адресу.
Таким образом, объектом атаки является микроархитектура процессора, и саму атаку в софте не починить.
Код атаки
- ; rcx = kernel address
- ; rbx = probe array
- retry:
- mov al, byte [rcx]
- shl rax, 0xc
- jz retry
- mov rbx, qword [rbx + rax]
Теперь по шагам, как это работает.
mov al, byte [rcx] — собственно чтение по интересующему атакующего адресу, заодно вызывает исключение. Важный момент заключается в том, что исключение обрабатывается не в момент чтения, а несколько позже.
shl rax, 0xc — зачение умножается на 4096 для того, чтобы избежать сложностей с механизмом загрузки данных в кэш
mov rbx, qword [rbx + rax] — "запоминание" прочитанного значения, этой строкой прогревается кэш
retry и jz retry нужны из-за того, что обращение к началу массива даёт слишком много шума и, таким образом, извлечение нулевых байтов достаточно проблематично. Честно говоря, я не особо понял, зачем так делать — я бы просто к rax прибавил единичку сразу после чтения, да и всё. Важный момент заключается в том, что этот цикл, на самом деле, не бесконечный. Уже первое чтение вызывает исключение
Как пофиксили в Linux
Достаточно прямолинейно — стали выключать отображение страниц памяти ядра в адресное пространство процесса, патч называется Kernel page-table isolation. В результате на каждый вызов сискола переключение контекста стало дороже, отсюда и падение производительности до 1.5 раз.
Комментарии
красиво. что скажешь. а если к этому добавить "небольшое усложнение с 2006-го" как Intel management engine (IME). вообще уже эти 64 битные расширения 32 разрядной графической оболочки, 16 битной дисковой ОС, написаной на 8 битную модификацию 4-х разрядного процессора достали. такой лес костылей и подпорок... были же прямые архитектуры, та же ДЕК-овская Альфа, приемник 32-битных ВАКСов и 16 разрядных ПДП-11... как и множество других. а как обычно бывает - временное решение - костыль-терминал к майнфреймам IBM в итоге и выжил всех остальных.
Всю эту шумиху от "угрозы" 20 летней давности начали раздувать думаю не зря, сейчас накрутят и устроят блекаут типа падения или взлома биржи/банков хакерами и понеслась. Китай/Корею/Россию/ и тд. назначить плохим парнем не вышло. За все ответит Мельтдоний хехе. Ну а название "вируса" просто привет от глобальщиков ))
Я всё ещё не понимаю, как это можно использовать. Первая инструкция даст исключение, допустим, код с циклом ещё и выполнится, но кто, кроме процесса, будет иметь доступ к результатам? Нужно успеть скинуть результат с "тонущего корабля" до того, как система остановит процесс. И куда/как это сделать, если межпроцессная коммуникация идёт через систему, и уж точно, что сначала должно быть обработано исключение?
Да, нагородили многабуков, хотя суть простая как угол дома:
Ядро эксплоита состоит из двух действий:
где
0xc001add0 -- адрес в памяти, где лежит секрет (например ключ шифра для SSL-сессии с интернет банком); R -- регистр CPU в который читается первый байт ключа; array -- вспомогательный массив.
что происходит во время выполнения:
теперь при чтении вспомогательного массива array все элементы i * PAGE_SIZE будут читаться из памяти (долго) а элемент 17 * PAGE_SIZE будет прочитан из кеша (что в разы быстрее). Измерив время чтения всех элементов массива [0..255] * PAGE_SIZE находим элемент который прочитался в два-три-четыре-пять раз быстрее чем все остальные. В нашем примере это будет элемент 17*PAGE_SIZE, значит первый байт ключа равен 17.
Как видно зловредный процесс обнаружить не сложно -- он будет непрерывно вызывать исключения по нарушению прав и игнорировать(!) их (иначе процесс убьют после первого же обращения к системной памяти, что при нормальной работе обычно и происходит). То есть достаточно завести счётчик, который увеличивать при каждом нарушении прав доступа и по достижении скажем 10 убивать процесс с воплями в логах, счётчик можно обнулять скажем раз в 10 сек, что бы оставить возможность легальным программам обрабатывать ошибки.
Гораздо интересней другое, как узнать искомый адрес 0xc001add0? На заборе он не написан, и, с учётом современной моды рандомизировать виртуальные адреса при загрузке, узнать его можно только с помощью какой-нибудь другой дыры в защите. Ибо что бы сдампить ну хотя бы 1G нужна неделя, за которую содержимое памяти мягко говоря несколько поменяется.
Нет, про эксплойт - понятно.
Непонятно, другое.
1. Нарушение уровня привилегий вызывает исключение - то есть, немаскируемое прерывание и передача управления коду ОС. Которая, вообще-то, должна такой нехороший процесс прибить (GPF в винде и кнопочка "закрыть"). Допустим, на это требуется время (реально требуется), и допустим, что ценой жизни процесса эксплойт-коду удаётся извлечь инфу из привилигерованных сегментов. Но...
2. Чтобы передать эту информацию, нужно передать её из процесса в другой, живой процесс (или вообще из компа) и т.д. Для этого нужно обратиться к системе. Что случится заведомо позже, чем обработка исключения.
И вот я не понимаю: то ли нынешние ОС просто игнорируют исключения (хотя обязаны прибивать процесс при нарушении уровня привилегий), то ли существует способ вывести инфу из процесса, не обращаясь к ОС (но обращение к памяти другого процесса с попыткой записи опять же вызовет исключение). ЧТо из этих двух вариантов верно?
А, ну это тоже просто.
После возникновения exception (не надо путать с interrupt и тем более с NMI) CPU переключается на системный кодовый сегмент и системный стек, где запускает ядерный обработчик исключения, который после недолгих размышлений модифицирует пользовательский стек так, что бы при возврате из обработчика обратно в user space произошёл переход на пользовательский обработчик (signal handler) который по умолчанию исполняет самоубийство.
Дальше есть два пути:
1. можно сначала сделать fork, в дочернем процессе выполнить обращение к системной памяти в результате чего дочерний процесс погибнет смертью енота, а в родительском процесс замерить время доступа к вспомогательному массиву, это будет работать поскольку при fork-е и родитель и дочерний процесс шарят как кодовый сегмент, так и сегмент данных.
2. можно повесить свой пользовательский обработчик сигналов, в нём измерить время доступа, а при выходе из обработчика снова выполнится инструкция которая вызвала исключение, в обработчике можно модифицировать стек так, что бы "плохая" инструкция выполнилась с новыми значениями регистров.
Откуда и следует самый простой фикс -- либо сразу убивать без разговоров процессы, которые обращаются к системной памяти, либо как-то лимитировать кол-во таких нарушений и в обеих случаях выводить предупреждение пользователю. Нормальные программы будут работать как работали, а написанные индусами по принципу "да ладно, главное что работает!", те да, могут стать не юзабельными.
Корень зла в том, что за попытку обращения к системной памяти ОС не расстреливает на месте, а позволяет бесконечно долго эти попытки повторять, вроде как "ну ошиблась программа адресом, с кем не бывает, может она ещё исправится".
shmem - и пофиг на процессы, хоть обубивайте их.
что shmem, что fork -- суть одна и та же: один приноситься в жертву, второй добывает результаты.
Можно развить идею, если есть время, убивать надо сначала по pid, потом, если эксцессы повторяются можно сбрасывать executable bit у бинарника, можно вырезать парента в обязательном порядке, кто-то должен же будет спавнить эти торпеды, вот его и прибивать за компанию.
И вы не дочитали: "и в обеих случаях выводить предупреждения пользователю", ибо без пользователя/администратора вся эта борьба мало продуктивна. Малограмотным пользователям всё равно уже не помочь, у них у всех уже стоит яндекс-бар :-) помогать надо тем, кто хочет сам себе помочь и делает для этого усилия, борется.
SEH
Дыра в процессоре: всё об ошибке, позволяющей украсть пароли почти с любого компьютера и смартфона
да понятно, что ошибка в диспечере безопасности, когда данные в кэш подгружаются без учёта прав доступа на текущее кольцо.
Спасибо!
Доходчиво!
Разослал ссылки касперышам...
Блин, очередной «биткоин»... Просто пользуйтесь одноразовыми паролями: вероятность атаки сразу на два взаимодействующих хоста мала.
Это как?
С трудом представляю 40-50 одноразовых паролей.
Для этого есть телефон.
Это называется двухфакторная авторизация.
Одноразовые пароли это другое.
О кстати мысль, под этим же предлогом Мельдония можно биткоин завалить ! едрит тваю ))
Или наоборот поднять... т.к. одноранговая сеть валится труднее сервера при критической уязвимости ядра хоста.
А никому не приходило в голову, что это не "дыра", а банальная отладочная "дверка", причем не для сторонних программеров, а для инженеров-разработчиков ? Собственно, подобные псевдоуязвимости это нормальное явление, пока туда не суют свой нос те кто любит гадить. Они просто необходимы для вылавливания "багов" как на этапе разработки, так и в процессе производства.
Блин, "специалисты" - ноль в тонкостях производства, зато вони от собственной значимости...
И закономерный вопрос: кто и зачем слил эту инфу ?
Мы думаем, что это ход для АНБ КГБ НКВД.
Не надо гадать, беги новый процессор покупать
А какой если не секрет? В интел - мелтдаун, в амд и арм - спектр. Что покупать? Эльбрус?
Спектр у всех. Возможно, даже у Эльбруса.
Тогда какой процессор покупать? Вы написали ироничное замечание с намеком на то, что новость о данной уязвимости была создана с целью чтобы люди бросились покупать другие процессоры (а заодно и материнки, скорее всего, ибо сокеты будут несовместимы) - но так ведь на рынке нет сейчас адекватных предложений для ноутбуков и ПК в которых бы данной уязвимости не было. В чем тогда юмор вашего замечания?
Юмор в том, что из этого постараются извлечь прибыль, ведь ошибка и последствия устраняются только заменой оборудования на новое. Посмотрим ещё, как будут судиться с интелом.
А покупать можно АМД.
Новое оборудование еще надо спроектировать. Как я понял, на это уйдет год-два, а то и больше. АМД подвержен уязвимости Spectre.
У Эльбрусе нет speculative execution. Он потому и требует предварительной компиляции кода, чтобы все веточки заранее расписать. И потому у него такие сложности с жабой и жабоскритом.
надо будет купить два интела, первый, самый мощный из доступных #вотпрямщас, потому как установка патча это потеря трети производительности, а второй тогда, когда (если) пофиксят эту багу в дизайне логики конвейра. вот.
Да какая разница, дыра или дверка. Сейчас уже нет разницы. Да и вопрос про "слил". Эта дыра в Интелах, например, была почти 20 лет. За 20 лет как бы и найти можно..
ну на самом деле о таких вещах производители процессоров должны сообщать если не всем, то хотя бы разработчикам ОС, чтобы те учитывали это в разработке
по сути это конечно не "дыра", а просто особенность работы процессора... потому что сам процессор без ПО в принципе просто кусок металла и кремния, но если разработчики ПО не знали об этой особенности - это косяк как со стороны производителей процессоров, так и со стороны разработчиков ПО
если учесть, что это всё существует аж с 1995(!) года, то значит что миллионы обалдуев не справились со своей работой и не протестили безопасность как следует
Эм, ну вообще-то все знали об этом принципе работы процессора. Это дырой как таковой не являлось. Просто сейчас опубликовали как этот механизм можно использовать в корыстных целях. А так все разработчики ОС знали о механизме спекулятивного исполнения - это все в открытом доступе.
Забей Хитый Лис. Тут программисты все верхнего уровня «питонщики и явщики». Им про «коре дамп» знать не обязательно. А почему только сейчас слили инфу? Гугл с Эплом разрабатывают собственные процессоры. Маркетинговый ход наверное.
Вот, почему-то об этом и подумалось - неужели Гугл свой проц пилит.
Интел на скорую руку сделала PR-заявление в духе, что если зловред украдёт ваши потроха, то это ваши проблемы, а наша микросхема работает как должна. Что они лучше всех заботятся о безопасности, работают совместно с другими и отвалите. Что вы всё врёте. И у кого ещё вы будете покупать? Ха-ха.
Тут недавно народ с пеной у рта доказывал что Эльбрус не нужен, что развивать свой процессор - не надо, что дорого и ненужно. Наверно сейчас промолчат, ну или скажут что это все не правда и что никто красть не будет.
Очень важно: данной уязвимости подвержены только процессоры Intel, в то время как AMD / ARM - нет.
Не нашёл упоминания об этом в статье.
Потому что статья про Мелтдаун. А в АМД - спектр. Хотя спектр и в интел тоже вроде есть.
Если описывается уязвимость, то каждый примеряет её к своему компьютеру, поэтому логично обозначить, что владельцам AMD / ARM волноваться нечего.
Spectre подвержены все современные процессоры (с обучаемым предсказателем ветвлений), и я убедился в этом на своём AMD процессоре.
Но это же неправда. А как вы убедились?
В описании проблемы Meltdown (https://meltdownattack.com/meltdown.pdf) указано, что AMD/ARM не подвержены данной проблеме:
Однако уязвимости Spectre подвержены почти все современные процессоры. Пример из описания Spectre (https://spectreattack.com/spectre.pdf) я воспроизвёл на своём AMD, где видно, как программа читает (с некоторой погрешностью) данные из области, в которую её никто не звал, и в которой лежит фраза "The Magic Words are Squeamish Ossifrage.":
Как видите, всё по науке.
Что-то мы о разных вещах говорим. Я говорю про уязвимости вообще, я вы все время пытаетесь сузить проблему только до Мелтдаун. То что, АМД не подвержены мелдауну не означает что им не нужно беспокоиться, ибо Спектр-1 и 2 тоже вполне себе справляются с чтением памяти.
Статья называется "Как именно работает Meltdown", т.е. в ней рассматривается Meltdown и нет ни одного упоминания о принципах работы Spectre.
А посему, было бы честно донести до читателей, что:
Иначе может сложиться неверное впечатление, что Meltdown затрагивает каждого, что далеко не так.
Spectre - уязвимость иного рода, в свою очередь, затрагивает почти всех, но более сложна в использовании и не так эффективна, однако это уже выходит за рамки статьи, как мне видится из названия.
Вы были бы правы, если бы в ленте АФ было две статьи подряд - одна про Мелтдаун, а вторая про Спектр. Но так как второй статьи нет, то читателю, непосещающему специфические ресурсы будет казаться, что если у него AMD то значит у него все ОК и его это не касается. Поэтому и нельзя писать что "с АМД все в порядке - не беспокойтесь"
В некоторых ARM есть.
Гы, я интелами и не пользуюсь... Один ноут есть, но он сейчас в качестве осциллографа..)
https://habrahabr.ru/post/346078/#comment_10599906
Я так понимаю, что процессор просто некорректно отрабатывает ситуацию с исключением - не все результаты уничтожает. Это баг производителя
Терзают меня смутные сомнения: mov al, byte [rcx] – считывает только байт, а в пароле ведь много символов/байтов...
Это не важно, можно много раз эту процедуру сделать
Нельзя, т.к. сработает исключение процессора.
потому и выходит столь низкая скорость доступа, т.к. процедура выполняется многократно.
Процесс валят сотни тысяч раз, что бы прочитать что-либо ценное и длинное, каждый раз доставая кроху
Страницы