Meltdown és Spectre, magyarázta

bár manapság leginkább az alkalmazásszintű hálózatépítésről és az elosztott rendszerekről vagyok ismert, karrierem első részét operációs rendszerekkel és hipervizorokkal töltöttem. Mélyen lenyűgöznek a modern processzorok és rendszerszoftverek működésének alacsony szintű részletei. Amikor bejelentették a legutóbbi összeomlást és a Spectre sebezhetőségét, beleástam magam a rendelkezésre álló információba, és alig vártam, hogy többet megtudjak.

a sebezhetőségek meghökkentőek; Azt állítom, hogy ezek az egyik legfontosabb felfedezés a számítástechnikában az elmúlt 10-20 évben. Az enyhítéseket szintén nehéz megérteni, és nehéz pontos információkat találni róluk. Ez nem meglepő, tekintettel kritikus jellegükre. A sebezhetőségek enyhítése hónapokig tartó titkos munkát igényelt a főbb CPU-k, operációs rendszerek és felhőgyártók számára. Elképesztő az a tény, hogy a kérdéseket 6 hónapig titokban tartották, amikor szó szerint több száz ember dolgozott rajtuk.

bár a bejelentés óta sokat írtak a Meltdownról és a Spectre-ről, nem láttam jó középszintű bevezetést a sebezhetőségekről és az enyhítésekről. Ebben a bejegyzésben megpróbálom kijavítani ezt azáltal, hogy gyengéd bevezetést adok a sebezhetőségek megértéséhez szükséges hardver és szoftver háttérbe, maguk a sebezhetőségek megvitatása, valamint a jelenlegi enyhítések megvitatása.

fontos megjegyzés: Mivel nem dolgoztam közvetlenül az enyhítéseken, és nem dolgozom az Intel, a Microsoft, a Google, az Amazon, a Red Hat stb. néhány részlet, amelyet közölni fogok, nem biztos, hogy teljesen pontos. Összeállítottam ezt a bejegyzést a rendszerek működéséről, a nyilvánosan elérhető dokumentációról és az LKML-hez és a xen-develhez írt javításokról/vitákról szóló ismereteim alapján. Szeretném, ha kijavítanák, ha a bejegyzés bármelyike pontatlan, bár kétlem, hogy ez hamarosan megtörténik, tekintettel arra, hogy ennek a témának mekkora részét még mindig az NDA fedezi.

ebben a részben megadom a biztonsági rések megértéséhez szükséges hátteret. A rész nagy részletességgel ragyog, és olyan olvasóknak szól, akik korlátozott mértékben ismerik a számítógépes hardvert és a rendszerszoftvert.

virtuális memória

a virtuális memória az 1970-es évek óta minden operációs rendszer által használt technika. absztrakciós réteget biztosít a legtöbb szoftver által látott memória címelrendezés és a memóriát támogató fizikai eszközök (RAM, lemezek stb.) között.). Magas szinten lehetővé teszi az alkalmazások számára, hogy több memóriát használjanak fel, mint a gép valójában; ez erőteljes absztrakciót biztosít, amely sok programozási feladatot megkönnyít.

1.ábra: virtuális memória

az 1. ábra egy egyszerű számítógépet mutat, amelynek 400 bájt memóriája van a 100 bájtos”oldalakon”(a valódi számítógépek két, általában 4096-os teljesítményt használnak). A számítógépnek két folyamata van, mindegyik 200 bájt memóriával rendelkezik 2 oldalon. Lehet, hogy a folyamatok ugyanazt a kódot futtatják a 0-199 bájtos tartományban lévő fix címekkel, azonban diszkrét fizikai memória támasztja alá őket, így nem befolyásolják egymást. Bár a modern operációs rendszerek és számítógépek lényegesen bonyolultabb módon használják a virtuális memóriát, mint amit ebben a példában bemutatunk, a fent bemutatott alapfeltevés minden esetben érvényes. Az operációs rendszerek kivonják azokat a címeket, amelyeket az alkalmazás lát az őket támogató fizikai erőforrásokból.

a virtuális fizikai címekre történő fordítás olyan gyakori művelet a modern számítógépekben, hogy ha az operációs rendszert minden esetben be kellene vonni, a számítógép hihetetlenül lassú lenne. A Modern CPU hardver egy fordítási Lookaside Buffer (TLB) nevű eszközt biztosít, amely gyorsítótárazza a közelmúltban használt leképezéseket. Ez lehetővé teszi a CPU-k számára, hogy az idő nagy részében közvetlenül a hardverben hajtsák végre a címfordítást.

2. ábra >

2. ábra: Virtuális memória fordítás

a 2.ábra a címfordítási folyamatot mutatja:

  1. EGY program lekér egy virtuális címet.
  2. a CPU megpróbálja lefordítani a TLB segítségével. Ha a cím megtalálható, a fordítást használják.
  3. Ha a cím nem található, a CPU konzultál egy sor “oldaltáblával” a leképezés meghatározásához. Az oldaltáblák az operációs rendszer által biztosított fizikai memória oldalak halmaza olyan helyen, ahol a hardver megtalálja őket (például a CR3 regiszter az x86 hardveren). Az oldaltáblák a virtuális címeket fizikai címekké térképezik fel, és metaadatokat, például engedélyeket is tartalmaznak.
  4. Ha az oldaltábla leképezést tartalmaz, a rendszer visszaadja, gyorsítótárazza a TLB-ben, és keresésre használja. Ha az oldaltábla nem tartalmaz leképezést, akkor az operációs rendszer” oldalhibát ” emel. Az oldalhiba egy speciális típusú megszakítás, amely lehetővé teszi az operációs rendszer számára, hogy átvegye az irányítást és meghatározza, mit kell tennie, ha hiányzik vagy érvénytelen leképezés van. Például az operációs rendszer leállíthatja a programot. Lehet, hogy fizikai memóriát is lefoglalhat, és feltérképezheti azt a folyamatba. Ha egy oldalhiba-kezelő folytatja a végrehajtást, az új leképezést a TLB fogja használni.
3.ábra: felhasználó/kernel virtuális memória leképezések

a 3. ábra kissé reálisabb képet mutat arról, hogy a virtuális memória Hogyan néz ki egy modern számítógépben (pre-Meltdown — erről bővebben alább). Ebben a beállításban a következő tulajdonságokkal rendelkezünk:

  • a kernelmemória piros színnel jelenik meg. A 0-99-es fizikai címtartományban található. A Kernel memória olyan speciális memória, amelyhez csak az operációs rendszer férhet hozzá. A felhasználói programok nem férhetnek hozzá.
  • a felhasználói memória szürke színnel jelenik meg.
  • a fel nem osztott fizikai memória kék színnel jelenik meg.

ebben a példában elkezdjük látni a virtuális memória néhány hasznos funkcióját. Elsősorban:

  • felhasználói memória minden folyamat a virtuális tartományban 0-99, de mögött különböző fizikai memória.
  • Kernel memória minden folyamat a virtuális tartományban 100-199, de mögött ugyanaz a fizikai memória.

amint azt az előző szakaszban röviden említettem, minden oldalhoz hozzárendelt engedélybitek vannak. Annak ellenére, hogy a kernelmemória minden felhasználói folyamatba be van térképezve, amikor a folyamat felhasználói módban fut, nem fér hozzá a kernelmemóriához. Ha egy folyamat megpróbálja ezt megtenni, akkor oldalhibát vált ki, amikor az operációs rendszer megszünteti azt. Ha azonban a folyamat kernel módban fut (például rendszerhívás közben), a processzor engedélyezi a hozzáférést.

Ezen a ponton megjegyzem, hogy ez a fajta kettős leképezés (minden folyamat, amelyben a kernel közvetlenül hozzá van rendelve) teljesítmény okokból több mint harminc éve szokásos gyakorlat az operációs rendszer tervezésében (a rendszerhívások nagyon gyakoriak, és sok időbe telik a kernel vagy a felhasználói terület újratervezése minden átmenetnél).

CPU gyorsítótár topológia

4.ábra: CPU szál, mag, csomag és gyorsítótár topológia.

a biztonsági rések megértéséhez szükséges következő háttérinformáció a modern processzorok CPU és cache topológiája. A 4. ábra egy általános topológiát mutat be, amely a legtöbb modern CPU-ban közös. A következő összetevőkből áll:

  • a végrehajtás alapegysége a” CPU szál “vagy” hardverszál “vagy” hyper-szál.”Minden CPU szál tartalmaz egy sor regisztert, és képes végrehajtani egy gépi kódfolyamot, hasonlóan egy szoftverszálhoz.
  • a CPU szálak egy “CPU magban vannak.”A legtöbb modern CPU magonként két szálat tartalmaz.
  • a Modern CPU-k általában több szintű gyorsítótár-memóriát tartalmaznak. A CPU szálhoz közelebb lévő gyorsítótár szintek kisebbek, gyorsabbak és drágábbak. Minél távolabb van a CPU-tól és közelebb van a fő memóriához a gyorsítótár annál nagyobb, lassabb és olcsóbb.
  • a tipikus modern CPU kialakítás magonként L1/L2 gyorsítótárat használ. Ez azt jelenti, hogy a mag minden CPU-szála ugyanazokat a gyorsítótárakat használja.
  • több CPU magot tartalmaz egy “CPU csomag.”A Modern CPU-k csomagonként legfeljebb 30 magot (60 szálat) tartalmazhatnak.
  • a csomag összes CPU magja általában egy L3 gyorsítótárral rendelkezik.
  • CPU csomagok illeszkednek “sockets.”A legtöbb fogyasztói számítógép egyetlen aljzatú, míg sok adatközpont-szerver több aljzattal rendelkezik.

spekulatív végrehajtás

5. ábra: modern CPU végrehajtó motor (forrás: Google images)

a biztonsági rések megértéséhez szükséges Utolsó háttérinformáció egy modern CPU technika, amelyet “spekulatív végrehajtásnak” neveznek.”Az 5. ábra a végrehajtó motor általános diagramját mutatja egy modern CPU-n belül.

az elsődleges elvihető, hogy a modern CPU-k hihetetlenül bonyolultak, és nem egyszerűen sorrendben hajtják végre a gép utasításait. Minden CPU szál bonyolult csővezeték-motorral rendelkezik, amely képes az utasítások sorrenden kívüli végrehajtására. Ennek oka a gyorsítótárazással kapcsolatos. Amint azt az előző részben tárgyaltam, minden CPU több szintű gyorsítótárat használ. Minden gyorsítótár hiányzik hozzáad egy jelentős mennyiségű késleltetési idő a program végrehajtását. Ennek enyhítése érdekében a processzorok képesek előre és nem megfelelően végrehajtani a memória terhelését várva. Ezt nevezik spekulatív kivégzésnek. A következő kódrészlet ezt bizonyítja.

if (x < array1_size) {
y = array2 * 256];
}

az előző részletben képzelje el, hogy a array1_size nem érhető el a gyorsítótárban, de a array1 címe. A CPU sejtheti (spekulálhatja), hogy a x kisebb, mint a array1_size, és végezze el a számításokat az if utasításban. Miután aarray1_size kiolvasásra került a memóriából, a CPU meg tudja határozni, hogy helyesen találta-e ki. Ha igen, akkor továbbra is megtakaríthat egy csomó időt. Ha nem, akkor eldobhatja a spekulatív számításokat és újrakezdheti. Ez nem rosszabb, mint ha eleve várt volna.

a spekulatív végrehajtás egy másik típusát közvetett ág-előrejelzésnek nevezik. Ez rendkívül gyakori a modern programokban a virtuális Küldés miatt.

class Base {
public:
virtual void Foo() = 0;
};class Derived : public Base {
public:
void Foo() override { … }
};Base* obj = new Derived;
obj->Foo();

(az előző kódrészlet forrása ez a bejegyzés)

az előző kódrészlet gépi kódban történő megvalósításának módja a” v-table “vagy a” virtual dispatch table”betöltése a memóriahelyről, amelyre a obj mutat, majd hívja. Mivel ez a művelet annyira gyakori, a modern CPU-k különböző belső gyorsítótárakkal rendelkeznek, és gyakran kitalálják (spekulálnak), hogy a közvetett ág hova megy, és folytatják a végrehajtást ezen a ponton. Ismét, ha a CPU helyesen kitalál, akkor továbbra is sok időt takaríthat meg. Ha nem, akkor eldobhatja a spekulatív számításokat és újrakezdheti.

Meltdown sebezhetőség

miután most lefedtük az összes háttérinformációt, belemerülhetünk a sebezhetőségekbe.

Rogue data cache load

az első biztonsági rés, az úgynevezett Meltdown, meglepően egyszerű megmagyarázni, és szinte triviális kihasználni. Az exploit kód nagyjából a következőképpen néz ki:

1. uint8_t* probe_array = new uint8_t;
2. // ... Make sure probe_array is not cached
3. uint8_t kernel_memory = *(uint8_t*)(kernel_address);
4. uint64_t final_kernel_memory = kernel_memory * 4096;
5. uint8_t dummy = probe_array;
6. // ... catch page fault
7. // ... determine which of 256 slots in probe_array is cached

tegyük meg a fenti lépéseket, írjuk le, mit csinál, és hogyan vezet ahhoz, hogy a teljes számítógép memóriáját ki tudjuk olvasni egy felhasználói programból.

  1. az első sorban egy “szonda tömb” kerül kiosztásra. Ez a folyamatunk memóriája, amelyet oldalsó csatornaként használunk az adatok lekérésére a kernelből. Hogy ez hogyan történik, hamarosan nyilvánvalóvá válik.
  2. az allokációt követően a támadó gondoskodik arról, hogy a probe tömb egyik memóriája sem legyen Gyorsítótárazva. Ennek különféle módjai vannak, amelyek közül a legegyszerűbb CPU-specifikus utasításokat tartalmaz a memória helyének a gyorsítótárból történő törléséhez.
  3. a támadó ezután továbblép egy bájt olvasására a kernel címteréből. Ne feledje a virtuális memóriáról és az oldaltáblákról szóló korábbi beszélgetésünkből, hogy az összes modern kernel általában a teljes kernel virtuális címteret leképezi a felhasználói folyamatba. Az operációs rendszerek azon a tényen alapulnak, hogy minden oldaltábla bejegyzés rendelkezik engedélybeállításokkal, és hogy a felhasználói módú programok nem férhetnek hozzá a kernel memóriájához. Minden ilyen hozzáférés oldalhibát eredményez. Valójában ez fog történni a 3. lépésben.
  4. a modern processzorok azonban spekulatív végrehajtást is végeznek, és a hibás utasítás előtt fognak végrehajtani. Így a 3-5 lépések végrehajthatók a CPU csővezetékében, mielőtt a hiba felmerülne. Ebben a lépésben a kernelmemória bájtját (amely 0-255 között mozog) megszorozzuk a rendszer oldalméretével, amely általában 4096.
  5. ebben a lépésben a kernelmemória szorzott bájtját használjuk fel arra, hogy a probe tömbből dummy értékre olvassuk. A bájt 4096-os szorzásával elkerülhető, hogy a “prefetcher” nevű CPU-funkció több adatot olvasson, mint amennyit szeretnénk a gyorsítótárba.
  6. ezzel a lépéssel a CPU felismerte a hibáját, és visszagurult a 3.lépésre. A spekulált utasítások eredményei azonban továbbra is láthatók a gyorsítótárban. A támadó az operációs rendszer funkcióit használja a hibás utasítás csapdába ejtésére és a végrehajtás folytatására (pl. a SIGFAULT kezelése).
  7. a 7.lépésben a támadó végigmegy, és meglátja, mennyi időbe telik a 256 lehetséges bájt beolvasása a Probe tömbben, amelyet a kernel memóriája indexelhetett. A CPU betölti az egyik helyet a gyorsítótárba, és ez a hely lényegesen gyorsabban töltődik be, mint az összes többi hely (amelyeket a fő memóriából kell olvasni). Ez a hely a kernel memóriájában lévő bájt értéke.

a fenti technikával, valamint azzal a ténnyel, hogy a modern operációs rendszerek általános gyakorlata, hogy az összes fizikai memóriát a kernel virtuális címterébe térképezik, a támadó képes olvasni a számítógép teljes fizikai memóriáját.

most, lehet, hogy kíváncsi: “azt mondta, hogy az oldal táblázatok engedély bit. Hogyan lehet, hogy a felhasználói mód kódja spekulatív módon hozzáférhetett a kernel memóriájához?”Ennek oka az, hogy ez egy hiba az Intel processzorokban. Véleményem szerint nincs jó ok, teljesítmény vagy más módon, hogy ez lehetséges legyen. Emlékezzünk arra, hogy minden virtuális memória-hozzáférésnek a TLB-n keresztül kell történnie. A spekulatív végrehajtás során könnyen ellenőrizhető, hogy a gyorsítótárazott leképezés rendelkezik-e az aktuális futó jogosultsági szinttel kompatibilis engedélyekkel. Az Intel hardver egyszerűen nem teszi ezt meg. Más processzorgyártók engedélyellenőrzést végeznek és blokkolják a spekulatív végrehajtást. Így, amennyire tudjuk, a Meltdown csak az Intel sebezhetősége.

Szerkesztés: úgy tűnik, hogy legalább egy ARM processzor is hajlamos az összeomlásra, amint azt itt és itt jeleztük.

Meltdown enyhítések

Meltdown könnyen érthető, triviális kihasználni, és szerencsére is van egy viszonylag egyszerű enyhítése (legalábbis fogalmilag — kernel Fejlesztők talán nem ért egyet, hogy ez egyszerű megvalósítani).

Kernel page table isolation (Kpti)

emlékezzünk arra, hogy a virtuális memóriáról szóló részben leírtam, hogy minden modern operációs rendszer olyan technikát alkalmaz, amelyben a kernel memória minden felhasználói módba leképezve feldolgozza a virtuális memória címterületét. Ez mind a teljesítmény, mind az egyszerűség miatt van. Ez azt jelenti, hogy amikor egy program rendszerhívást kezdeményez, a kernel minden további munka nélkül készen áll a használatra. A Meltdown javítása az, hogy már nem hajtja végre ezt a kettős leképezést.

6.ábra: Kernel page table isolation

a 6. ábra a kernel page table isolation (kpti) nevű technikát mutatja be. Ez alapvetően csapódik le, hogy nem leképezése kernel memória egy programot, amikor fut a felhasználói térben. Ha nincs leképezés jelen, spekulatív végrehajtás már nem lehetséges, és azonnal hiba.

amellett, hogy az operációs rendszer virtuális memória kezelőjét (VMM) bonyolultabbá teszi, hardveres segítség nélkül ez a technika jelentősen lelassítja azokat a munkaterheléseket is, amelyek nagyszámú felhasználói módot váltanak át a kernel módra, mivel az oldaltáblákat minden átmenetnél módosítani kell, és a TLB-t ki kell öblíteni (tekintettel arra, hogy a TLB megtarthatja az elavult leképezéseket).

az újabb x86 CPU-knak van egy asid (address space ID) vagy pcid (process context ID) néven ismert funkciója, amely ezt a feladatot lényegesen olcsóbbá teheti (az ARM és más mikroarchitektúrák évek óta rendelkeznek ezzel a funkcióval). A PCID lehetővé teszi egy azonosító társítását egy TLB-bejegyzéshez, majd csak a TLB-bejegyzések öblítését ezzel az azonosítóval. A PCID használata olcsóbbá teszi a KPTI-t, de még mindig nem ingyenes.

összefoglalva, A Meltdown egy rendkívül súlyos és könnyen kihasználható sebezhetőség. Szerencsére viszonylag egyszerű enyhítéssel rendelkezik, amelyet az összes nagyobb operációs rendszer-gyártó már telepített, azzal a figyelmeztetéssel, hogy bizonyos munkaterhelések lassabban fognak futni, amíg a jövőbeni hardvert kifejezetten a leírt címtartomány-elválasztáshoz nem tervezik.

Spectre sebezhetőség

a Spectre A Meltdown néhány tulajdonságával rendelkezik, és két változatból áll. A Meltdown-tól eltérően a Spectre-t lényegesen nehezebb kihasználni, de szinte az összes, az elmúlt húsz évben gyártott modern processzort érinti. Lényegében a Spectre egy támadás a modern CPU és operációs rendszer kialakítása ellen, szemben egy speciális biztonsági réssel.

Bounds check bypass (Spectre variant 1)

az első Spectre variáns “bounds check bypass” néven ismert.”Ezt a következő kódrészlet mutatja be (amely ugyanaz a kódrészlet, amelyet a fenti spekulatív végrehajtás bevezetésére használtam).

if (x < array1_size) {
y = array2 * 256];
}

az előző példában tegyük fel a következő eseménysorozatot:

  1. a támadó irányítja x.
  2. array1_size nincs gyorsítótárban.
  3. array1 gyorsítótárban van.
  4. a CPU azt hiszi, hogy x kisebb, mint array1_size. (A CPU-k különböző szabadalmaztatott algoritmusokat és heurisztikákat alkalmaznak annak meghatározására, hogy spekuláljanak-e, ezért a Spectre támadási részletei eltérnek a processzorgyártók és a modellek között.)
  5. a CPU végrehajtja az if utasítás törzsét, miközben a array1_size betöltésére vár, a gyorsítótárat a Meltdownhoz hasonló módon befolyásolja.
  6. a támadó ezután különböző módszerek egyikével meghatározhatja a array1 tényleges értékét. (A gyorsítótár-következtetési támadásokkal kapcsolatos további részletekért lásd a kutatási cikket.)

A Spectre-t lényegesen nehezebb kihasználni, mint a Meltdown-t, mivel ez a biztonsági rés nem függ a jogosultságok eszkalációjától. A támadónak meg kell győznie a kernelt, hogy futtassa a kódot, és helytelenül spekuláljon. Általában a támadónak meg kell mérgeznie a spekulációs motort, és becsapnia kell, hogy helytelenül tippeljen. Hogy az említett, a kutatók számos koncepcióbizonyító kihasználást mutattak be.

szeretném megismételni, milyen igazán hihetetlen megállapítás ez a kizsákmányolás. Én személy szerint nem úgy ez a CPU tervezési hiba, mint Meltdown önmagában. Úgy vélem, ez egy alapvető kinyilatkoztatás arról, hogyan működik együtt a modern hardver és szoftver. Az a tény, hogy a CPU gyorsítótárak közvetett módon felhasználhatók a hozzáférési minták megismerésére, már egy ideje ismert. Az a tény, hogy a CPU gyorsítótárak oldalsó csatornaként használhatók a számítógép memóriájának eldobására, meghökkentő, mind fogalmilag, mind annak következményeiben.

Branch target injection (Spectre variant 2)

emlékezzünk arra, hogy a közvetett elágazás nagyon gyakori a modern programokban. A Spectre 2. változata közvetett elágazási előrejelzést használ, hogy megmérgezze a CPU-t spekulatív módon egy olyan memóriahelyre, amelyet egyébként soha nem hajtott volna végre. Ha ezeknek az utasításoknak a végrehajtása a gyorsítótárban olyan állapotot hagyhat maga után, amely a gyorsítótár következtetési támadásaival észlelhető, akkor a támadó az összes kernel memóriát eldobhatja. Mint Spectre variant 1, Spectre variant 2 sokkal nehezebb kihasználni, mint Meltdown, azonban a kutatók bebizonyították működő proof-of-concept hasznosítja variáns 2.

kísértet-enyhítések

a Kísértet-enyhítések lényegesen érdekesebbek, mint az olvadás-enyhítés. Valójában, az academic Spectre papír azt írja, hogy jelenleg nincsenek ismert enyhítések. Úgy tűnik, hogy a színfalak mögött és az akadémiai munkával párhuzamosan az Intel (és valószínűleg más CPU-gyártók), valamint a főbb operációs rendszerek és felhő-gyártók hónapok óta dühösen dolgoznak az enyhítések kidolgozásán. Ebben a részben kitérek a különféle enyhítésekre, amelyeket kidolgoztak és alkalmaztak. Ez az a szakasz, amelyben a leginkább ködös vagyok, mivel hihetetlenül nehéz pontos információkat szerezni, ezért különféle forrásokból összerakom a dolgokat.

statikus elemzés és kerítés (1.változat enyhítése)

az egyetlen ismert 1. változat (bounds check bypass) enyhítés a kód statikus elemzése, amely meghatározza azokat a kódsorozatokat, amelyeket támadók irányíthatnak, hogy zavarják a spekulációt. A sebezhető kódsorozatoknak lehet egy sorosító utasításuk, például lfence beillesztve, amely leállítja a spekulatív végrehajtást, amíg az összes utasítás a kerítésig nem kerül végrehajtásra. Óvatosan kell eljárni a kerítés utasításainak behelyezésekor, mivel a túl sok súlyos teljesítményhatásokkal járhat.

Retpoline (variant 2 enyhítés)

az első Spectre variant 2 (branch target injection) enyhítést a Google fejlesztette ki, és “retpoline” néven ismert.”Számomra nem világos, hogy a Google elszigetelten fejlesztette-e ki, vagy a Google az Intellel együttműködve. Feltételezem, hogy a Google kísérletileg fejlesztette ki, majd az Intel hardvermérnökei ellenőrizték, de nem vagyok biztos benne. A “retpoline” megközelítés részletei megtalálhatók a Google témában írt cikkében. Fogom összefoglalni őket itt (én glossing át néhány részletet, beleértve underflow, amelyek szerepelnek a papír).

a Retpoline arra a tényre támaszkodik, hogy a hívó és visszatérő funkciók és a kapcsolódó verem manipulációk annyira gyakoriak a számítógépes programokban, hogy a CPU-k erősen optimalizáltak azok végrehajtására. (Ha nem ismeri a verem működését a hívással és a funkciókból való visszatéréssel kapcsolatban, akkor ez a bejegyzés jó alapozó.) Dióhéjban, amikor egy” hívást ” hajtanak végre, a visszatérési cím a verembe kerül. a” ret ” kikapcsolja a visszatérési címet, és folytatja a végrehajtást. A spekulatív végrehajtási hardver emlékezni fog a tolt visszatérési címre, és spekulatív módon folytatja a végrehajtást ezen a ponton.

a retpoline konstrukció helyettesíti a regiszterben tárolt memória helyére történő közvetett ugrást r11:

jmp *%r11

:

call set_up_target; (1)
capture_spec: (4)
pause;
jmp capture_spec;
set_up_target:
mov %r11, (%rsp); (2)
ret; (3)

nézzük meg, hogy az előző összeszerelési kód mit tesz egy lépésben, és hogyan enyhíti az elágazási cél befecskendezését.

  1. ebben a lépésben a kód egy fordítási időben ismert memóriahelyet hív meg, tehát egy kemény kódolt eltolás, nem pedig közvetett. Ez a capture_spec visszatérési címét helyezi a verembe.
  2. a hívás visszatérési címét felülírja a tényleges ugrási cél.
  3. a visszatérés a valós célponton történik.
  4. amikor a CPU spekulatív módon végrehajtja, visszatér egy végtelen hurokba! Ne feledje, hogy a CPU előre spekulál, amíg a memória betöltése befejeződik. Ebben az esetben a spekulációt manipulálták, hogy egy végtelen hurokba rögzítsék, amelynek nincsenek olyan mellékhatásai, amelyek megfigyelhetők a támadó számára. Amikor a CPU végül végrehajtja a reálhozamot, megszakítja a spekulatív végrehajtást, amelynek nincs hatása.

véleményem szerint ez egy igazán ötletes enyhítés. Dicséret azoknak a mérnököknek, akik kifejlesztették. Ennek az enyhítésnek az a hátránya, hogy minden szoftvert újra kell fordítani úgy, hogy a közvetett ágakat retpoline ágakká alakítsák át. Az olyan felhőszolgáltatások esetében, mint például a Google, amelyek a teljes verem tulajdonában vannak, az újrafordítás nem nagy ügy. Másoknak, lehet, hogy nagyon nagy ügy vagy lehetetlen.

IBRS, STIBP és IBPB (variant 2 enyhítés)

úgy tűnik, hogy a retpoline fejlesztésével párhuzamosan az Intel (és az AMD bizonyos mértékig) dühösen dolgozik a hardveres változtatásokon, hogy enyhítse az ág célinjekciós támadásokat. A CPU mikrokód frissítéseként szállított három új hardverfunkció a következő:

  • közvetett elágazási Korlátozott spekuláció (IBRS)
  • egyszálú közvetett elágazási prediktorok (STIBP)
  • közvetett elágazási prediktor gát (IBPB)

az új mikrokód-funkciókkal kapcsolatos korlátozott információk az Intel itt érhetők el. Nagyjából össze tudtam rakni, hogy ezek az új funkciók mit tesznek a fenti dokumentáció elolvasásával, valamint a Linux kernel és a Xen hypervisor javítások áttekintésével. Elemzésem alapján minden funkciót potenciálisan a következőképpen használunk:

  • az IBRS mind a jogosultsági szintek között (felhasználó a kernelhez) átöblíti az ág-előrejelzési gyorsítótárat, mind pedig letiltja az ág-előrejelzést a testvér CPU-szálon. Emlékezzünk arra, hogy minden CPU magnak általában két CPU-szála van. Úgy tűnik, hogy a modern CPU-kon az ág-előrejelző hardver meg van osztva a szálak között. Ez azt jelenti, hogy a felhasználói mód kódja nemcsak megmérgezheti az ág előrejelzőjét a kernel kód megadása előtt, hanem a testvér CPU szálon futó kód is megmérgezheti. Az IBRS engedélyezése kernel módban lényegében megakadályozza, hogy minden korábbi végrehajtás felhasználói módban, valamint a testvér CPU szálon történő végrehajtás befolyásolja az elágazások előrejelzését.
  • úgy tűnik, hogy a STIBP az IBRS egy részhalmaza, amely csak letiltja az ág előrejelzését a testvér CPU szálon. Amennyire meg tudom mondani, ennek a funkciónak a fő felhasználási esete annak megakadályozása, hogy egy testvér CPU-szál megmérgezze az ág-előrejelzőt, ha két különböző felhasználói módú folyamatot (vagy virtuális gépet) futtat ugyanazon a CPU-magon egyszerre. Most őszintén nem teljesen világos számomra, hogy mikor kell használni a STIBP-t.úgy tűnik, hogy a
  • IBPB átöblíti az ugyanazon jogosultsági szinten futó kód elágazási előrejelzési gyorsítótárát. Ez két felhasználói módú program vagy két virtuális gép közötti váltáskor használható annak biztosítására, hogy az előző kód ne zavarja a futtatni kívánt kódot (bár STIBP nélkül úgy gondolom, hogy a testvér CPU szálon futó kód még mindig megmérgezheti az ág előrejelzőjét).

az írás óta a fő enyhítések, amelyeket látok az ág célinjekció sebezhetőségének megvalósításában, mind a retpoline, mind az IBRS. Feltehetően ez a leggyorsabb módja annak, hogy megvédje a kernelt a felhasználói módú programoktól, vagy a hipervizort a virtuális gép vendégeitől. A jövőben azt várnám, hogy mind a STIBP, mind az IBPB telepítve legyen, attól függően, hogy a különböző felhasználói módú programok paranoia szintje zavarja egymást.

úgy tűnik, hogy az IBRS költsége rendkívül széles körben változik a CPU architektúrák között, az újabb Intel Skylake processzorok viszonylag olcsóak a régebbi processzorokhoz képest. A Lyft – nél körülbelül 20% – kal lassult bizonyos rendszerhívások nehéz munkaterhelése az AWS C4 példányokon, amikor az enyhítéseket bevezették. Feltételezem, hogy az Amazon bevezette az IBRS-t és potenciálisan a retpoline-t is, de nem vagyok biztos benne. Úgy tűnik, hogy a Google csak a retpoline-t dobta ki a felhőjében.

Idővel arra számítok, hogy a processzorok végül egy IBRS “always on” modellre költöznek, ahol a hardver csak alapértelmezés szerint tisztítja az ág-előrejelző elválasztását a CPU szálak között, és helyesen öblíti le az állapotot a privilege szintű változásokon. Az egyetlen ok, amiért ezt ma nem tennék meg, az a látszólagos teljesítményköltség, hogy ezt a funkciót mikrokód-frissítésekkel utólag felszerelik a már kiadott mikroarchitektúrákra.

következtetés

nagyon ritka, hogy egy kutatási eredmény alapvetően megváltoztatja a számítógépek felépítését és működését. A Meltdown és a Spectre pontosan ezt tette. Ezek a megállapítások jelentősen megváltoztatják a hardver-és szoftvertervezést a következő 7-10 évben (a következő CPU hardverciklus), mivel a tervezők figyelembe veszik a gyorsítótár-mellékcsatornákon keresztüli adatszivárgás lehetőségeinek új valóságát.időközben a Meltdown és a Spectre megállapításai és a kapcsolódó enyhítések jelentős következményekkel járnak a számítógép-felhasználók számára az elkövetkező években. Az enyhítések rövid távon a munkaterheléstől és az adott hardvertől függően jelentős teljesítményhatással járnak majd. Ez szükségessé teheti egyes infrastruktúrák működési változtatásait (például a Lyft-nél agresszíven mozgatunk néhány munkaterhelést az AWS C5 példányokra, mivel az IBRS lényegesen gyorsabban fut a Skylake processzorokon, és az új Nitro hypervisor megszakításokat biztosít közvetlenül a vendégeknek az SR-IOV és az APICv használatával, eltávolítva sok virtuális gép kijáratot az IO nehéz munkaterhelésekhez). Az asztali számítógép-felhasználók sem immunisak, mivel az operációs rendszer és a böngészőgyártók azon dolgoznak, hogy enyhítsék a Javascriptet használó proof-of-concept böngésző támadásokat. Ezenkívül a sebezhetőségek összetettsége miatt szinte biztos, hogy a biztonsági kutatók új kihasználásokat találnak, amelyekre nem vonatkoznak a jelenlegi enyhítések, amelyeket javítani kell.

bár szeretek a Lyft-nél dolgozni, és úgy érzem, hogy a microservice systems infrastructure térben végzett munka az iparág egyik leghatásosabb munkája, az ilyen események miatt hiányzik az operációs rendszereken és a hipervizorokon végzett munka. Rendkívül féltékeny vagyok arra a hősies munkára, amelyet az elmúlt hat hónapban rengeteg ember végzett a sebezhetőségek kutatásában és enyhítésében. Szerettem volna a részese lenni!

további olvasmányok

  • Meltdown and Spectre akadémiai papírok: https://spectreattack.com/
  • Google Project Zero blogbejegyzés: https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html
  • Intel Spectre hardver enyhítések: https://software.intel.com/sites/default/files/managed/c5/63/336996-Speculative-Execution-Side-Channel-Mitigations.pdf
  • retpoline blogbejegyzés: https://support.google.com/faqs/answer/7625886
  • az ismert információk jó összefoglalása: https://github.com/marcan/speculation-bugs/blob/master/README.md



Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.