Meltdown ja Spectre, selitti
vaikka nykyään minut tunnetaan lähinnä sovellustason verkostoitumisesta ja hajautetuista järjestelmistä, vietin urani ensimmäisen osan käyttöjärjestelmien ja hypervisorien parissa. Pidän yllä syvää viehtymystä alhaisiin yksityiskohtiin siitä, miten nykyaikaiset prosessorit ja järjestelmäohjelmistot toimivat. Kun viimeaikaiset Meltdown ja Spectre haavoittuvuudet julkistettiin, kaivoin käytettävissä olevaa tietoa ja olin innokas oppimaan lisää.
haavoittuvuudet ovat ällistyttäviä; Väitän, että ne ovat tietojenkäsittelytieteen tärkeimpiä löytöjä viimeisen 10-20 vuoden aikana. Lieventäviä asianhaaroja on myös vaikea ymmärtää ja niistä on vaikea löytää tarkkaa tietoa. Tämä ei ole yllättävää, kun otetaan huomioon niiden kriittinen luonne. Haavoittuvuuksien lieventäminen on vaatinut kuukausien salaista työtä kaikilta suurimmilta suoritin -, käyttöjärjestelmä-ja pilvipalvelutoimittajilta. Se, että asiat pidettiin salassa 6 kuukautta, kun kirjaimellisesti satoja ihmisiä todennäköisesti työskennellyt niitä on hämmästyttävää.
vaikka Meltdownista ja Spectrestä on kirjoitettu paljon niiden julkistamisen jälkeen, en ole nähnyt hyvää keskitason esittelyä haavoittuvuuksiin ja lieventämisiin. Tässä viestissä aion yrittää korjata, että tarjoamalla lempeä johdatus laitteisto ja ohjelmisto Tausta tarvitaan ymmärtämään haavoittuvuuksia, keskustelua haavoittuvuuksia itse, sekä keskustelua nykyisen lieventämistä.
Tärkeä huomautus: Koska en ole työskennellyt suoraan lieventämisestä, enkä työskentele Intel, Microsoft, Google, Amazon, Red Hat, jne. jotkut yksityiskohdat, jotka aion antaa, eivät ehkä ole täysin tarkkoja. Olen koonnut tämän viestin perustuen tietämykseeni siitä, miten nämä järjestelmät toimivat, julkisesti saatavilla olevaan dokumentaatioon ja lkml: lle ja xen-develille lähetettyihin laastareihin/keskusteluihin. Haluaisin korjata, jos jokin tästä viestistä on epätarkka, vaikka epäilen, että tapahtuu milloin tahansa pian ottaen huomioon, kuinka paljon tästä aiheesta on vielä katettu NDA.
tässä osassa esitän joitakin taustatietoja, joita tarvitaan haavoittuvuuksien ymmärtämiseen. Osassa kaunistellaan paljon yksityiskohtia ja se on suunnattu lukijoille, joilla on rajallinen tietämys tietokonelaitteistoista ja järjestelmäohjelmistoista.
virtuaalimuisti
virtuaalimuisti on kaikkien käyttöjärjestelmien 1970-luvulta lähtien käyttämä tekniikka, jossa useimpien ohjelmistojen näkemän muistiosoitteen asettelun ja kyseistä muistia tukevien fyysisten laitteiden (RAM, levyt jne. ). Korkealla tasolla sen avulla sovellukset voivat hyödyntää enemmän muistia kuin koneella todellisuudessa on; tämä tarjoaa tehokkaan abstraktion, joka helpottaa monia ohjelmointitehtäviä.
kuvassa 1 on yksinkertainen tietokone, jossa on 400 tavua muistia 100 tavun ”sivuilla” (oikeat tietokoneet käyttävät kahden, tyypillisesti 4096). Tietokoneessa on kaksi prosessia, joissa kummassakin on 200 tavua muistia kahden sivun välein. Prosessit saattavat ajaa samaa koodia käyttäen kiinteitä osoitteita 0-199 tavun alueella, mutta niiden tukena on diskreetti fyysinen muisti niin, että ne eivät vaikuta toisiinsa. Vaikka nykyaikaiset käyttöjärjestelmät ja tietokoneet käyttävät virtuaalimuistia huomattavasti monimutkaisemmin kuin tässä esimerkissä esitetään, yllä esitetty peruslähtökohta pätee kaikissa tapauksissa. Käyttöjärjestelmät abstrahoivat osoitteet, jotka sovellus näkee niitä tukevista fyysisistä resursseista.
virtuaalisten kääntäminen fyysisiin osoitteisiin on nykytietokoneissa niin yleinen operaatio, että jos käyttöjärjestelmä olisi kaikissa tapauksissa mukana, tietokone olisi uskomattoman hidas. Moderni CPU laitteisto tarjoaa laitteen nimeltä käännös Lookaside Puskuri (TLB), joka välimuistit äskettäin käytetyt kuvaukset. Tämä mahdollistaa suorittimien tehdä osoitteen käännös suoraan laitteisto suurimman osan ajasta.
kuva 2 näyttää osoitteen käännösvirran:
- ohjelma hakee virtuaaliosoitteen.
- suoritin yrittää kääntää sen TLB: n avulla. Jos osoite löytyy, käytetään käännöstä.
- Jos osoitetta ei löydy, suoritin konsultoi joukkoa ”sivutauluja” kartoituksen määrittämiseksi. Sivutaulukot ovat joukko käyttöjärjestelmän tarjoamia fyysisiä muistisivuja paikassa, josta laitteisto ne löytää (esimerkiksi x86-laitteiston CR3-Rekisteri). Sivutaulukot kartoittavat virtuaaliosoitteet fyysisiksi osoitteiksi ja sisältävät myös metatietoja, kuten käyttöoikeuksia.
- jos sivutaulukossa on kartoitus, se palautetaan, tallennetaan välimuistiin TLB: hen ja käytetään hakuun. Jos sivutaulukossa ei ole kartoitusta, käyttöjärjestelmään nostetaan ”sivuvika”. Sivuvirhe on erityinen keskeytys, jonka avulla käyttöjärjestelmä voi ottaa ohjat käsiinsä ja päättää, mitä tehdä, kun yhdistämisestä puuttuu tai se on virheellinen. Käyttöjärjestelmä saattaa esimerkiksi lopettaa ohjelman. Se voi myös jakaa jonkin verran fyysistä muistia ja kartoittaa sen prosessiin. Jos sivuvirheiden käsittelijä jatkaa suoritusta, TLB käyttää uutta kartoitusta.
kuva 3 näyttää hieman realistisemman kuvan siitä, miltä virtuaalimuisti näyttää modernissa tietokoneessa (Pre-Meltdown — lisää tästä alla). Tässä asetelmassa on seuraavat ominaisuudet:
- ytimen muisti näkyy punaisella. Se sisältyy fyysiseen osoitealueeseen 0-99. Kernel-muisti on erikoismuisti, johon vain käyttöjärjestelmän pitäisi päästä käsiksi. Käyttäjien ohjelmien ei pitäisi pystyä käyttämään sitä.
- Käyttäjämuisti näkyy harmaana.
- Kohdistamaton fyysinen muisti näkyy sinisellä.
tässä esimerkissä alamme nähdä joitakin virtuaalimuistin hyödyllisiä ominaisuuksia. Ensisijaisesti:
- käyttäjän muisti kussakin prosessissa on virtuaalisella alueella 0-99, mutta sitä tukee erilainen fyysinen muisti.
- Ydinmuisti kussakin prosessissa on virtuaalisella alueella 100-199, mutta sen tukena on sama fyysinen muisti.
kuten lyhyesti edellisessä osassa mainitsin, jokaiselle sivulle on liitetty käyttöoikeusbittejä. Vaikka ydinmuisti on yhdistetty kuhunkin käyttäjäprosessiin, kun prosessi on käynnissä käyttäjätilassa, se ei voi käyttää ydinmuistia. Jos prosessi yrittää tehdä niin, se laukaisee sivuvian, jolloin käyttöjärjestelmä lopettaa sen. Kuitenkin, kun prosessi on käynnissä kernel-tilassa (esimerkiksi järjestelmäkutsun aikana), prosessori sallii pääsyn.
tässä vaiheessa huomaan, että tämän tyyppinen kaksoiskartoitus (jokainen prosessi, jossa ydin on yhdistetty siihen suoraan) on ollut standardi käytäntö käyttöjärjestelmän suunnittelussa yli kolmenkymmenen vuoden ajan suorituskykysyistä (järjestelmäkutsut ovat hyvin yleisiä ja ytimen tai käyttäjätilan uudelleenmuokkaus kestää kauan jokaisessa siirtymävaiheessa).
suorittimen välimuistitopologia
seuraava haavoittuvuuksien ymmärtämiseen tarvittava taustatieto on nykyaikaisten suorittimien suoritin-ja välimuistitopologia. Kuvassa 4 esitetään yleinen topologia, joka on yleinen useimmille nykyisille suorittimille. Se koostuu seuraavista komponenteista:
- suorituksen perusyksikkö on ”SUORITINKIERRE” eli ”laitteistokierre” tai ”hyperkierre.”Jokainen suorittimen säie sisältää joukon rekistereitä ja kyvyn suorittaa konekoodin virta, aivan kuten ohjelmistosäike.
- suorittimen kierteet sisältyvät ”suorittimen ytimeen.”Useimmat nykyaikaiset suorittimet sisältävät kaksi lankaa per ydin.
- nykyaikaiset suorittimet sisältävät yleensä useita tasoja välimuistia. SUORITINKIERRETTÄ lähempänä olevat välimuistitasot ovat pienempiä, nopeampia ja kalliimpia. Mitä kauempana suorittimesta ja lähempänä päämuistia välimuisti on, sitä suurempi, hitaampi ja halvempi se on.
- tyypillinen moderni SUORITINSUUNNITTELU käyttää L1 / L2-välimuistia per ydin. Tämä tarkoittaa, että jokainen ytimen suorittimen säie käyttää samoja välimuisteja.
- Useita suoritinytimiä sisältyy ”SUORITINPAKETTIIN.”Nykyaikaiset suorittimet saattavat sisältää yli 30 ydintä (60 lankaa) tai enemmän pakettia kohti.
- kaikilla paketin SUORITINYTIMILLÄ on tyypillisesti yhteinen L3-välimuisti.
- SUORITINPAKETIT mahtuvat ”pistorasioihin.”Useimmat kuluttajatietokoneet ovat yhden pistorasian, kun taas monissa datakeskuksen palvelimissa on useita pistorasioita.
spekulatiivinen toteutus
viimeinen haavoittuvuuksien ymmärtämiseen tarvittava taustatieto on nykyaikainen SUORITINTEKNIIKKA, joka tunnetaan nimellä ”spekulatiivinen toteutus.”Kuvassa 5 esitetään yleinen kaavio suoritusmoottorista nykyaikaisen suorittimen sisällä.
ensisijainen takeaway on, että nykyaikaiset suorittimet ovat uskomattoman monimutkaisia eivätkä yksinkertaisesti suorita koneen ohjeita järjestyksessä. Jokaisella suorittimen kierteellä on monimutkainen putkimoottori, joka pystyy suorittamaan ohjeet epäkunnossa. Syy tähän liittyy välimuistiin tallentamiseen. Kuten olen keskustellut edellisessä osassa, jokainen CPU käyttää useita tasoja välimuistia. Jokainen välimuisti miss lisää huomattavan määrän viiveaikaa ohjelman suoritukseen. Tämän lieventämiseksi prosessorit pystyvät suorittamaan eteenpäin ja epäkuntoon muistikuormia odotellessa. Tätä kutsutaan spekulatiiviseksi teloitukseksi. Seuraava koodinpätkä osoittaa tämän.
if (x < array1_size) {
y = array2 * 256];
}
edellisessä pätkässä kuvitellaan, että ei ole välimuistissa, mutta array1
on. Suoritin saattaa arvata (spekuloida), että x
on pienempi kuin array1_size
, ja mennä eteenpäin ja suorittaa laskelmat if-lausekkeen sisällä. Kun array1_size
on luettu muistista, suoritin voi määrittää, arvasiko se oikein. Jos niin kävi, se voi jatkaa säästäen aikaa. Jos ei, se voi heittää pois spekulatiiviset laskelmat ja aloittaa alusta. Tämä ei ole pahempaa kuin jos se olisi odottanut.
toinen spekulatiivisen toteutuksen tyyppi tunnetaan epäsuorana haaran ennustamisena. Tämä on erittäin yleistä nykyohjelmissa johtuen virtuaalisesta lähettämisestä.
class Base {
public:
virtual void Foo() = 0;
};class Derived : public Base {
public:
void Foo() override { … }
};Base* obj = new Derived;
obj->Foo();
(edellisen pätkän lähde on tämä viesti)
edellisen pätkän toteutustapa konekoodissa on ladata ”v-taulukko” tai ”virtual dispatch table” muistipaikasta, johon obj
osoittaa ja kutsuu sitä sitten. Koska tämä operaatio on niin yleinen, nykyaikaisilla suorittimilla on erilaisia sisäisiä välimuisteja ja ne usein arvailevat (spekuloivat), minne epäsuora haara menee ja jatkavat suoritusta siinä vaiheessa. Jälleen, jos CPU arvaa oikein, se voi edelleen säästää nippu aikaa. Jos ei, se voi heittää pois spekulatiiviset laskelmat ja aloittaa alusta.
Meltdown-haavoittuvuus
kun kaikki taustatiedot on nyt käyty läpi, voimme sukeltaa haavoittuvuuksiin.
Rogue data cache load
ensimmäinen haavoittuvuus, joka tunnetaan nimellä Meltdown, on yllättävän yksinkertainen selittää ja lähes triviaali hyödyntää. Exploit-koodi näyttää karkeasti ottaen seuraavilta:
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
otetaan jokainen askel edellä, kuvataan mitä se tekee ja miten se johtaa siihen, että pystyy lukemaan koko tietokoneen muistin käyttäjäohjelmasta.
- ensimmäiselle riville on varattu ”luotainjoukko”. Tämä on prosessissamme olevaa muistia, jota käytetään sivukanavana tietojen hakemiseen ytimestä. Se, miten tämä tehdään, selviää pian.
- allokaation jälkeen hyökkääjä varmistaa, ettei luotainrivistössä olevaa muistia ole välimuistissa. On olemassa erilaisia tapoja toteuttaa tämä, joista yksinkertaisin sisältää SUORITINKOHTAISET ohjeet tyhjentää muistin sijainti välimuistista.
- tämän jälkeen hyökkääjä etenee lukemaan tavun ytimen osoiteavaruudesta. Muista aiemmasta keskustelustamme virtuaalimuistista ja sivutauluista, että kaikki modernit ytimet tyypillisesti kartoittavat koko ytimen virtuaaliosoiteavaruuden käyttäjäprosessiin. Käyttöjärjestelmät luottavat siihen, että jokaisella sivutaulun merkinnällä on käyttöoikeusasetukset ja että käyttäjätilan ohjelmilla ei ole lupaa käyttää ytimen muistia. Tällainen pääsy johtaa sivuvirheeseen. Näin todellakin tapahtuu lopulta vaiheessa 3.
- nykyaikaiset suorittimet suorittavat kuitenkin myös spekulatiivista suoritusta ja suorittavat ennen faulttiohjetta. Näin ollen vaiheet 3-5 voidaan suorittaa suorittimen putkessa ennen vian nostamista. Tässä vaiheessa ytimen muistin tavu (joka vaihtelee välillä 0-255) kerrotaan järjestelmän sivukoolla, joka on tyypillisesti 4096.
- tässä vaiheessa kernelimuistin kerrottua tavua käytetään sitten koettimien joukosta lukemiseen valearvoksi. Tavun kertolasku 4096: lla on estää ”prefetcher” – nimistä SUORITINOMINAISUUTTA lukemasta enemmän dataa kuin haluamme välimuistiin.
- tässä vaiheessa suoritin on ymmärtänyt virheensä ja rullannut takaisin vaiheeseen 3. Spekuloitujen ohjeiden tulokset näkyvät kuitenkin edelleen kätköissä. Hyökkääjä käyttää käyttöjärjestelmän toimintoja ansoittaakseen virheohjeen ja jatkaakseen suoritusta (esim.sigfault ’ n käsittely).
- vaiheessa 7 hyökkääjä iteroi läpi ja näkee, kuinka kauan kestää lukea jokainen luotaimen 256 mahdollisesta tavusta, jotka ytimen muisti olisi voinut indeksoida. Suoritin on ladannut yhden sijainneista välimuistiin ja tämä sijainti latautuu huomattavasti nopeammin kuin kaikki muut sijainnit (jotka on luettava päämuistista). Tämä sijainti on tavun arvo ytimen muistissa.
käyttämällä edellä mainittua tekniikkaa ja sitä, että nykyaikaisten käyttöjärjestelmien vakiokäytäntönä on kartoittaa kaikki fyysinen muisti ytimen virtuaaliosoiteavaruuteen, hyökkääjä voi lukea tietokoneen koko fyysisen muistin.
nyt saatat ihmetellä: ”sanoit, että sivutaulukoissa on lupabittejä. Miten voi olla, että käyttäjätila koodi pystyi spekulatiivisesti käyttää ytimen muistia?”Syynä on vika Intel-prosessoreissa. Mielestäni ei ole mitään hyvää syytä, suoritusta tai muutakaan, että tämä olisi mahdollista. Muista, että kaiken virtuaalimuistin käytön on tapahduttava TLB: n kautta. Spekulatiivisen suorituksen aikana on helposti mahdollista tarkistaa, että välimuistiin tallennetulla yhdistämisellä on käyttöoikeudet, jotka ovat yhteensopivia käynnissä olevan käyttöoikeustason kanssa. Intel hardware ei yksinkertaisesti tee tätä. Muut prosessoritoimittajat tekevät lupatarkistuksen ja estävät spekulatiivisen suorituksen. Siten, sikäli kuin tiedämme, Meltdown on Intel vain haavoittuvuus.
Edit: näyttää siltä, että ainakin yksi ARM-prosessori on myös altis Sulamiselle, kuten tässä ja tässä ilmoitetaan.
Meltdown mitigations
Meltdown on helppo ymmärtää, triviaali hyödyntää, ja onneksi sillä on myös suhteellisen suoraviivainen lieventäminen (ainakaan käsitteellisesti ytimen kehittäjät eivät välttämättä ole samaa mieltä siitä, että se on helppo toteuttaa).
Kernel page table isolation (Kpti)
muista, että virtuaalimuistia koskevassa osiossa kuvasin, että kaikki nykyaikaiset käyttöjärjestelmät käyttävät tekniikkaa, jossa kernel-muisti on yhdistetty jokaiseen käyttäjätilaan prosessoimaan virtuaalimuistin osoiteavaruutta. Tämä on sekä suorituskykyä ja yksinkertaisuus syistä. Se tarkoittaa, että kun ohjelma tekee järjestelmäkutsun, ydin on valmis käytettäväksi ilman sen kummempaa työtä. Sulamisen ratkaisu on, ettei tätä kaksoismerkintää enää tehdä.
kuva 6 esittää tekniikkaa, jota kutsutaan kernel page Table isolationiksi (kpti). Tämä pohjimmiltaan kiteytyy siihen, ettei kernel-muistia kartoiteta ohjelmaksi, kun se on käynnissä käyttäjäavaruudessa. Jos kartoitusta ei ole läsnä, spekulatiivinen toteutus ei ole enää mahdollista ja tulee heti vika.
käyttöjärjestelmän virtual memory Managerin (VMM) monimutkaistamisen lisäksi ilman laitteistoapua tämä tekniikka myös hidastaa huomattavasti työmääriä, jotka tekevät suuren määrän käyttäjätilasta kernel-tilaan siirtymisiä, johtuen siitä, että sivutaulukoita on muutettava jokaisen siirtymän yhteydessä ja TLB on huuhdeltava (koska TLB voi pitää kiinni tunkkaisista kartoituksista).
uudemmissa x86-suorittimissa on ominaisuus, joka tunnetaan nimellä ASID (address space ID) tai PCID (process context ID), jonka avulla tehtävästä voidaan tehdä huomattavasti halvempi (ARM ja muut mikroarkkitehtuurit ovat olleet tämä ominaisuus jo vuosia). PCID sallii ID: n yhdistämisen TLB-tietueeseen ja sen jälkeen TLB-tietueiden huuhtelemisen kyseisellä ID: llä. PCID: n käyttö tekee KPTI: stä halvempaa, mutta ei silti ilmaista.
tiivistettynä Meltdown on äärimmäisen vakava ja helposti hyödynnettävä haavoittuvuus. Onneksi se on suhteellisen yksinkertainen lieventäminen, joka on jo otettu käyttöön kaikki suuret OS toimittajat, varoitus on, että tietyt työkuormat ajaa hitaammin, kunnes tulevaisuudessa laitteisto on nimenomaisesti suunniteltu osoite avaruuden erottaminen kuvattu.
Spectre-haavoittuvuus
Spectre jakaa joitakin Meltdownin ominaisuuksia ja koostuu kahdesta muunnoksesta. Meltdownista poiketen Spectre on huomattavasti vaikeampi hyödyntää, mutta vaikuttaa lähes kaikkiin viimeisen kahdenkymmenen vuoden aikana valmistettuihin moderneihin suorittimiin. Pohjimmiltaan Spectre on hyökkäys modernia suoritin-ja käyttöjärjestelmäsuunnittelua vastaan tiettyä tietoturvahaavoittuvuutta vastaan.
Bounds check bypass (Spectre variant 1)
ensimmäinen Spectre-variantti tunnetaan nimellä ”bounds check bypass.”Tämä näkyy seuraavassa koodinpätkässä (joka on sama koodinpätkä, jota käytin esitellessäni spekulatiivisen suorituksen yllä).
if (x < array1_size) {
y = array2 * 256];
}
edellisessä esimerkissä oletetaan seuraava tapahtumasarja:
- hyökkääjä hallitsee
x
. -
array1_size
ei ole välimuistissa. -
array1
on välimuistissa. - suorittimen arvailujen mukaan
x
on pienempi kuinarray1_size
. (Suorittimet käyttävät erilaisia patentoituja algoritmeja ja heuristiikkoja päättääkseen spekuloidako, minkä vuoksi Spectren hyökkäystiedot vaihtelevat prosessoritoimittajien ja mallien välillä.) - suoritin suorittaa if-lausekkeen rungon odottaessaan
array1_size
lataamista, vaikuttaen välimuistiin samalla tavalla kuin sulaminen. - hyökkääjä voi sitten määrittää
array1
todellisen arvon jollakin eri menetelmällä. (Katso tutkimus paperi lisätietoja cache päättely hyökkäyksiä.)
Spectre on huomattavasti vaikeampi hyödyntää kuin Meltdown, koska tämä haavoittuvuus ei riipu etuoikeuksien lisääntymisestä. Hyökkääjän täytyy vakuuttaa ydin ajamaan koodia ja spekuloimaan väärin. Tyypillisesti hyökkääjän täytyy myrkyttää spekulaatiomoottori ja huijata se arvaamaan väärin. Tästä huolimatta tutkijat ovat osoittaneet useita todisteita-of-concept hyödyntää.
haluan toistaa, miten uskomaton löytö tämä uroteko on. En henkilökohtaisesti pidä tätä CPU suunnitteluvirhe kuten Meltdown sinänsä. Pidän tätä perustavanlaatuisena paljastuksena siitä, miten nykyaikaiset laitteet ja ohjelmistot toimivat yhdessä. Se, että SUORITINKÄTKÖJÄ voidaan käyttää epäsuorasti käyttömallien oppimiseen, on ollut tiedossa jo jonkin aikaa. Se, että SUORITINKÄTKÖJÄ voidaan käyttää sivukanavana tietokoneen muistin dumppaamiseen, on ällistyttävää sekä käsitteellisesti että seurauksiltaan.
haaran kohderuiskutus (Spectre variant 2)
muista, että epäsuora haarautuminen on hyvin yleistä nykyohjelmissa. Spectren muunnelma 2 käyttää epäsuoraa haaran ennustusta MYRKYTTÄÄKSEEN suorittimen spekulatiivisesti suoritettavaksi muistipaikkaan, jota se ei olisi muuten koskaan suorittanut. Jos näiden ohjeiden suorittaminen voi jättää tilan taakse välimuistiin, joka voidaan havaita välimuistin päättelyhyökkäyksillä, hyökkääjä voi sitten dumpata kaiken ytimen muistin. Kuten Spectre variant 1, Spectre variant 2 on paljon vaikeampi hyödyntää kuin Meltdown, mutta tutkijat ovat osoittaneet working proof-of-concept hyödyntää variant 2.
spektrin lieventämiset
spektrin lieventämiset ovat huomattavasti kiinnostavampia kuin sulamisen lieventäminen. Akateeminen Spectre-lehti kirjoittaakin, että tällä hetkellä tiedossa ei ole lieventäviä asianhaaroja. Näyttää siltä, että kulissien takana ja akateemisen työn rinnalla Intel (ja luultavasti muut CPU-toimittajat) ja suuret käyttöjärjestelmä-ja pilvipalvelutoimittajat ovat työskennelleet raivokkaasti kuukausien ajan kehittääkseen lieventäviä asianhaaroja. Tässä jaksossa käsittelen erilaisia lievennyksiä, joita on kehitetty ja otettu käyttöön. Tämä on osa olen eniten epäselvä, koska se on uskomattoman vaikea saada tarkkoja tietoja, joten olen paloittelee asioita yhteen eri lähteistä.
staattinen analyysi ja miekkailu (muunnelma 1 lieventäminen)
ainoa tunnettu muunnelma 1 (bounds check bypass) lieventäminen on koodin staattinen analysointi, jolla määritetään koodisekvenssejä, joita hyökkääjä voi ohjata häiritsemään spekulaatiota. Haavoittuviin koodisekvensseihin voi lisätä sarjamuotoisen ohjeen, kuten lfence
, joka pysäyttää spekulatiivisen toteutuksen, kunnes kaikki aitaan asti ulottuvat ohjeet on toteutettu. Aitaohjeita asetettaessa on oltava varovainen, sillä liian monella voi olla vakavia suorituskykyvaikutuksia.
Retpoline (variant 2 mitigation)
ensimmäinen Spectre variant 2 (branch target injection) – hillintä on Googlen kehittämä ja tunnetaan nimellä ”retpoline.”Minulle on epäselvää, onko sen kehittänyt erillisenä Google vai Google yhteistyössä Intelin kanssa. Haluaisin spekuloida, että se oli kokeellisesti kehitetty Google ja sitten tarkistaa Intel laitteisto insinöörit, mutta en ole varma. Yksityiskohdat ”retpoline” lähestymistapa löytyy Googlen paperi aiheesta. Tiivistän ne tässä (olen glossing yli joitakin yksityiskohtia, kuten underflow, jotka on peitetty paperi).
Retpoline luottaa siihen, että funktioista kutsuminen ja palaaminen sekä niihin liittyvät pinomanipulaatiot ovat tietokoneohjelmissa niin yleisiä, että suorittimet on optimoitu voimakkaasti niiden suorittamiseen. (Jos et ole perehtynyt miten pino toimii suhteessa soittaminen ja palaavat toiminnot tämä viesti on hyvä pohjamaali.) Pähkinänkuoressa, kun” puhelu ” suoritetaan, palautusosoite työnnetään pinoon. ”ret” poksauttaa palautusosoitteen pois ja jatkaa suoritusta. Spekulatiivinen suoritus laitteisto muistaa työnnetyn palautusosoitteen ja spekulatiivisesti jatkaa suoritusta siinä vaiheessa.
retpoliinin konstruktio korvaa epäsuoran hypyn rekisteriin tallennettuun muistipaikkaan r11
:
jmp *%r11
:
call set_up_target; (1)
capture_spec: (4)
pause;
jmp capture_spec;
set_up_target:
mov %r11, (%rsp); (2)
ret; (3)
katsotaan, mitä edellinen kokoonpanokoodi tekee askel kerrallaan ja miten se lieventää haaran kohderuiskutusta.
- tässä vaiheessa koodi kutsuu muistipaikkaa, joka tunnetaan käännösaikaan, joten se on kova koodattu offset eikä epäsuora. Tämä asettaa
capture_spec
paluuosoitteen pinoon. - puhelun palautusosoite korvataan varsinaisella hyppymaalilla.
- paluu suoritetaan todelliselle kohteelle.
- kun suoritin spekulatiivisesti suorittaa, se palaa äärettömäksi silmukaksi! Muista, että suoritin spekuloi eteenpäin, kunnes muistikuormitus on valmis. Tässä tapauksessa spekulaatiot on manipuloitu vangittavaksi äärettömään silmukkaan, jolla ei ole hyökkääjälle havaittavia sivuvaikutuksia. Kun suoritin lopulta suorittaa todellisen tuoton, se keskeyttää spekulatiivisen suorituksen, jolla ei ollut vaikutusta.
mielestäni tämä on todella nerokas lievennys. Kunnia sen kehittäneille insinööreille. Haittapuolena tässä lieventämisessä on se, että se vaatii kaikkien ohjelmistojen kääntämistä uudelleen siten, että epäsuorat haarat muunnetaan retpoline-haaroiksi. Googlen kaltaisille pilvipalveluille, jotka omistavat koko pinon, uusiminen ei ole iso juttu. Toisille se voi olla hyvin iso juttu tai mahdotonta.
IBRS, STIBP ja IBPB (variant 2 mitigation)
näyttää siltä, että samanaikaisesti retpolinen kehityksen kanssa Intel (ja AMD jossain määrin) ovat työskennelleet raivokkaasti laitteistomuutosten parissa lieventääkseen haaran kohderuiskutushyökkäyksiä. Kolme uutta laitteiston ominaisuuksia toimitetaan CPU microcode päivitykset ovat:
- epäsuoran haaran rajoitettu spekulointi (Ibrs)
- yhden kierteen epäsuoran haaran Ennustusaineet (STIBP)
- epäsuoran haaran Ennustuseste (IBPB)
rajoitetusti tietoa uusista mikrokoodiominaisuuksista on saatavissa Inteliltä täältä. Olen voinut karkeasti koota yhteen, mitä nämä uudet ominaisuudet tekevät lukemalla edellä mainitut asiakirjat ja katsomalla Linux-ytimen ja Xen hypervisor laastaria. Analyysini mukaan jokaista ominaisuutta käytetään mahdollisesti seuraavasti:
- IBRS sekä huuhtoo Branch prediction-välimuistin etuoikeustasojen välillä (käyttäjästä ytimeen) että poistaa branch predictionin sisarpuolen suorittimen säie. Muista, että jokaisessa suorittimen ytimessä on tyypillisesti kaksi suorittimen kiertettä. Näyttää siltä, että nykyaikaisissa suorittimissa haaran ennustuslaitteisto jaetaan kierteiden kesken. Tämä tarkoittaa sitä, että paitsi käyttäjätilan koodi voi myrkyttää haaran ennustimen ennen ytimen koodin syöttämistä, myös sisarpuolen suorittimen kierteellä kulkeva koodi voi myrkyttää sen. Ibrs: n ottaminen käyttöön ydintilassa estää olennaisesti aikaisemman suorituksen käyttäjätilassa ja suorituksen sisarprosessorikierteellä vaikuttamasta haaran ennustamiseen.
- STIBP näyttää olevan ibrs: n osajoukko, joka vain poistaa haaran ennustamisen sisarpuolen suorittimen säie. Sikäli kuin voin kertoa, tärkein käyttötapaus tämän ominaisuuden on estää sisar CPU säiettä myrkyttämästä haara predictor kun käynnissä kaksi eri käyttäjätilan prosesseja (tai virtuaalikoneita) samalla CPU core samanaikaisesti. Minulle ei ole täysin selvää, milloin STIBP: tä pitäisi käyttää.
- IBPB näyttää huuhtovan haaran ennustusvälimuistin samalla etuoikeustasolla kulkevalle koodille. Tätä voidaan käyttää, kun vaihdat kahden käyttäjätilan ohjelman tai kahden virtuaalikoneen välillä varmistaaksesi, että edellinen koodi ei häiritse käynnissä olevaa koodia (vaikka ilman STIBP: tä uskon, että sisarpuolen PROSESSORIKIERTEELLÄ kulkeva koodi voi silti myrkyttää haaran ennustimen).
tätä kirjoitettaessa tärkeimmät lievennykset, joita näen toteutettavan haaran kohderuiskutushaavoittuvuuden osalta, näyttävät olevan sekä retpoline että IBRS. Oletettavasti tämä on nopein tapa suojata ydin käyttäjätilan ohjelmilta tai hypervisor virtuaalikonevierailta. Tulevaisuudessa odottaisin, että sekä STIBP että IBPB otetaan käyttöön riippuen eri käyttäjätilan ohjelmien vainoharhaisuudesta, jotka häiritsevät toisiaan.
ibrs: n hinta näyttää myös vaihtelevan erittäin paljon SUORITINARKKITEHTUURIEN välillä uudempien Intel Skylake-suorittimien ollessa suhteellisen halpoja vanhempiin suorittimiin verrattuna. Lyftillä näimme noin 20% hidastumisen tiettyjen järjestelmäkutsujen raskailla työkuormilla AWS C4-tapauksissa, kun lievennykset otettiin käyttöön. Haluaisin spekuloida, että Amazon rullannut ibrs ja mahdollisesti myös retpoline, mutta en ole varma. Näyttää siltä, että Google on saattanut vain rullata retpoline niiden pilvi.
ajan myötä odottaisin prosessorien siirtyvän lopulta ibrs: n ”aina päällä” – malliin, jossa laitteisto vain oletusarvoisesti puhdistaa haaran predikaattorin erotuksen suorittimen kierteiden välillä ja huuhtelee oikein tilan etuoikeustason muutoksia. Ainoa syy, miksi tätä ei tehdä tänään, on ilmeiset suorituskykykustannukset, jotka aiheutuvat tämän toiminnon jälkiasentamisesta jo julkaistuihin mikroarkkitehtuureihin mikrokoodipäivitysten kautta.
johtopäätös
on hyvin harvinaista, että tutkimustulos muuttaa oleellisesti tietokoneiden rakennetta ja toimintaa. Meltdown ja Spectre ovat tehneet juuri niin. Nämä havainnot muuttavat laitteistojen ja ohjelmistojen suunnittelua merkittävästi seuraavien 7-10 vuoden aikana (seuraava CPU-laitteistosykli), kun suunnittelijat ottavat huomioon uuden todellisuuden välimuistin sivukanavien kautta tapahtuvan tietovuodon mahdollisuuksista.
tällä välin sulamis-ja Spectre-löydöksillä ja niihin liittyvillä lievennyksillä on merkittäviä vaikutuksia tietokoneen käyttäjiin tulevina vuosina. Lyhyellä aikavälillä lievennykset vaikuttavat suorituskykyyn, joka voi olla huomattava työmäärästä ja laitteistosta riippuen. Tämä saattaa edellyttää toiminnallisia muutoksia joissakin infrastruktuureissa (esimerkiksi Lyftillä siirrämme aggressiivisesti joitakin työkuormia AWS C5-instansseihin, koska IBRS näyttää toimivan huomattavasti nopeammin Skylake-prosessoreilla ja uusi Nitro hypervisor tarjoaa keskeytyksiä suoraan vieraille SR-IO: n ja APICv: n avulla, poistaen monia virtuaalikoneen uloskäyntejä IO: n raskaille työkuormille). Pöytätietokoneen käyttäjät eivät ole immuuneja myöskään, koska proof-of-concept selainhyökkäykset käyttäen JavaScript että OS ja selaimen toimittajat pyrkivät lieventämään. Lisäksi haavoittuvuuksien monimutkaisuuden vuoksi on lähes varmaa, että tietoturvatutkijat löytävät uusia hyväksikäyttöjä, joita nykyiset lievennykset eivät kata ja jotka on paikattava.
vaikka rakastan työskentelyä Lyftillä ja tunnen, että työ, jota teemme microservice systems-infrastruktuuritilassa, on vaikuttavinta työtä, mitä alalla tehdään juuri nyt, tällaiset tapahtumat saavat minut kaipaamaan käyttöjärjestelmien ja hypervisorien parissa työskentelyä. Olen äärimmäisen kateellinen siitä sankarillisesta työstä, jota valtava määrä ihmisiä teki viimeisen puolen vuoden aikana haavoittuvuuksien tutkimisessa ja lieventämisessä. Olisin halunnut olla osa sitä!
Further reading
- Meltdown and Spectre academic papers: https://spectreattack.com/
- Google Project Zeron blogikirjoitus: https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html
- Intel Spectre hardware mitigations: https://software.intel.com/sites/default/files/managed/c5/63/336996-Speculative-Execution-Side-Channel-Mitigations.pdf
- retpolinen blogikirjoitus:
- hyvä yhteenveto tunnetuista tiedoista: https://github.com/marcan/speculation-bugs/blob/master/README.md