Що таке реполінія і як вона працює?


244

Для пом'якшення ядра або розкриття пам’яті між процесами ( атака Spectre ) ядро 1 Linux буде складено з новою опцією , -mindirect-branch=thunk-externвведеною gccдля здійснення непрямих викликів через так звану реполінію .

Це, здається, щойно придуманий термін, оскільки пошук у Google виявляється лише зовсім недавнього використання (як правило, у 2018 році).

Що таке реполінія та як запобігає останнім атакам на розкриття інформації про ядро?


1 Це не специфічно для Linux - схожа чи однакова конструкція, здається, використовується як частина стратегій пом'якшення наслідків на інших ОС.


6
Цікава стаття підтримки від Google.
sgbj

2
о, так це вимовляється / ˌtræmpəˈlin / (американський) або / ˈtræmpəˌliːn / (британський)
Walter Tross

2
Ви можете згадати, що це ядро Linux , хоча gccвказує саме так! Я не розпізнав lkml.org/lkml/2018/1/3/780 як на веб-сайті Linux Kernel Mailing List, жодного разу не заглядав туди (і мені було зроблено знімок, коли він був офлайн).
PJTraill

@PJTraill - додав тег ядра Linux
RichVel

@PJTraill - хороший момент, я оновив текст запитання. Зауважте, що я побачив це спочатку в ядрі Linux через відносно відкритий процес розробки, але, без сумніву, використовуються ті самі чи подібні методи в якості пом'якшення по всьому спектру відкритих і закритих джерел ОС. Тому я не вважаю це специфічним для Linux, але посилання, безумовно, є.
BeeOnRope

Відповіді:


158

Стаття, згадана sgbj в коментарях, написаних Павлом Тернером від Google, пояснює наступне значно детальніше, але я спробую це зробити:

Наскільки я можу скласти це разом з обмеженою інформацією на даний момент, ретрансляція - це зворотний батут, який використовує нескінченний цикл, який ніколи не виконується для запобігання процесору міркувати про ціль непрямого стрибка.

Основний підхід можна побачити у відділенні ядра Andi Kleen, що стосується цього питання:

Він вводить новий __x86.indirect_thunkвиклик, який завантажує ціль виклику, адреса пам'яті (яку я зателефоную ADDR) зберігається вгорі стека і виконує стрибок, використовуючи RETінструкцію. Потім сам виклик викликається за допомогою макросу NOSPEC_JMP / CALL , який використовувався для заміни багатьох (якщо не всіх) непрямих викликів та стрибків. Макрос просто розміщує ціль виклику на стеці і при необхідності правильно встановлює зворотну адресу (зверніть увагу на нелінійний потік управління):

.macro NOSPEC_CALL target
    jmp     1221f            /* jumps to the end of the macro */
1222:
    push    \target          /* pushes ADDR to the stack */
    jmp __x86.indirect_thunk /* executes the indirect jump */
1221:
    call    1222b            /* pushes the return address to the stack */
.endm

Розміщення callв кінці є необхідним, щоб коли непрямий виклик закінчувався, потік управління продовжується позаду використання NOSPEC_CALLмакросу, щоб його можна було використовувати замість звичайногоcall

Сам грона виглядає так:

    call retpoline_call_target
2:
    lfence /* stop speculation */
    jmp 2b
retpoline_call_target:
    lea 8(%rsp), %rsp 
    ret

Потік управління тут може трохи заплутатися, тому дозвольте мені уточнити:

  • call натискає поточний покажчик інструкцій (мітка 2) до стеку.
  • leaдодає 8 до покажчика стека , ефективно відкидаючи останнє натиснуте чотирислівне, що є останньою зворотною адресою (до мітки 2). Після цього вершина стека знову вказує на реальну адресу повернення ADDR.
  • retпереходить до *ADDRта скидає вказівник стека на початок стеку викликів.

Зрештою, вся ця поведінка практично еквівалентна стрибкам безпосередньо *ADDR. Одна з переваг, яку ми отримуємо, полягає в тому, що передбачувач гілки, який використовується для операторів повернення (Buffer Return Stack, RSB), виконуючи callінструкцію, передбачає, що відповідне retтвердження перейде до мітки 2.

Частина після мітки 2 насправді ніколи не виконується, це просто нескінченний цикл, який теоретично заповнив би конвеєр JMPінструкцій інструкціями. При використанні LFENCE, PAUSEабо в більш загальному випадку інструкції в результаті чого конвеєра команд буде зрив зупиняє процесор від витрачаючи сил і часу на цьому спекулятивному виконанні. Це тому, що у випадку, якщо виклик retpoline_call_target повернеться нормально, LFENCEнаступна інструкція повинна бути виконана. Це також те, що прогнозує галузевий предиктор на основі вихідної адреси повернення (мітка 2)

Цитувати з посібника з архітектури Intel:

Інструкції, що слідують за LFENCE, можуть бути вилучені з пам'яті перед LFENCE, але вони не виконуватимуться, поки LFENCE не завершиться.

Однак зауважте, що в специфікації ніколи не згадується, що LFENCE і PAUSE викликають зупинку трубопроводу, тому я читаю тут трохи між рядками.

Повернімось до вашого початкового запитання: Розкриття інформації пам'яті ядра можливо завдяки поєднанню двох ідей:

  • Незважаючи на те, що спекулятивне виконання повинно бути вільним від побічних ефектів, коли спекуляція була неправильною, спекулятивне виконання все ще впливає на ієрархію кешу . Це означає, що коли навантаження на пам'ять виконується спекулятивно, це все ще може призвести до вилучення кеш-лінії. Цю зміну в ієрархії кешу можна визначити, ретельно вимірявши час доступу до пам'яті, яка відображається на той самий набір кешу.
    Можна навіть просочити деякі біти довільної пам'яті, коли адреса джерела зчитуваної пам'яті сама зчитується з пам'яті ядра.

  • Прогноз непрямої гілки процесорів Intel використовує лише найнижчі 12 біт інструкції щодо джерела, тому легко отруїти всі 2 ^ 12 можливих історій прогнозування з керованими користувачами адресами пам'яті. Вони можуть тоді, коли прогнозується непрямий стрибок у межах ядра, спекулятивно виконуватись з привілеями ядра. Використовуючи бічний канал кешування часу, ви можете просочити довільну пам'ять ядра.

ОНОВЛЕННЯ: У списку розсилки ядра триває дискусія, яка змушує мене вважати, що ретполіни не повністю пом'якшують проблеми передбачення гілки, як коли буфер повернення стека (RSB) працює порожнім, новіші архітектури Intel (Skylake +) відпадають для вразливого цільового буфера галузі (BTB):

Retpoline як стратегія пом'якшення міняє непрямі гілки для повернення, щоб уникнути використання прогнозів, що надходять від BTB, оскільки вони можуть отруїтися зловмисником. Проблема з Skylake + полягає в тому, що підтік RSB переходить до використання прогнозування BTB, що дозволяє зловмиснику взяти під контроль спекуляції.


Я не думаю, що інструкція LFENCE важлива, впровадження Google використовує натомість інструкцію PAUSE. support.google.com/faqs/answer/7625886 Зауважте, що документація, яку ви цитуєте, говорить "не буде виконана," не буде "не буде спекулятивно виконана".
Росс Ридж

1
На цій сторінці поширених запитань Google: "Інструкції про паузу в наших спекулятивних циклах вище не потрібні для коректності. Але це означає, що невиробниче спекулятивне виконання займає менш функціональні одиниці процесора." Тож це не підтверджує ваш висновок, що саме тут є ключовим фактором LFENCE.
Росс Ридж

@RossRidge Я погоджуюсь частково, на мене це виглядає як дві можливі реалізації нескінченного циклу, які натякають на процесор, щоб не спекулятивно виконувати код за ПАУЗАМ / LFENCE. Однак якщо LFENCE було виконано спекулятивно і не відкотився тому, що міркування були правильними, це буде суперечити твердженню, що воно буде виконане лише після закінчення завантаження пам'яті. (Інакше весь набір інструкцій, які були спекулятивно виконані, доведеться
відкотити

1
Це має перевагу push/ у retтому, що воно не врівноважує стек прогнозованих зворотних адрес. Є один неправильний прогноз (перехід до використання lfenceфактичної адреси повернення), але з використанням call+ модифікація rspврівноважує це ret.
Пітер Кордес

1
ой, перевага перед push / ret(в моєму останньому коментарі). re: ваше редагування: Підтікання RSB має бути неможливим, оскільки реполінія включає в себе a call. Якщо попереднє виведення ядра там переключилося на контекст, ми б відновили виконання за допомогою RSB, завантаженого з callв планувальник. Але, можливо, обробник переривання може закінчитися достатньою rets, щоб спорожнити RSB.
Пітер Кордес

46

Retpoline призначений для захисту від цільової гілки ін'єкції ( CVE-2017-5715 ) експлуатувати. Це атака, коли непряма інструкція гілки в ядрі використовується для примушування до спекулятивного виконання довільного фрагмента коду. Вибраний код - це "гаджет", який якимось чином корисний для нападника. Наприклад, код можна вибрати таким чином, щоб витікати дані ядра через те, як це впливає на кеш. Ретполінія запобігає цьому використанню, просто замінивши всі непрямі вказівки гілки на інструкцію повернення.

Я думаю, що ключовим у retpoline є лише "ret" частина, що вона замінює непряму гілку на інструкцію повернення, щоб CPU використовував передбачувач стека повернення замість експлуатованого предиктора гілки. Якщо замість цього було використано просту інструкцію про натискання та повернення, то кодом, який би був спекулятивно виконаний, був би код, який з часом повернеться в будь-який момент, а не в який-небудь гаджет, корисний для зловмисника. Основна перевага батутної частини, здається, полягає в підтримці зворотного стеку, тому коли функція насправді повертається до свого абонента, це прогнозується правильно.

Основна ідея введення цільової гілки проста. Скористається тим, що ЦП не записує повну адресу джерела та призначення відділень у своїх цільових буферах гілок. Таким чином зловмисник може заповнити буфер за допомогою стрибків у власному адресному просторі, що призведе до прогнозованих звернень, коли певний непрямий стрибок виконується в адресному просторі ядра.

Зауважте, що retpoline не перешкоджає розкриттю інформації ядра безпосередньо, вона лише запобігає використанню непрямих інструкцій гілок для спекулятивного виконання гаджета, який би розкривав інформацію. Якщо зловмисник може знайти якісь інші способи спекулятивно виконати гаджет, то реполінія не запобігає атаці.

Газета " Призракові напади": Використання спекулятивного розстрілу Пол Кочер, Даніель Генкін, Даніель Грус, Вернер Хаас, Майк Гамбург, Моріц Ліпп, Стефан Мангард, Томас Прешер, Майкл Шварц та Юваль Яром дають наступний огляд того, як можна експлуатувати непрямі гілки. :

Експлуатація непрямих гілок. Виходячи із програмування, орієнтованого на повернення (ROP), у цьому методі зловмисник вибирає гаджетз адресного простору жертви та впливає на жертву спекулятивно виконувати пристрій. На відміну від ROP, зловмисник не покладається на вразливість коду жертви. Натомість зловмисник навчає буфер для цільового відгалуження (BTB), щоб неправильно передбачити гілку від непрямої інструкції гілки до адреси гаджета, що призвело до спекулятивного виконання гаджета. Поки спекулятивно виконані інструкції відмовляються, їх вплив на кеш не повертається. Ці ефекти можуть використовуватися гаджетом для витоку чутливої ​​інформації. Ми покажемо, як при ретельному підборі гаджета цей метод можна використовувати для зчитування довільної пам'яті від жертви.

Для зловживання BTB зловмисник знаходить віртуальну адресу гаджета в адресному просторі жертви, а потім виконує непрямі гілки на цю адресу. Ця підготовка проводиться з адресного простору зловмисника, і не має значення, що знаходиться за адресою гаджета в адресному просторі зловмисника; все, що потрібно, це те, що гілка, що використовується для навчання філій, щоб використовувати ту саму віртуальну адресу призначення. (Насправді, доки зловмисник обробляє винятки, атака може працювати, навіть якщо в віртуальній адресі гаджета в адресному просторі зловмисника відсутній код.) Також немає необхідності в повному збігу вихідної адреси галузі, яка використовується для навчання, та адреса цільової галузі. Таким чином, нападник має значну гнучкість у налаштуванні тренувань.

Запис у блозі під назвою « Читання привілейованої пам’яті» з боку каналу від команди Project Zero в Google надає ще один приклад того, як введення цільової гілки може бути використане для створення робочого подвигу.


9

Це питання було задано деякий час тому і заслуговує на нову відповідь.

Резюме :

Послідовності "Retpoline" - це програмна конструкція, яка дозволяє непрямі гілки бути ізольованими від спекулятивного виконання. Це може бути застосовано для захисту чутливих бінарних файлів (таких як операційна система чи реалізація гіпервізора) від атак введення цільової гілки проти їх непрямих гілок.

Слово " ret poline " є портмантом слів "повернення" та "батут", подібно до поліпшення " rel poline " було придумано з "відносного дзвінка" та "батута". Це батутна конструкція, побудована за допомогою операцій повернення, яка також образно забезпечує, що будь-яке пов'язане з ним спекулятивне виконання буде "відскокувати" нескінченно.

Для пом'якшення ядра або розкриття пам’яті між процесами (атака Spectre) ядро ​​Linux [1] буде складено з новою опцією, -mindirect-branch=thunk-externвведеної в gcc для здійснення непрямих викликів через так звану реполінію.

[1] Однак це не специфічно для Linux - схожа чи однакова конструкція, здається, використовується як частина стратегій пом'якшення наслідків на інших ОС.

Використання цього параметра компілятора захищає лише від Spectre V2 в пошкоджених процесорах, які мають оновлення мікрокоду, необхідне для CVE-2017-5715. Він буде працювати над будь-яким кодом (не лише ядром), а лише атакувати код, який містить "секрети", варто атакувати.

Це, здається, щойно придуманий термін, оскільки пошук у Google виявляється лише зовсім недавнього використання (як правило, у 2018 році).

LLVM компілятор мав -mretpolineперемикач , так як до 4 січня 2018 року . Ця дата настала, коли вразливість була вперше публічно повідомлена . GCC зробила свої патчі доступними 7 січня 2018 року.

Дата CVE свідчить про те, що вразливість була " виявлена " у 2017 році, але вона впливає на деякі процесори, виготовлені за останні два десятиліття (таким чином, ймовірно, це було виявлено давно).

Що таке реполінія та як запобігає останнім атакам на розкриття інформації про ядро?

По-перше, кілька визначень:

  • Батут - іноді його називають батутами опосередкованих стрибків - це місця пам’яті, що містять адреси, що вказують на переривання режиму обслуговування, підпрограми вводу / виводу тощо. Виконання стрибає на батут, а потім негайно вискакує або відскакує, звідси і термін батут. GCC традиційно підтримує вкладені функції, створюючи виконуваний батут під час виконання, коли приймається адреса вкладеної функції. Це невеликий фрагмент коду, який, як правило, знаходиться на стеку, в кадрі стека функції, що містить. Батут завантажує статичний регістр ланцюга, а потім переходить до реальної адреси вкладеної функції.

  • Thunk - Thunk - це підпрограма, яка використовується для введення додаткового обчислення в іншу підпрограму. Thunks в основному використовуються для затримки обчислення, поки не потрібен його результат, або для вставки операцій на початку або в кінці іншої підпрограми

  • Пам'ять - Запам'ятована функція "запам'ятовує" результати, що відповідають деякому набору конкретних входів. Подальші дзвінки з запам'ятовуваними входами повертають запам'ятований результат, а не перераховують його, тим самим виключаючи первинну вартість дзвінка з заданими параметрами з усіх, крім першого дзвінка, здійсненого до функції з цими параметрами.

Дуже орієнтовно, ретполія - це батут із поверненням у вигляді грому , щоб " зіпсувати " запам'ятовування в непрямому передбачувачі гілки.

Джерело : Retpoline включає інструкцію PAUSE для Intel, але інструкція LFENCE необхідна для AMD, оскільки на цьому процесорі інструкція PAUSE не є серіалізуючою інструкцією, тому цикл паузи / jmp використовуватиме надлишкову потужність, оскільки спекулюється на очікуванні повернення неправильно передбачити правильну ціль.

У Arstechnica є просте пояснення проблеми:

"Кожен процесор має архітектурну поведінку (документально підтверджена поведінка, яка описує, як працюють інструкції, і від яких програмісти залежать від написання своїх програм) та мікроархітектурну поведінку (спосіб, яким поводиться реальна реалізація архітектури). Вони можуть розходитися тонко. Наприклад, архітектурно програма, яка завантажує значення з певної адреси в пам'ять, буде чекати, поки адреса буде відома, перш ніж спробувати виконати навантаження. Але мікроархітектурно, однак процесор може спробувати спекулятивно відгадати адресу, щоб вона могла запуститися завантаження значення з пам'яті (яке повільно) навіть до того, як буде абсолютно визначено, яку адресу він повинен використовувати.

Якщо процесор здогадається неправильно, він ігнорує відгадане значення і виконає завантаження знову, на цей раз з правильною адресою. Таким чином, архітектурно визначена поведінка зберігається. Але ця несправна здогадка порушить інші частини процесора, зокрема вміст кешу. Ці мікроархітектурні порушення можуть бути виявлені та виміряні тимчасовим часом, який потребує доступу до даних, які повинні (або не повинні) знаходитись у кеші, дозволяючи шкідливій програмі робити висновки про значення, що зберігаються в пам'яті. "

З статті Intel: " Retpoline: Помилкове зменшення впорскування галузі " ( .PDF ):

"Послідовність ретрансляції запобігає спекулятивному виконанню процесором використання" непрямого передбачувача гілки "(один із способів прогнозування потоку програми) для спекуляції над адресою, керованою експлуатацією (задовольняючи елемент 4 з п'яти елементів введення цільової гілки (варіант Spectre 2 ) експлуатаційний склад, перелічений вище). "

Зауважимо, елемент 4: "Експлоатація повинна успішно впливати на цю непряму гілку, щоб спекулятивно неправильно передбачити та виконати гаджет. Цей гаджет, обраний експлойтом, просочує секретні дані через бічний канал, як правило, кеш-таймінг."

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.