Об уязвимостях RIDL и воспроизведении нагрузок

Я пытаюсь понять класс уязвимости RIDL.

Это класс уязвимостей, который может считывать устаревшие данные из различных буферов микроархитектуры.
Сегодня используются известные уязвимости: LFB, порты загрузки, eMC и буфер хранилища.

Связанная статья в основном ориентирована на LFB.

Я не понимаю, почему ЦП удовлетворяет нагрузку устаревшими данными в LFB.
Я могу представить, что если нагрузка попадает в L1d, она внутренне «воспроизводится» до тех пор, пока L1d не внесет данные в LFB, сигнализируя об OoO core, чтобы остановить его "воспроизведение" (поскольку теперь считанные данные действительны).

Однако я не уверен, что на самом деле означает «воспроизведение».
Я думал, что нагрузки были отправлены на порт, поддерживающий загрузку, а затем записаны в буфер загрузки (в MOB) и в конечном итоге сохраняются по мере необходимости до тех пор, пока их данные не станут доступными. (как сигнализирует L1).
Поэтому я не уверен, как «воспроизведение» вступает в игру, кроме того, чтобы RIDL работал, каждая попытка «воспроизвести» загрузку должна также разблокировать зависимые инструкции.
Это мне кажется странным, поскольку ЦП должен отслеживать, какие инструкции следует воспроизвести после правильного завершения загрузки.

В документе о RIDL этот код используется в качестве примера (к сожалению, мне пришлось вставить его как изображение, поскольку макет PDF не позволял мне его скопировать):

Фрагмент RIDL

Единственная причина, по которой это может работать, заключается в том, что ЦП сначала удовлетворит нагрузку в строке 6 устаревшими данными, а затем воспроизведет их.
Кажется, это подтверждается несколькими строками ниже:

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

Но я бы ожидал, что ЦП проверит адрес загрузки перед пересылкой данных в LFB (или любой другой внутренний буфер).
Если ЦП не выполняет загрузку повторно до тех пор, пока не обнаружит, что загруженные данные теперь действительны (т. Е. воспроизведение).
Но, опять же, почему каждая попытка разблокирует зависимые инструкции?

Как именно работает механизм воспроизведения, если он вообще существует, и как он взаимодействует с уязвимостями RIDL?


person Margaret Bloom    schedule 17.05.2019    source источник
comment
Что такое eMC?   -  person Hadi Brais    schedule 18.05.2019
comment
@HadiBrais Embedded Memory Controller, по крайней мере, часть, подключенная к кольцевой шине.   -  person Margaret Bloom    schedule 18.05.2019
comment
Я не понимаю, почему здесь важен контроллер памяти. Таблица IV из документа RIDL показывает, какие аппаратные структуры вызывают какие уязвимости.   -  person Hadi Brais    schedule 18.05.2019
comment
@HadiBrais Мне тоже. Возможно, я неверно истолковал изображение на первой странице, где eMC выделен красным, как и другой источник данных об уязвимостях MDS.   -  person Margaret Bloom    schedule 19.05.2019
comment
Ах, это, наверное, ошибка. Из документов RIDL и Fallout ясно, что авторы (как и мы) не совсем понимают, что происходит.   -  person Hadi Brais    schedule 19.05.2019


Ответы (2)


Я не думаю, что повторы загрузки с RS причастны к атакам RIDL. Поэтому вместо того, чтобы объяснять, что такое повторы нагрузки (ответ @ Peter является хорошей отправной точкой для этого), я расскажу о том, что, по моему мнению, происходит, основываясь на моем понимании информации, представленной в документе RIDL, Intel анализ этих уязвимостей и соответствующих патентов. .

Буферы заполнения строк - это аппаратные структуры в кэше L1D, используемые для хранения запросов памяти, которые отсутствуют в кэше, и запросов ввода-вывода до тех пор, пока они не будут обработаны. Кэшируемый запрос обслуживается, когда требуемая строка кэша заполняется в массив данных L1D. Запись с объединением записи обслуживается, когда возникает какое-либо из условий удаления буфера объединения записи (как описано в руководстве). Запрос UC или ввода-вывода обслуживается, когда он отправляется в кэш L2 (что происходит при первой возможности).

См. Рисунок 4 в документе RIDL. Эксперимент, использованный для получения этих результатов, работает следующим образом:

  • Поток-жертва записывает известное значение в одну ячейку памяти. Тип ячейки памяти - WB, WT, WC или UC.
  • Поток-жертва циклически читает ту же самую ячейку памяти. За каждой операцией загрузки следует MFENCE и необязательный CLFLUSH. Из статьи мне неясен порядок CLFLUSH по отношению к двум другим инструкциям, но, вероятно, это не имеет значения. MFENCE сериализует операцию очистки строки кэша, чтобы увидеть, что происходит, когда каждая загрузка отсутствует в кэше. Кроме того, MFENCE уменьшает конкуренцию между двумя логическими ядрами на портах L1D, что увеличивает пропускную способность злоумышленника.
  • Поток злоумышленника, работающий на логическом ядре-близнеце, выполняет код, показанный в листинге 1, в цикле. Адрес, используемый в строке 6, может быть любым. Единственное, что имеет значение, это то, что загрузка в строке 6 либо дает сбой, либо вызывает обход страницы, требующий помощи микрокода (для установки бита доступа в записи таблицы страниц). Обход страницы также требует использования LFB, и большинство LFB совместно используются логическими ядрами.

Мне не ясно, что представляет собой ось Y на рисунке 4. Насколько я понимаю, он представляет собой количество строк из скрытого канала, которые были извлечены в иерархию кеша (строка 10) в секунду, где индекс строки в массиве равен значению, записанному жертвой.

Если ячейка памяти относится к типу WB, когда поток-жертва записывает известное значение в эту ячейку памяти, строка будет заполнена в кэш L1D. Если ячейка памяти относится к типу WT, когда поток-жертва записывает известное значение в эту ячейку памяти, строка не будет заполнена в кэш L1D. Однако при первом чтении из строки он будет заполнен. Таким образом, в обоих случаях и без CLFLUSH большинство загрузок из потока-жертвы попадут в кеш.

Когда строка кэша для запроса загрузки достигает кеша L1D, она сначала записывается в LFB, выделенный для запроса. Запрошенная часть строки кэша может быть напрямую передана в буфер загрузки из LFB, не дожидаясь заполнения строки в кэше. Согласно описанию уязвимости MFBDS, в определенных ситуациях устаревшие данные из предыдущих запросов могут быть перенаправлены в буфер загрузки, чтобы удовлетворить нагрузку uop. В случаях WB и WT (без сброса) данные жертвы записываются не более чем в 2 разных LFB. Переход страницы из потока злоумышленника может легко перезаписать данные жертвы в LFB, после чего эти данные никогда не будут найдены там потоком злоумышленника. Все запросы загрузки, попадающие в кэш L1D, не проходят через LFB; для них есть отдельный путь, который мультиплексируется с путем от LFB. Тем не менее, есть некоторые случаи, когда устаревшие данные (шум) от LFB спекулятивно перенаправляются в логическое ядро ​​злоумышленника, которое, вероятно, происходит из обхода страниц (и, возможно, обработчиков прерываний и аппаратных программ предварительной выборки).

Интересно отметить, что частота пересылки устаревших данных в случаях WB и WT намного ниже, чем во всех других случаях. Это можно объяснить тем, что пропускная способность жертвы в этих случаях намного выше, и эксперимент может закончиться раньше.

Во всех остальных случаях (WC, UC и все типы с промывкой) каждая загрузка отсутствует в кэше, и данные должны быть извлечены из основной памяти в буфер загрузки через LFB. Происходит следующая последовательность событий:

  1. Доступы от жертвы попадают в TLB, потому что они относятся к одной и той же действительной виртуальной странице. Физический адрес получается из TLB и предоставляется L1D, который выделяет LFB для запроса (из-за промаха), а физический адрес записывается в LFB вместе с другой информацией, описывающей запрос загрузки. На данный момент запрос от жертвы ожидает обработки в LFB. Поскольку жертва выполняет MFENCE после каждой загрузки, в LFB может быть не более одной невыполненной нагрузки в любом заданном цикле от жертвы.
  2. Злоумышленник, работающий на логическом ядре-близнеце, отправляет запрос загрузки на L1D и TLB. Каждая загрузка выполняется на несопоставленную пользовательскую страницу, поэтому это вызовет ошибку. Когда он отсутствует в TLB, MMU сообщает буферу загрузки, что загрузка должна быть заблокирована до завершения трансляции адреса. Согласно параграфу 26 патента и другим патентам Intel, именно так обрабатываются промахи TLB. Преобразование адреса еще продолжается, загрузка заблокирована.
  3. Запрос на загрузку от жертвы получает свою строку кэша, которая записывается в LFB, покрытый слоем загрузки. Часть строки, запрошенная загрузкой, пересылается в MOB, и в то же время строка записывается в кэш L1D. После этого с LFB можно снять покрытие, но ни одно из полей не очищается (кроме поля, которое указывает, что он свободен). В частности, данные все еще находятся в LFB. Затем жертва отправляет еще один запрос на загрузку, который также не попадает в кеш либо потому, что он не кэшируется, либо потому, что строка кеша была очищена.
  4. Процесс трансляции адресов загрузки злоумышленника завершается. MMU определяет, что необходимо вызвать ошибку, потому что физическая страница отсутствует. Однако неисправность не возникает до тех пор, пока нагрузка не выйдет из строя (когда она достигнет верха ROB). Недействительные переводы не кэшируются в MMU на процессорах Intel. MMU по-прежнему должен сообщить MOB, что преобразование завершено, и в этом случае устанавливает код ошибки в соответствующей записи ROB. Кажется, что когда ROB видит, что один из мопов имеет действительный код ошибки / помощи, он отключает все проверки, связанные с размерами и адресами этого мупа (и, возможно, всех последующих мопов в ROB). Эти проверки больше не имеют значения. Предположительно, отключение этих проверок экономит динамическое потребление энергии. Логика вывода из эксплуатации знает, что, когда нагрузка вот-вот спадет, неисправность все равно возникнет. В то же время, когда MOB информируется о завершении перевода, он, как обычно, воспроизводит нагрузку злоумышленника. Однако на этот раз в кэш L1D предоставляется недопустимый физический адрес. Обычно физический адрес необходимо сравнивать со всеми ожидающими в LFB запросами от одного и того же логического ядра, чтобы гарантировать, что логическое ядро ​​видит самые последние значения. Это делается до или параллельно с поиском кеша L1D. Физический адрес на самом деле не имеет значения, потому что логика сравнения отключена. Однако результаты всех сравнений ведут себя так, как будто результат указывает на успех. Если есть хотя бы один выделенный LFB, физический адрес будет соответствовать некоторому выделенному LFB. Поскольку есть невыполненный запрос от жертвы и поскольку секрет жертвы, возможно, уже был записан в том же LFB из предыдущих запросов, та же часть строки кэша, которая технически содержит устаревшие данные, и в этом случае (устаревшие данные - это секрет), будет перенаправлен злоумышленнику. Обратите внимание, что злоумышленник может контролировать смещение в строке кэша и количество байтов, которые нужно получить, но не может контролировать, какой LFB. Размер строки кэша составляет 64 байта, поэтому только 6 младших битов виртуального адреса нагрузки злоумышленника имеют значение вместе с размером нагрузки. Затем злоумышленник использует данные для индексации в своем массиве, чтобы раскрыть секрет, используя атаку по боковому каналу кеша. Это поведение также объясняет MSBDS, где, по-видимому, отключены проверки размера данных и STD uop (т. Е. Проверки проходят тривиально).
  5. Позже неисправная / вспомогательная нагрузка достигает вершины ROB. Нагрузка не снимается, и трубопровод промывается. В случае неисправной нагрузки возникает неисправность. В случае вспомогательной загрузки выполнение перезапускается с той же инструкции загрузки, но с помощью установки требуемых флагов в структурах подкачки.
  6. Эти шаги повторяются. Но злоумышленник не всегда может раскрыть секрет жертвы. Как видите, может случиться так, что запрос загрузки от злоумышленника попадет в выделенную запись LFB, содержащую секрет. LFB, выделенные для обходов страниц и аппаратной предварительной выборки, могут затруднить выполнение успешной атаки.

Если загрузка злоумышленника не сработала / не помогла, LFB получат действительный физический адрес от MMU, и будут выполнены все необходимые проверки на правильность. Вот почему нагрузка должна отказывать / помогать.

Следующая цитата из статьи обсуждает, как выполнить атаку RIDL в том же потоке:

мы выполняем атаку RIDL без SMT, записывая значения в наш собственный поток и наблюдая за значениями, которые мы утекаем из того же потока. На рисунке 3 показано, что если мы не записываем значения («без жертвы»), мы утекаем только нули, но когда жертва и злоумышленник работают в одном аппаратном потоке (например, в песочнице), мы утекаем секретное значение почти во всех случаях. .

Я думаю, что в этом эксперименте нет изменений уровня привилегий. Жертва и злоумышленник работают в одном потоке ОС на одном аппаратном потоке. При возврате от жертвы к злоумышленнику в LFB все еще могут быть некоторые невыполненные запросы (особенно из магазинов). Обратите внимание, что в статье RIDL KPTI включен во всех экспериментах (в отличие от статьи Fallout).

В дополнение к утечке данных из LFB, MLPDS показывает, что данные также могут быть утечкой из буферов порта загрузки. К ним относятся буферы с разделением строк и буферы, используемые для нагрузок размером более 8 байтов (которые, я думаю, необходимы, когда размер uop загрузки больше, чем размер порта загрузки, например, AVX 256b на SnB / IvB которые занимают порт на 2 цикла).

Случай WB (без промывки) на Рисунке 5 также интересен. В этом эксперименте поток жертвы записывает 4 разных значения в 4 разные строки кэша вместо чтения из одной и той же строки кэша. На рисунке показано, что в случае WB злоумышленнику попадают только данные, записанные в последнюю строку кэша. Объяснение может зависеть от того, отличаются ли строки кэша на разных итерациях цикла, что, к сожалению, не ясно в статье. В документе говорится:

Для WB без сброса есть сигнал только для последней строки кэша, который предполагает, что ЦП выполняет объединение записи в одной записи LFB перед сохранением данных в кэше.

Как можно объединить записи в разные строки кэша в одном LFB перед сохранением данных в кеше? В этом нет никакого смысла. LFB может содержать одну строку кэша и один физический адрес. Так совмещать записи просто невозможно. Что может происходить, так это то, что записи WB записываются в LFB, выделенные для их запросов RFO. Когда недопустимый физический адрес передается в LFB для сравнения, данные всегда могут быть предоставлены из LFB, который был назначен последним. Это могло бы объяснить, почему утекает только значение, записанное четвертым хранилищем.

Для получения информации о смягчении последствий MDS см .: Что представляют собой новые MDS-атаки и как их уменьшить?. В моем ответе обсуждаются только меры по снижению рисков, основанные на обновлении микрокода Intel (не очень интересные «программные последовательности»).


На следующем рисунке показаны уязвимые структуры, использующие спекуляцию данными.

введите здесь описание изображения

person Hadi Brais    schedule 18.05.2019
comment
Я не осознавал, читая газету, что нагрузка должна быть неисправной или вспомогательной. Вместо этого необходимо перечитать его. Мне нужно время, чтобы полностью перечитать / понять ваш ответ. Пожалуйста, проявите терпение :) - person Margaret Bloom; 18.05.2019
comment
Из любопытства правильно сказать, что зависимые мопы переигрываются из ROB? - person Margaret Bloom; 19.05.2019
comment
@MargaretBloom Это невозможно, потому что нет пути от ROB к портам выполнения. Uops можно воспроизвести только с RS или MOB. Мое текущее понимание состоит в том, что, когда загрузка uop отсутствует в LTB, она может быть воспроизведена из буфера загрузки в кеш L1D (не RS), когда физический адрес предоставляется буферу загрузки. С другой стороны, если виртуальный адрес загрузки сам по себе неверен (получен из uop по неверно предсказанному пути) или недоступен (uop загрузки был отправлен, когда планировщик предсказал, что виртуальный адрес будет доступен. . - person Hadi Brais; 19.05.2019
comment
... в обходной сети, но это не потому, что, например, мопы, которые предоставляют виртуальный адрес, пропущены в кэше L1D), затем должен быть отправлен моп загрузки и пройти через весь канал загрузки (через свободную сеть, тонкую сеть , и MMU). В противном случае, если виртуальный адрес известен, нет необходимости проходить через конвейер загрузки и без необходимости использовать порт выполнения загрузки. Лучше было бы найти патенты, подтверждающие все это. В случае сброса машины загрузочный uop повторно выдается из распределителя. - person Hadi Brais; 19.05.2019
comment
Я имел ввиду из ROB - ›Планировщик -› Порты выполнения. Нагрузка может оставаться в буфере загрузки, но как насчет последующих зависимых мопов? Думаю, они должны оставаться в планировщике. Таким образом, планировщик не может просто удалить их после отправки. Следует знать, что они зависят от нагрузки, которая, возможно, использует спекулятивные данные. Разве это не дублирование функций ROB? - person Margaret Bloom; 19.05.2019
comment
@MargaretBloom: Я не думаю, что есть какой-либо механизм для копирования uop из ROB в планировщик. После того, как он ушел из RS, он не возвращается, кроме сброса конвейера в более раннее состояние, что приводит к его повторной выдаче интерфейсом. Насколько я понимаю, мопы не могут быть удалены из RS до тех пор, пока они не будут завершены успешно, а не просто отправлены. Хади, вы говорите, что MOB позволяет планировщику узнать, когда UOP загрузки может быть повторно отправлен (например, когда данные поступают из внешнего ядра)? Конечно, планировщик по-прежнему должен отслеживать свои зависимости и не выдавать еще один uop на этот порт. - person Peter Cordes; 19.05.2019
comment
@MargaretBloom: Разве это не дублирование функций ROB? Не совсем; ROB не отслеживает зависимости, а зависит только от того, завершили ли выполнение uop (или все uop, составляющие всю инструкцию). Предположительно, это занимает 1 бит на запись, в то время как RS сбрасывает ошибки, для которых это так. ROB, по-видимому, является кольцевым буфером, при этом удаление непрерывных завершенных мопов в хвосте (и освобождение любых дополнительных ресурсов, выделенных для мупа, например, загрузки буфера для проверки неверного спекулятивного порядка памяти), а также проблема добавления мопов в голове. - person Peter Cordes; 19.05.2019
comment
@Hadi: почему это предположение делается только для нагрузок, которые вызовут сбой / помощь? Мое предположение: это, вероятно, всегда происходит, но если обнаруживается сбой, то порт загрузки просто сбрасывает все и движется дальше (для экономии энергии), при этом выходной буфер сохраняет все, что он делал в то время. Неисправные нагрузки генерируют фактические входные данные для мультиплексоров, которые подают в выходной буфер результата загрузки либо LFB, L1d, либо переадресацию памяти. Опять же, это полное предположение; дизайн, который звучит правдоподобно и объясняет наблюдения, учитывая то немногое, что я знаю о проектировании логики ЦП. - person Peter Cordes; 19.05.2019
comment
@MargaretBloom и Питер, фундаментальное различие между ROB и RS состоит в том, что ROB является кольцевым буфером и, следовательно, эффективно поддерживает порядок программ. RS не может эффективно определять порядок программ. Если ROB не было, RS должен каждый цикл проверять порядок всех мопов, чтобы определить, готов ли самый старый к списанию. Это явно слишком неэффективно. ROB существует в основном для этой цели. Конечно, есть много других различий, таких как ROB поддерживает другую информацию, а записи RS могут быть освобождены раньше, но это не принципиальные различия. - person Hadi Brais; 19.05.2019
comment
Эффективен с точки зрения производительности и мощности. Но да, есть поля, которые дублируются как в RS, так и в ROB, так что они могут быть доступны для обоих без конкуренции. - person Hadi Brais; 19.05.2019
comment
@MargaretBloom Что касается воспроизведения, я вернулся, чтобы освежить свои знания о патентах Intel на воспроизведение (их много). Существует 4 различных типа воспроизведения: (1) воспроизведение из RS, когда планировщик неверно предсказывает время прибытия операнда в сеть пересылки (2) воспроизведение из MOB, которое происходит, когда отсутствует доступ в TLB (3) частичное воспроизведение из кэш uop, который возникает, когда uop завершил выполнение или выполняется с неправильными операндами (4) полное воспроизведение, которое является сбросом конвейера. Очевидно, может быть несколько одновременных повторов одного и того же мупа. Как это круто - person Hadi Brais; 19.05.2019
comment
@PeterCordes и HadiBrais, спасибо за ваши ответы. - person Margaret Bloom; 20.05.2019
comment
@HadiBrais: Однако эти определения повтора включают в себя переиздание. Я не понимаю, как воспроизводить из кеша uop, это не имеет для меня смысла, потому что я думал, что единственный путь от кеша uop к RS / исполнительным блокам был через обычный интерфейс IDQ + issue + переименование. О, частичное воспроизведение может означать пропуск перехода с быстрым восстановлением, которое отбрасывает все мопы после ошибочно предсказанного перехода и выдает правильный путь. Он может повторно присоединиться к пути, который уже был в RS, но, возможно, с другой цепочкой dep, поэтому IDK, если бы я назвал это тем же uop. Это другой экземпляр той же инструкции x86. - person Peter Cordes; 20.05.2019
comment
@PeterCordes Я не читал досконально патенты, поэтому у меня нет полной картины. Также разные патенты могут применяться к разным микроархитектурам. Думаю на ветке неверный прогноз, полное реле происходит. Частичное воспроизведение относится к воспроизведению выбранных мопов, которые не обязательно должны быть смежными в программном порядке. Хотя возможно, что он воспроизводит сообщения RS, не освобождая запись RS, когда один из операндов предоставляется спекулятивно (спекулятивная переадресация хранилища). Запись RS освобождается только в том случае, если все операнды uop не являются спекулятивными. Матрица нагрузки может позволить такую ​​реализацию. - person Hadi Brais; 20.05.2019
comment
@MargaretBloom Я переписал большую часть ответа в виде последовательности событий для большей ясности. Я также добавил возможное объяснение того, почему нагрузка злоумышленника должна отказывать / помогать. Сообщите мне, имеет ли это какой-либо смысл. - person Hadi Brais; 20.05.2019
comment
Спасибо @HadiBrais, единственное, что мне кажется странным: в то же время, когда MOB сообщает, что перевод завершен, он, как обычно, воспроизводит нагрузку злоумышленника. но в это время загрузка злоумышленника еще не выполнялась, если я правильно следил за пунктами. Боковое примечание: у меня создалось впечатление, что планировщик отправляет нагрузку и зависимые мопы, предполагая, что нагрузка попадет в L1. Т.е. он заставит зависимые мопы получать входные данные из сети обратной / прямой записи ... - person Margaret Bloom; 20.05.2019
comment
Эта сеть представляет собой мультиплексор, питаемый от LFB, L1d и регистров разделения (по крайней мере), реальный источник выбран правильно, если физический адрес известен к тому времени, когда зависимые мопы читают из него (благодаря попаданию TLB). Но если физический адрес отсутствует (отсутствует TLB или отсутствует PTE) или происходит сбой при загрузке (это сэкономит энергию), сеть повторно использует последнюю использованную конфигурацию, что приводит к утечке данных. Если в загрузке произошел сбой, она не воспроизводится, если ей помогают, она будет воспроизведена, когда MMU сигнализирует MOB, что у него есть phy-адрес. - person Margaret Bloom; 20.05.2019
comment
Тем не менее, эта модель отличается от пунктов 2 и 5, и у вас есть патент, подтверждающий их, так что я думаю, ваша модель правильная. - person Margaret Bloom; 20.05.2019
comment
@MargaretBloom В терминологии, которую я использую, отправка виртуального адреса загрузки в TLB - это один из этапов выполнения uop загрузки. Воспроизведение включает повторное выполнение тех шагов, которые уже были выполнены; это не обязательно означает полное повторное исполнение. Что касается другого комментария, я думаю, что должен быть запрос к L1D для получения каких-либо данных; Я имею в виду, что (устаревшие) данные не могут сами по себе появиться в сети пересылки без запроса. Вот почему я думаю, что MOB не волнует, произошла ли ошибка, и он все равно просто воспроизведет загрузку и получит устаревшие данные. - person Hadi Brais; 20.05.2019
comment
Meltdown аналогичен, за исключением того, что злоумышленник загружается с адреса ядра с допустимым переводом. MOB не заботится о том, что проверка привилегий не удалась, но в этом случае загрузка будет продолжаться нормально с действительным физическим адресом. В RIDL кажется, что используется недействительный физический адрес, а фактический физический адрес предоставляется только после обработки ошибки / помощи. - person Hadi Brais; 20.05.2019
comment
@Margaret, FWIW, мое впечатление о том, как это работает, точно такое же, как ваш комментарий и следующий опишите. То есть основной механизм заключается в том, что зависимые мопы нагрузки злоумышленника предполагают, что результат загрузки появится в обходной сети с наилучшим возможным временем (попадание L1), и используют любые значения, которые они там находят, даже если попадания не было. , который в конечном итоге оказывается тем, что было подключено к обходной сети последним, что использовало этот порт / обходной путь. - person BeeOnRope; 10.04.2020
comment
Если злоумышленник попадает в нагрузку, это, конечно, означает, что там нужные данные. Если нагрузка злоумышленника отсутствует в L1 (но не ошибка / TLB промах), обходная сеть, по-видимому, все еще содержит что-то, но не утечку секрета из LFB (при условии, что они пытались это сделать). Или, может быть, в этом случае он действительно устареет из LFB, но восстановление происходит достаточно быстро, чтобы не допустить утечки последующим зондом. - person BeeOnRope; 10.04.2020

replay = повторная отправка из RS (планировщик). (Это не полный ответ на весь ваш вопрос, только часть о том, что такое повторы. Хотя я думаю, что это покрывает большую часть этого, включая разблокировку зависимых мопов.)

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

См. обсуждение в чате - мопы, зависящие от разделенной загрузки или кеш-промаха, воспроизводятся, но не загрузки сам. (Если только нагрузка не зависит от самой себя в цикле, как я делал для тестирования ›.‹). ТОДО: исправьте остальную часть этого и других ответов.


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

Таким образом, промах L1 / попадание в L2 приведет к двукратному увеличению числа отправленных мопов нагрузки. (Планировщик оптимистичен, а L2 находится в ядре, поэтому ожидаемая задержка попадания L2 фиксирована, в отличие от времени для ответа вне ядра. IDK, если планировщик продолжает оптимистично относиться к данным, поступающим в определенное время из L3. )


Документ RIDL предоставляет некоторые интересные доказательства того, что мопы загрузки действительно напрямую взаимодействуют с LFB, не дожидаясь, пока входящие данные будут помещены в L1d, и просто читают их оттуда.


Мы можем наблюдать повторы на практике легче всего для загрузок с разделением строк кэша, потому что повторение этого даже более тривиально, чем промахи кеша, поскольку требует меньше кода. Счетчики для uops_dispatched_port.port_2 и port_3 будут примерно вдвое больше для цикла, который только разделяет нагрузки. (Я проверил это на практике на Skylake, используя по существу тот же цикл и процедуру тестирования, что и в Как я могу точно измерить скорость невыровненного доступа на x86_64)

Вместо того, чтобы сигнализировать об успешном завершении обратно в RS, загрузка, которая обнаруживает разделение (возможно только после вычисления адреса), выполнит загрузку для первой части данных, поместив этот результат в буфер разделения 1 для соединения с данными из 2-й строки кэша при второй отправке uop. (Предполагая, что ни одно время не является промахом в кэше, иначе для этого тоже потребуются повторы.)


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

Если этого не произошло (потому что данные загрузки не были готовы), зависимые мопы также должны быть воспроизведены. Опять же, IIRC это наблюдается с помощью счетчиков perf для dispatch to ports.


Существующие вопросы и ответы с доказательствами повторов UOP на процессорах Intel:


Сноска 1:

Мы знаем, что количество разделенных буферов ограничено; есть ld_blocks.no_sr счетчик для грузов, которые останавливаются из-за его отсутствия. Я предполагаю, что они находятся в порту загрузки, потому что это имеет смысл. Повторная отправка того же загрузочного uop отправит его на тот же порт загрузки, потому что uop назначается портам во время выдачи / переименования. Хотя, может быть, есть общий пул разделенных буферов.


RIDL:

Оптимистичное планирование - это часть механизма, создающего проблему. Более очевидная проблема заключается в том, чтобы позволить выполнению более поздних мопов увидеть внутреннее значение мусора из LFB, как в Meltdown.

http://blog.stuffedcow.net/2018/05/meltdown-microarchitecture/ даже показывает, что сбои в загрузке в PPro раскрывают различные части микроархитектурного состояния, точно так же, как эта уязвимость, которая все еще существует в последних процессорах.

Pentium Pro буквально понимает «значение нагрузки - это безразлично». Для всех запрещенных загрузок блок загрузки завершает работу и выдает значение, и это значение оказывается различными значениями, взятыми из различных частей процессора. Значение варьируется и может быть недетерминированным. Ни одно из возвращенных значений не является данными памяти, поэтому Pentium Pro не кажется уязвимым для Meltdown.

Распознаваемые значения включают PTE для загрузки (которая, по крайней мере в последние годы, сама считается привилегированной информацией), 12-е самое последнее сохраненное значение (очередь магазина имеет 12 записей) и, в редких случаях, дескриптор сегмента откуда-то. .

(Более поздние процессоры, начиная с Core 2, открывают значение из кэша L1d; это сама уязвимость Meltdown. Но PPro / PII / PIII не уязвимы для Meltdown. Очевидно, уязвимы для RIDL-атак. в этом случае вместо этого.)

Таким образом, это та же философия дизайна Intel, которая подвергает части микроархитектурного состояния спекулятивному исполнению.

Обнуление аппаратного обеспечения должно быть простым решением; порт загрузки уже знает, что это не удалось, поэтому маскирование данных загрузки в соответствии с успехом / неудачей должно, надеюсь, только добавить пару дополнительных задержек шлюза и быть возможным без ограничения тактовой частоты. (Если только последняя стадия конвейера в порту загрузки уже не была критическим путем для частоты процессора.)

Вероятно, это простое и дешевое решение для аппаратного обеспечения будущих ЦП, но его очень сложно устранить с помощью микрокода и программного обеспечения для существующих ЦП.

person Peter Cordes    schedule 17.05.2019
comment
Значит, зависимый uop будет храниться в RS до тех пор, пока загрузка не будет помечена как успешно завершенная? По сути, каждый uop имеет бит успешно выполненного действия, который действителен, если он установлен в самом uop и во всех предыдущих uop (что легко проверить, поскольку RS заполняется по порядку). Так что в RIDL виноват оптимистичный характер планировщика. - person Margaret Bloom; 17.05.2019
comment
@MargaretBloom: каждый муп остается в RS до тех пор, пока сам не будет успешно выполнен. После успешного выполнения uop удаляется из RS, полностью освобождая место для новых. (Но да, ROB будет немного для отслеживания выполнения, то есть готового к удалению, если / когда вывод пройдёт через все предыдущие успешно выполненные uop. Проверка предыдущего статуса uop, вероятно, не произойдет до вывода на пенсию.) Даже обнаружение промаха ветки невозможно. Проблема: все ошибки, полученные после ошибочного предположения, в любом случае отбрасываются из ROB + RS, а правильный путь вводится из выдачи / переименования. - person Peter Cordes; 17.05.2019
comment
@MargaretBloom: обновил свой ответ, переписав мой второй (теперь удаленный) комментарий. - person Peter Cordes; 17.05.2019
comment
Intel выпускает обновление ucode с новой командой (или инструкцией), которая будет использоваться для очистки всего буфера uarch при переключении привилегированного контекста. Так что, возможно, уменьшение значения нагрузки до 0 не всегда возможно (например, в случае промаха TLB?), Или это исправление будет выпущено для новых поколений. - person Margaret Bloom; 18.05.2019
comment
@MargaretBloom: Как я уже сказал в своем ответе, это должно быть легко исправить, изменив конструкцию оборудования с фиксированной функцией (новый uarch), но не просто обновив микрокод для существующих аппаратное обеспечение. ЦП - это не ПЛИС, которые можно произвольно перенастроить с помощью микрокода; микрокод может использовать только существующие хуки, предоставленные HW, например, отключить или очистить материал. (например, микрокод Skylake отключает буфер цикла, а не просто исправляет ошибку, потому что разработчики сделали это возможным для ucode, предположительно в случае обнаружения такой ошибки. Ошибки P5 FDIV и F00F внушали осторожность!) - person Peter Cordes; 18.05.2019
comment
По-видимому, очистка затронутых буферов является существующей ловушкой, поскольку это то, что Intel выпускает для существующих процессоров. Так что последняя фраза кажется не совсем правильной. - person Margaret Bloom; 18.05.2019
comment
@MargaretBloom: по-видимому, для этого потребуется несколько мопов; Я не совсем удивлен, что они смогли сколотить последовательность микрокода, которая устанавливает внутреннее состояние на безопасные значения, возможно, злоупотребляя мопами, предназначенными для чего-то другого. Как, может быть, сохранение нулей делает какой-то фиктивный адрес, который на самом деле никуда не записывается в физическую память? Во всяком случае, последний абзац моего ответа все еще в силе; новый MSR для запуска ручного сброса не очень поможет защитить песочницу Javascript от webasm изнутри того же процесса: нет переключения контекста. - person Peter Cordes; 18.05.2019
comment
(Новая инструкция, добавленная для защиты от микрокода Spectre, на самом деле представляет собой новый MSR, в который вы можете писать. Это единственный прием, который есть у Intel для добавления совершенно новых вещей в микрокод, потому что фактические декодеры частично являются фиксированными функциями; было бы невозможно добавить фактическую новую инструкцию со своим собственным кодом операции. Но запись / чтение MSR - это в основном перехватчики в микрокод с номером вызова и одним входным или выходным аргументом. Можно с уверенностью предположить, что новый перехватчик для этого также будет записью MSR.) - person Peter Cordes; 18.05.2019
comment
Действительно, очень верно! Кстати, спасибо за добавленную информацию о RIDL. - person Margaret Bloom; 18.05.2019
comment
@MargaretBloom и Питер, обновление микрокода усиливает поведение инструкции VERW, так что она декодируется во многие другие ошибки. Эти дополнительные операции - это загрузка памяти и сохранение операций, которые просто перезаписывают все буферы, затронутые MDS, некоторым безопасным значением (например, нулем). Они эквивалентны программным последовательностям, показанным Intel, которые могут использоваться для процессоров без обновления микрокода. VERW всегда был микрокодирован на всех поддерживающих его процессорах. Таким образом, обновление (среди прочего) изменяет процедуру микрокода VERW и больше ничего не меняет. - person Hadi Brais; 18.05.2019
comment
@HadiBrais: ах, так что это делает его доступным для пользовательского пространства, в отличие от добавления другого MSR. Аккуратный. - person Peter Cordes; 18.05.2019
comment
Я помню, как обсуждал с вами разделенные регистры строки кэша в каком-то разделе комментариев на Stack Overflow, но я не могу его найти. Ты помнишь где? - person Hadi Brais; 18.05.2019
comment
@HadiBrais: нет, и Google не находит. Иногда site:stackoverflow.com "cordes" ... находит обсуждения, которые у меня были в комментариях, но я либо пропустил их, либо погуглил, их не было в результатах. - person Peter Cordes; 18.05.2019