Для предотвращения раскрытия памяти в ядре или в межпроцессном режиме ( атака Spectre ) ядро Linux 1 будет скомпилировано с новой опцией , -mindirect-branch=thunk-extern
введенной gcc
для выполнения косвенных вызовов через так называемый retpoline .
Похоже, что это новый изобретенный термин, поскольку поиск в Google обнаруживает только очень недавнее использование (как правило, все в 2018 году).
Что такое ретполин и как он предотвращает недавние атаки по раскрытию информации ядра?
1 Однако, это не специфично для Linux - похоже, что аналогичная или идентичная конструкция используется как часть стратегии смягчения в других ОС.
security
assembly
x86
cpu-architecture
BeeOnRope
источник
источник
gcc
указывает на это! Я не узнал lkml.org/lkml/2018/1/3/780 как на сайте списка рассылки ядра Linux, даже когда я туда заглянул (и мне предоставили снимок, так как он был отключен).Ответы:
В статье, упомянутой sgbj в комментариях, написанных Полом Тернером из Google, гораздо более подробно объясняется следующее, но я вкратце расскажу об этом:
Насколько я могу собрать это воедино из ограниченной информации на данный момент, ретполин является возвратным батутом, который использует бесконечный цикл, который никогда не выполняется, чтобы не допустить спекуляции ЦП о цели косвенного прыжка.
Базовый подход можно увидеть в ветке ядра Энди Клин, решающей эту проблему:
Он вводит новый
__x86.indirect_thunk
вызов, который загружает цель вызова, чей адрес памяти (который я буду называтьADDR
) хранится в верхней части стека, и выполняет переход с помощьюRET
инструкции. Затем сам блок вызывается с помощью макроса NOSPEC_JMP / CALL , который использовался для замены многих (если не всех) косвенных вызовов и переходов. Макрос просто помещает цель вызова в стек и корректно устанавливает адрес возврата, если необходимо (обратите внимание на нелинейный поток управления):Размещение
call
в конце необходимо, чтобы, когда косвенный вызов завершился, поток управления продолжался за использованиемNOSPEC_CALL
макроса, поэтому его можно было использовать вместо обычногоcall
Сам Thunk выглядит следующим образом:
Поток управления может немного запутаться, поэтому позвольте мне уточнить:
call
выталкивает текущий указатель инструкции (метка 2) в стек.lea
добавляет 8 к указателю стека , фактически отбрасывая последнее введенное четырехзначное слово, которое является последним адресом возврата (для метки 2). После этого вершина стека снова указывает на реальный адрес возврата ADDR.ret
переходит*ADDR
и сбрасывает указатель стека на начало стека вызовов.В конце концов, все это поведение практически эквивалентно прыгать прямо
*ADDR
. Единственное преимущество, которое мы получаем, заключается в том, что предиктор ветвления, используемый для операторов возврата (Return Stack Buffer, RSB), при выполненииcall
инструкции предполагает, что соответствующийret
оператор перейдет к метке 2.Часть после метки 2 фактически никогда не выполняется, это просто бесконечный цикл, который теоретически заполнил бы конвейер
JMP
инструкций инструкциями. При использованииLFENCE
,PAUSE
или в более общем случае инструкции в результате чего конвейера команд будет срыв останавливает процессор от тратя сил и времени на этом спекулятивном выполнении. Это потому, что в случае, если вызов retpoline_call_target будет возвращаться нормально, этоLFENCE
будет следующая инструкция, которая будет выполнена. Это также то, что предсказатель ветви будет предсказывать на основе исходного адреса возврата (метка 2).Цитировать из руководства по архитектуре Intel:
Однако обратите внимание, что в спецификации никогда не упоминается, что LFENCE и PAUSE приводят к остановке конвейера, поэтому я читаю немного между строк здесь.
Теперь вернемся к исходному вопросу: раскрытие информации о памяти ядра возможно благодаря комбинации двух идей:
Даже если спекулятивное выполнение должно быть свободным от побочных эффектов, если спекуляция была неправильной, спекулятивное выполнение по-прежнему влияет на иерархию кэша . Это означает, что, когда загрузка памяти выполняется спекулятивно, это все равно могло привести к удалению строки кэша. Это изменение в иерархии кеша можно определить путем тщательного измерения времени доступа к памяти, которая отображается на тот же набор кеша.
Вы даже можете потерять некоторые биты произвольной памяти, когда адрес источника чтения памяти сам считывался из памяти ядра.
Косвенный предсказатель ветвления процессоров Intel использует только самые младшие 12 битов исходной команды, поэтому легко отравить все 2 ^ 12 возможных историй предсказания с адресами памяти, управляемыми пользователем. Затем они могут, когда в ядре прогнозируется косвенный переход, спекулятивно выполняться с привилегиями ядра. Используя побочный канал тайминга кеша, вы можете утечь произвольную память ядра.
ОБНОВЛЕНИЕ: В списке рассылки ядра идет постоянное обсуждение, которое наводит меня на мысль, что retpolines не полностью смягчают проблемы предсказания ветвлений, например, когда буфер возврата стека (RSB) работает пусто, более поздние архитектуры Intel (Skylake +) отступают. в уязвимый целевой буфер филиала (BTB):
источник
push
/ret
что делает не разбалансировать обратный адрес стека предсказателя. Есть один неверный прогноз (переход к тому, что используетсяlfence
до фактического обратного адреса), но использованиеcall
модификации +rsp
уравновешивает этоret
.push
/ret
(в моем последнем комментарии). re: ваше редактирование: потеря значения RSB должна быть невозможной, потому что ретполин включает в себяcall
. Если бы приоритетное ядро переключало контекст там, мы возобновили бы выполнение с RSB, начатым изcall
в планировщик. Но, возможно, обработчик прерываний может закончиться с достаточным количествомret
s, чтобы очистить RSB.Retpoline предназначен для защиты от целевой ветви инъекции ( CVE-2017-5715 ) эксплуатировать. Это атака, когда косвенная инструкция ветвления в ядре используется для принудительного выполнения произвольного фрагмента кода. Выбранный код является «гаджетом», который так или иначе полезен для злоумышленника. Например, можно выбрать код, чтобы утечка данных ядра влияла на кеш. Retpoline предотвращает этот эксплойт, просто заменяя все косвенные инструкции ветвления инструкцией возврата.
Я думаю, что ключ к retpoline - это просто часть «ret», которая заменяет косвенную ветвь инструкцией возврата, так что CPU использует предиктор стека возврата вместо эксплуатируемого предиктора ветвления. Если вместо этого использовать простую команду push и return, то код, который будет спекулятивно выполняться, будет кодом, в который функция в конечном итоге вернется, а не каким-нибудь гаджетом, полезным для злоумышленника. Кажется, что главное преимущество батутной части заключается в поддержании стека возврата, поэтому, когда функция действительно возвращается к своему вызывающему, это предсказывается правильно.
Основная идея внедрения целевой ветви проста. Он использует тот факт, что процессор не записывает полный адрес источника и места назначения ветвей в своих целевых буферах ветвей. Таким образом, злоумышленник может заполнить буфер, используя переходы в своем собственном адресном пространстве, что приведет к предсказанным попаданиям при выполнении определенного косвенного перехода в адресном пространстве ядра.
Обратите внимание, что retpoline не предотвращает раскрытие информации о ядре напрямую, а только предотвращает использование косвенных инструкций ветвления для спекулятивного выполнения гаджета, который раскрыл бы информацию. Если злоумышленник может найти другие способы для умозрительного выполнения гаджета, то ретполина не предотвратит атаку.
В статье « Атаки призрака: использование спекулятивного исполнения » Пола Кохера, Даниэля Генкина, Даниэля Грусса, Вернера Хааса, Майка Гамбурга, Морица Липпа, Стефана Мангарда, Томаса Прешера, Майкла Шварца и Ювала Ярома дается следующий обзор того, как могут использоваться косвенные ветви. :
Запись в блоге, озаглавленная « Чтение привилегированной памяти с побочным каналом» командой Project Zero в Google, представляет еще один пример того, как внедрение целевого объекта ветвления можно использовать для создания рабочего эксплойта.
источник
Этот вопрос был задан некоторое время назад, и заслуживает более нового ответа.
Резюме :
Последовательности «Retpoline» - это программная конструкция, которая позволяет изолировать непрямые ветви от спекулятивного исполнения. Это может применяться для защиты чувствительных двоичных файлов (таких как реализации операционной системы или гипервизора) от атак внедрения целевых ветвей против их косвенных ветвей.
Слово « RET Полине » является контаминация слов «возвращение» и «батут», так же, как улучшение « отн Полине » был придуман от «относительного вызова» и «трамплин». Это батутная конструкция, построенная с использованием операций возврата, которая также образно гарантирует, что любое связанное с ней умозрительное выполнение будет бесконечно «подпрыгивать».
Использование этого параметра компилятора защищает только от Spectre V2 в уязвимых процессорах, для которых требуется обновление микрокода, необходимое для CVE-2017-5715. Он будет « работать » с любым кодом (не только с ядром), но стоит атаковать только код, содержащий «секреты».
LLVM компилятор имел
-mretpoline
переключатель , так как до 4 января 2018 года . Это дата, когда уязвимость была впервые опубликована . GCC выпустила свои патчи 7 января 2018 года.Дата CVE предполагает, что уязвимость была « обнаружена » в 2017 году, но она затрагивает некоторые процессоры, изготовленные в последние два десятилетия (таким образом, она, вероятно, была обнаружена давно).
Сначала несколько определений:
Батут - иногда называемые косвенными векторами прыжков батуты - это области памяти, в которых хранятся адреса, указывающие на процедуры обработки прерываний, процедуры ввода-вывода и т. Д. Выполнение прыгает в батут, а затем сразу же выпрыгивает или подпрыгивает, отсюда и термин батут. GCC традиционно поддерживает вложенные функции, создавая исполняемый батут во время выполнения, когда берется адрес вложенной функции. Это небольшой фрагмент кода, который обычно находится в стеке, в кадре стека содержащей функции. Батут загружает статический цепной регистр, а затем переходит к реальному адресу вложенной функции.
Thunk - Thunk - это подпрограмма, используемая для добавления дополнительных вычислений в другую подпрограмму. Thunks в основном используются для задержки вычисления до тех пор, пока не понадобится его результат, или для вставки операций в начале или конце другой подпрограммы.
Memoization - запоминаемая функция «запоминает» результаты, соответствующие некоторому набору определенных входных данных. Последующие вызовы с запомненными входами возвращают запомненный результат, а не пересчитывают его, тем самым устраняя первичную стоимость вызова с заданными параметрами из всех, кроме первого вызова функции с этими параметрами.
Грубо говоря, ретполин - это батут с возвратом в качестве снаряда , чтобы « испортить » запоминание в косвенном предсказателе ветвления.
Источник : retpoline включает инструкцию PAUSE для Intel, но инструкция AMD LFENCE необходима для AMD, так как на этом процессоре инструкция PAUSE не является командой сериализации, поэтому цикл pause / jmp будет использовать избыточную мощность, так как предполагается, что он ожидает возврата неправильно прогнозировать правильную цель.
У Arstechnica есть простое объяснение проблемы:
Из статьи Intel: « Ретполин: снижение уровня инъекций для целевого сектора » ( .PDF ):
источник