Nestačí, protože Spectre útok umožňuje přečíst cokoliv z paměti procesu. Takže javascript spuštěný v prohlížeči může teoreticky přečíst z paměti prohlížeče heslo, které zadávám na úplně jiné stránce. Obecně je nutné znovu přeložit všechno, co zpracovává nějaký nedůvěrýhodný vstup (stdin, soubor, data ze sítě...). Takže prakticky všechno.
Spectre bug není o sahání do paměti jádra, Spectre umožňuje jednomu procesu číst pamět jiného procesu tak, že attacker proces podstrčí victim procesu vhodný (chybný) vstup, který způsobí spekulativní vykonání kódu ve victim procesu (např. čtení mimo meze pole, to se spekulativně vykoná, i když kontrola mezí pole neprojde) a side kanálem si potom attackter proces přečte paměť victim procesu.
Victim process se musí opatchovat (znovu přeložit s patřičnými přepínači GCC), aby takový spekulativní kód (čtení mimo meze pole) nikdy nevykonával.
Mě teda přijde Spectre že funguje jen v rámci procesu (což pořád neeliminuje javascript běžící v prohlížeči). Nějak jsem nepochopil, jak na útočníkově straně najdu tu stránku, kam oběť spekulativně přistoupila, protože máme izolované adresové prostory.
Strana oběti také musí buď obsahovat kód speciálního patternu, který jsem schopen ovládat, byť obsahuje kontrolu rozsahů. nebo má možnost vykonat můj kód, i přesto, že je zabezpečen kontrolou rozsahů. Vše ale v rámci jednoho procesu. Tedy buď do oběti přesunu i měření přístupu na stránky, nebo zase v procesu oběti najdu kód, který mi přístup do mou vybrané stránky umožní a já přitom dokážu změřit, jak dlouho to trvalo
tl;rd - útočník musí mít v procesu oběti zajištěny dvě věci
1) kód na kterým lze exekuovat exploit
2) kód který pak přistupuje na spekulativně natažené stránky v cache se schopnosti měřit čas přístupu.
Bez zaruky na spravnost mojej odpovede ma napadaju veci ako shared memory alebo pamatovo mapovane subory. Neviem sice aky efekt bude mat volanie flush napr. na stranku pamate v ktorej je ulozeny kod zdielanej kniznice, ale ak bude nenulovy, potom je osa utoku v skutocnosti primitivna:
1) loadnut si kniznicu, ktoru loaduje aj obet, ziskat memory mapping tej kniznice v pamati obete (to moze byt netrivialne, pripustam)
2) flushnut stranku zodpovedajucu nejakej deterministickej adresy v zavislosti od kodu, ktory sposobi side-efekt
3) donutit obet vykonat kod, ktory sposobi side-efekt
4) zistit, ci je stranka v cache, alebo nie
Problemom moze byt, ze ak sa bude flushovat nejaka often used stranka, tento proces bude mat asi znacnu chybovost vystupu, to sa ale da vzdy poladit.
Spectre v principu funguje i mezi oddělenými procesy, stačí k tomu, aby se ve victim procesu nacházel např. nějaký takový kód:
if (x < array1_size)
y = array2[array1[x] * 256];
Victim proces se donutí, aby spekulativně provedl přístup do paměti s x větším než array1_size a přečtená paměť zůstane v cache. Potom stačí uvedený kód zavolat znovu s náhodným x menším než array1_size. Pokud je to rychlé (je to v cache od minula ze spekulativního čtení), tak vím že jsem se trefil a znám hodnotu paměti array1[prvni_x_mimo_meze]. Pokud to trvá dlouho, tak jsem se netrefil, vyprázdním cache a zkouším to znovu. Není potřeba sdílená paměť ani nic dalšího speciálního.
Tohle je jen jeden z mnoha možných vektorů útoku, možností jak zkonstruovat postranní kanál je mnohem víc. Hezky je to popsáno ve Spectre paperu.
Nepotřebuju žádná složitá sychronizační primitiva, stačí atomic proměnná, která sice způsobí cache miss u jiných CPU jader, ale projeví se to jenom na konktétní cache line, ke které victim proces nepřistupuje. Pro tento typ útoku více než dostatečné. Nicméně je to stejně hypotetická debata, možností jak přesně měřit čas je spousta.
Spectre právě sandboxing umí obejít. Ten buffer overflow nastane jen spekulativně, např. v korektním kódu:
char a[10];
if (b < 10) {
c = a[b];
}
Nikdy teoreticky přístup mimo meze pole nemůže nastat. Ale CPU začně výraz c = a[b] spekulatvně provádět ještě předtím, než zná výsledek testu if (b < 10). Takže fyzicky proběhne čtení mimo meze pole. Ten výsledek se zahodí hned potom, co CPU vyhodnotí podmínku (b < 10) jako false, ale to už je pozdě, protože spekulativní vykonávání čtení mimo meze pole už ovlivnilo vnitřní stav procesoru, mohla se např. změnit cache v závislosti na výsledku a[b], což může být možné side kanálem přečíst a vyhodnotit z jiného procesu.
Jenze tady se zadnej kod z pohledu aplikace nevykona, on se vykona jen z pohledu CPU, kterej to proste zkusi, nacez se zjisti, ze to padne/nejsou na to prava/neni splnena podminka... ale vysledek uz je stejne v cache.
Jinak receno, kdyz mas v kodu neco jako
if () a+b else a*b
Tak se bez ohledu na vysledek podminky spocita oboji (proto aby po vyhodnoceni podminky byl uz vysledek hotovej a necekalo se) a oba vysledky lze ziskat, coz je prave ten pruser. A viz lopata, mezi CPU a RAM neni zadnej plot, CPU proste muze cist kdekoli cokoli. Takze ti i nacte casti pameti ktery lezej mimo hranice kam ma pristup aplikace. Ty sice vysledek neziskas primo, ale muzes si ho najit v cache. Jednoduse proto, ze CPU proste ty data nacet, co kdyby.
> proč nestačí to cache invalidovat vždy jen v takovém případě
Protože tím vytvoříš úplně stejný side channel - kvůli omezené asociativitě vyhodíš nějaký jiný (dost konkrétní) řádek a útočníkovi pak stačí zjistit, který řádek se nenacachoval (oproti současnému, kdy zjišťuje, který řádek se nacachoval).
Podle mě to má jediné řešení, i při prefetchingu kontrolovat oprávnění. Netuším, jaký to bude mít dopad na výkon.
Ta výjimka se nevyvolá. Útočník natrénuje optimalizátor CPU, že podmínka:
if (a < 10) b = pole1[pole2[a]];
má očekávat a < 10, a CPU pak bude spekulativně číst stránku, kam ukazuje pole1[pole2[a]]
. Pak útočníkovi stačí předat vyšší a, CPU to spekulativně vykoná (je natrénované, že v naprosté většině případů a < 10), načte stránku, kam by to sáhlo, ale výjimku nevyvolá, protože to se testuje až při commitu (jinak by to házelo výjimky pořád). Při commitu if (a < 10)
se ale zjistí, že se to nemělo vykonávat, a tak se vykonaný spekulativní kód zahodí. Jenže ta stránka s adresou odpovídající hodnotě na pole2[a]
(což může odkazovat na bajt kdekoliv v adresovatelné paměti) už v cache zůstane a přítomnost té stránky lze detekovat.
Spectre nedokáže číst paměť jiného procesu, ale jádra či v rámci jednoho procesu. Procesy mají oddělené mapování, nelze z jednoho procesu testovat adresní rozsah jiného, a to ani spekulativně. Jádro je ale namapované do paměti každého procesu, a je sice chráněné ringy, takže nelze číst přímo, ale lze vyvolat spekulativní spuštění kódu v jádře, což ringy nekontroluje, vhodný kód natáhne do cache stránku s adresou odpovídající bajtu paměti, kterou chceme přečíst, a která stránka byla natažena, lze odvodit, protože čtení takové stránky bude oproti ostatním velmi rychlé (pro konkrétní bajt je již v cache, pro ostatní ne). U sandboxu řešeného kontrolou přístupů k paměti (typicky hromadou if
ů) lze takto vyvolat spekulativní čtení mimo sandbox, které to sice neprovede (ten if
selže a třeba to vyhodí výjimku), ale opět se tím natáhne do cache stránku s adresou odpovídající nějakému bajtu.
V jednom procesu nejsou virtuální adresy mapovány na celou RAM, ale jen na stránky, ke kterým má ten proces přístup, a jádro. Při přepínání procesů se pak mění mapování virtuálních stránek. Mapovat to jinak ani nejde, protože v rámci mapování stránek se stránky oddělují jen přes ringy a všechny procesy běží ve stejném ringu, takže pokud by byla mapována paměť víc procesů, buď by při každém přepínání muselo jádro přepisovat ringy v té mapě (což je hrozně pomalé, když stačí jen změnit ukazatel na mapu) nebo by procesy mohly lézt do stránek ostatních procesů vždy.
Z pohledu distra urcite ne, ale z pohledu uzivatelu to asi celkem ranec bude, protoze pokud to pustej vsechno najednou, bude to kompleti reinstal vseho. A pak je jeste otazka, jestli rekompilujou vsechny vydany verze, protoze nekdo muze pouzivat nejakou konkretni kvuli nejakymu chovani/bugu/... v novejsi.