Řešení v budoucnu?
Je otázkou, jestli spekulativní exekuce by měla pokračovat v případě, že je třeba čekat na data z main memory. Si říkám, že tam nějaké velké urychlení nebude. Jsou to stovky cyklů. Prostě pokud se při spekulativním provádění narazí na čtení z paměti, která nemá v cache, tak se fronta zastaví, dokud není rozhodnuto, jakým dalším směrem bude kód pokračovat. Tohle omezení lze software obejít tak, že spekulativní čtení paměti zařídím v kódu (prostě načtu i ty proměnné, které pak nejsou použity)
Jenomže právě tím spekulativní čtením dopředu se dosahuje značného zrychlení zpracování, a prostě se to v té cache nestačí zneplatnit nebo se to neřeší správně a ta data tam zůstávají, i když by tam zůstat neměla.
Obávám se, že jde o problém hluboko v návrhu a to nejde snadno odstranit/obejít patchem v mikrokodu
Ta data tam zustanou jednoduse proto, ze prepis = snizeni vykonu cache na 1/2. Coz ostatne ukazujou i ty workaroundy, ktery vedou na -30% vykonu celyho CPU prave tim, ze tu cache mazou. Coz je teda asi vyrazne pomalejsi nez kdyby si to resil primo CPU per zaznam, ale i tak bys mel trebas -10%.
RAM žádnou cache nemá, jede rychlostí FSB a rychleji už s procesorem komunikovat nemůže.
L3/L4 cache je už teď částečně transakční, stačí přidat možnost mít dočasně načtená data, která jiná jádra nevidí. Pokud by byla plně ACID, tak by tam byl side-channel, že při spekulativním běhu bude čtení z jiného jádra trvat trochu déle (než se spekulativní běh zahodí a paměťová transakce rollbackne), ale nemyslím si, že by to vůbec bylo měřitelné (musíte se trefit přesně do spekuliativního běhu a měřit extrémně přesně, jde o pár taktů, než CPU vyhodnotí ten if
, ne několik set jako u čtení RAM), a navíc to jde řešit ignorováním atomicity (každé jádro bude mít svoji kopii stejné stránky v dočasné L3 a při commitu se bez kontroly přepíší), konkurenční přístup bez memory barrier (které to celé serializují, takže to nejde měřit) stejně nemá zaručený výsledek.
Nebo to jde řešit tak, že se načítá přímo do L2 a do L3/L4 se to zapíše až při commitu.
Záleží na implementaci transakcí. Ono by mohlo stačit si spekulativně poznačit changelog a jen v případě commitu to zapsat do cache. Otázkou jsou race conditions ti sdílené cache (jádro X by načetlo kus paměti, jádro Y by na totéž místo zapsalo, jádro X by následně commitnulo "novou" hodnotu do sdílené cache), ale to by asi bylo řešitelné, třeba skrze nějaké monotoní hodiny.
Pořád to ale neřeší fakt, že se spekulativně přistupuje do RAM, jen to řeší cache. Mohl by tu být nějaký jiný side channel.
Ten konkurenční přístup jsem právě uváděl, bez memory barrier stejně není zaručené, jak se to poskládá, takže klidně může X načíst starou hodnotu a přepsat novou hodnotou Y, s memory barrierami se serializuje přístup k celé paměti, takže nejde detekovat konkrétní stránku, kam spekulace sáhla.
Hypoteticky můžete nějakým side-channelem přečíst, co se posílá z RAM do cache, a máte přístup ke všem datům na systému. Vymýšlet zabezpečení proti takovým hrozbám ale není moc přínosné, když chybí teorie, jak by se to mělo udělat, takže se nemáte proti čemu bránit.
> Ten konkurenční přístup jsem právě uváděl, bez memory barrier stejně není zaručené, jak se to poskládá
A jste si jist, že je na x86 OK nevidět vlastní zápis? Protože přesně to by se mohlo stát: něco zapíšu, ale protože při tom jiné jádro z téhož místa četlo, po čase (až to vypadne z cache, která je per-core) uvidím původní hodnotu, která bude ve sdílené cache. A po čase třeba uvidí zpět hodnotu, kterou zapsal.
Neznám do detailů memory model x86, ale intuitivně toto asi nebude korektní chování.
> Hypoteticky můžete nějakým side-channelem přečíst, co se posílá z RAM do cache
O přímém přečtení samotných dat jsem ale nepsal, „pouze“ o zjištění, kam se (spekulativně) přistupuje. To mi nepřijde až tak nereálné. Vzpomněl jsem si na bank conflict. OK, to není x86, ale Nvidia CUDA, ale se znalostí bližších detailů x86 by se mohlo něco najít i tady.
> Vymýšlet zabezpečení proti takovým hrozbám ale není moc přínosné, když chybí teorie, jak by se to mělo udělat, takže se nemáte proti čemu bránit.
Místo toho můžeme teoretické hrozby ignorovat a čekat, než se z nich stanou praktické. Jak se už nejednou stalo. Ostatně u branch prediction mě kdysi napadlo, že jde o teoretický problém, akorát jsem neměl nápad, jak útok převést do praxe. A je tu spousta jiných útoků, které byly původně označeny za čistě teotetické problémy. Třeba CRIME/BREACH byla často vytýkána teotetičnost, ale – zvlášt v kombinaci s HEIST – se to stává mocným nástrojem.
Je to OK, pokud do stejné cache line bude zapisovat více jader bez synchronizace, není výsledek definovaný. To, že neuvidíte svůj zápis, je ještě poměrně konzistentní stav, můžete se stát, že jedno číslo bude mít některé bajty z jednoho vlákna a zbylé z druhého (podle toho, jak se to zrovna seřadí na sběrnici).
Jaký teoretický problém vás napadl u branch prediction (při branch prediction by mohlo jít …)? Nebo to spíš obecné varování, že to může být nebezpečné (branch prediction by mohlo způsobovat problémy, ale nemám nejmenší tušení, jak a kde)? Proti tomu prvnímu se dá bránit a nepotřebujete praktický útok, velká část mechanizmů v kryptografii je založena na teoretických útocích. Proti tomu druhému se bránit nedá, protože je to tak obecné, že jediné řešení je tu technologii vůbec nepoužít.
Ale já nepsal o situaci, kdy na jedno místo zapisuje více vláken bez synchronizace. Já psal o situaci, kdy na to místo jedno vlákno zapisuje a jiné jedno (nebo více) čte.
Neříkám, že jde o typický scénář korektního kódu. Za současné situace může čtecí vlákno přečíst ledacos, což typicky není žádaný výsledek. Nově by to ale mohlo mít vliv i na zapisovací vlákno.
Kde by to mohlo mít vliv:
* Sběr entropie zahashováním paměti. OK, je to punkové řešení, které už teď má potenciální problémy, ale tímto byste přidal nové úplně jiného druhu.
* Buffer overflow při čtení by mohlo mít dopad i na obsah paměti.
* Memory dump neatomickým způsobem (za běhy programu) by mohl ovlivnit chod programu.
Uznávám, nic ze zmíněných příkladů se netýká vzorně napsaného kódu, ale zvyšuje to dopady některých implementačních nebo návrhových nedostatků mimo tradiční meze. A dost možná by to změnilo memory model na něco, co není kompatibilní se současnou specifikací.
K branch prediction: Napadlo mě, že spekulativní vykonání nějaké větve může mít vliv na cache, což by se dalo zneužít k nějakému side channelu. Nevěnoval jsem tomu pozornost. Teď ale někdo vymyslel, jak to dotáhnout do konce.