- předchozí článek - následující článek - obsah -

Linuxové noviny Prosinec 1997

Chyba v procesorech Intel Pentium - softwarové řešení

Jan Kasprzak, 21. listopadu 1997

V předchozím čísle Linuxových novin byla uveřejněna zpráva o tom, že byla objevena nová chyba v procesorech Intel Pentium. Tato chyba umožňuje běžnému uživateli zablokovat procesor tak, že je nutno provést studený start počítače. Této chybě se začalo říkat "F00F bug" podle hodnoty prvních dvou bajtů chybné instrukce.

Dnes je situace podstatně lepší. Existuje způsob, jak softwarově danou chybu odstínit. Stojí za povšimnutí, že k dnešnímu dni (21. listopadu 1997) byla podle stránky společnosti Intel http://support.intel.com/support/processors/pentium/ppiie/software.htm k dispozici softwarová záplata pouze pro Linux a BSDI. (Hovořím zde o systémech s jejichž vývojáři Intel spolupracoval. Z ostatních systémů má softwarovou záplatu na tuto chybu například FreeBSD. Na uvedené stránce jsou zmiňovány tyto systémy nebo tito výrobci: BSDI, IBM, Linux, Microsoft, NCR, Novell, SCO, Sequent, SunSoft a Unisys.) Ostatní systémy včetně například Solarisu a SCO UNIXu mají příslušnou záplatu pouze "ve stádiu vývoje" nebo "testů".

Je zajímavé, co se zde píše o operačních systémech Microsoftu:

"Microsoft has worked closely with Intel to understand and characterize the effects of the recently uncovered Pentium processor erratum, and we're in the process of studying the implementation of potential workarounds in order to meet the needs of our customers," said Moshe Dunie Vice-President Windows Operating Systems Division at Microsoft. "Since this erratum can only be exploited by a program that was developed with malicious intent and deliberately uses this illegal instruction, following common-sense computing practices, such as not downloading or running executables from unknown sources, can protect a user from this problem."

"Microsoft a Intel těsně spolupracovali ve snaze pochopit nedávno odhalenou chybu procesoru Pentium a určit její dopad. Právě teď studujeme různé možnosti jak chybu obejít, abychom co nejlépe vyšli vstříc potřebám našich zákazníků", řekl Moshe Dunie, viceprezident divize Windows Operating Systems ve firmě Microsoft. "Vzhledem k tomu, že tato chyba může být využita pouze programem, který byl napsán se zlým úmyslem a záměrně používá tuto nedovolenou instrukci, stačí k ochraně uživatelů před tímto problémem chovat se podle zdravého rozumu, třeba nestahovat a nespouštět programy z neznámých zdrojů."

Tedy Microsoft se zřejmě vzdal myšlenky na to, že by jeho operační systémy mohly být někdy nasazeny ve víceuživatelském prostředí.

Nyní se budu věnovat mechanismu, jakým chybná instrukce pravděpodobně pracuje a navrhovaným softwarovým řešením. Samozřejmě není v silách jednoho člověka pochopit milióny tranzistorů v Pentiu, ale z chování CPU při této chybě lze leccos odpozorovat. Popisované mechanismy se tedy snaží odhadnout, co se v CPU přibližně děje při výskytu této instrukce. Nekladu si žádné nároky na správnost níže uvedených mechanismů, nicméně s jejich využitím se podařilo chybu izolovat a odstínit.

Inkriminovanou instrukci s operačním kódem f0 0f c7 by bylo možné zapsat jako lock cmpxchg8b s adresovacím módem, který není pro tuto instrukci platný. Tato instrukce je "téměř legální". Co to znamená? Na to, že jde o neplatnou instrukci, a že by se tedy měla vyvolat příslušná výjimka, procesor přijde až poměrně pozdě, když už je provádění instrukce uvnitř CPU v pokročilém stavu. Tento stav se vyznačuje tím, že procesor už zamknul datovou sběrnici, jak mu přikazuje instrukční prefix lock. A zde právě nastává problém. Procesor zjistí, že instrukce je neplatná, a chce vyvolat příslušné přerušení. Toto v první řadě znamená podívat se do tabulky přerušení (IDT, Interrupt Description Table), na které adrese začíná obsluha přerušení "neplatná instrukce". Pokus o přístup k IDT ale selže, protože CPU má zamčenou sběrnici! Někde v tomto místě se procesor zastaví.

Jak takovouto chybu ošetřit? K přístupu na sběrnici nedochází, pokud se příslušná část IDT právě nachází v primární cache procesoru. První a nejstarší softwarové záplaty na tuto chybu fungovaly tak, že zajistily, aby se v cache objevil ovladač pro obsluhu výjimky "neplatná instrukce" (například vykonáním nějaké skutečně neplatné instrukce), a pak zamkly obsah cache. Toto fungovalo, ale výkon procesoru bez primární cache klesl až někam k průměrným procesorům 486.

Pozdější úpravy využívaly toho, že jiné výjimky mají obsluhu zamčené sběrnice vyřešeny lépe než výjimka "neplatná instrukce". Pokud se například stránka IDT, ve které byl vektor přerušení "neplatná instrukce", nenachází v operační paměti, Pentium toto rozpozná a vyvolá výjimku číslo 8 - "dvojitý výpadek" (double fault). Jak tohoto ale dosáhnout? Stačí zarovnat začátek IDT tak, aby prvních sedm položek IDT (pro výjimky 0 až 6, přičemž "neplatná instrukce" má číslo 6) bylo v jedné stránce a ostatní byly v následující stránce paměti. Tedy aby tabulka IDT začínala těsně před koncem stránky. Dále je nutno upravit obsluhu výjimky číslo 8 tak, aby byla schopna analyzovat situaci a případně zavolat výjimky číslo 0 až 6. A poslední věc je označit první stránku IDT jako neplatnou.

V případě pokusu o vykonání neplatné instrukce se tedy zavolá výjimka číslo 8 "dvojitý výpadek", která pak zpětně zjistí o co jde a zavolá příslušnou výjimku z intervalu 0 až 6. Toto může udělat dvěma způsoby. Buďto zjistí příslušnou informaci z chybového slova a pak zavolá příslušnou obsluhu jako funkci (což byl způsob, jakým byla situace řešena v Linuxu) nebo se první stránka IDT namapuje do paměti a daná instrukce se restartuje (samozřejmě až po kontrole, jestli náhodou nešlo o instrukci f0 0f c7 cx. Toto byl způsob, jakým doporučoval chybu izolovat Intel. První způsob měl problémy s obsluhou speciálních případů, jako je například ladící bod na místě neplatné instrukce a podobně. Ladící a krokovací funkce zde nefungovaly úplně korektně. Naproti tomu druhý způsob (navrhovaný Intelem) měl problémy se samomodifikujícím se kódem (například na SMP stroji - dojde k přerušení, namapuje se IDT, ale někdo jiný změní zatím instrukci, na které došlo k přerušení na f0 0f c7 cx. Restartuje se instrukce, ale IDT je namapovaná a procesor se zablokuje). Linus Torvalds napsal na linux-kernel

Charles is right about the problems, although they don't actually apply to the current Linux version of the patch. They _do_ apply to the intel one, exactly because the intel one tries to be more clever, and fails in subtle ways.

Co se týče těchto problémů, má Charles pravdu, ačkoli se vlastně nevztahují k současné Linuxové verzi toho patche. Vztahují se ale k Intelovské verzi, právě proto, že ten jejich patch se snaží být chytřejší a nezvládá to.
Linus Torvalds

Obsloužení chyby F00F jedním ze dvou výše popsaných způsobů stálo přibližně 55 taktů při vyvolání kterékoli z výjimek 0 až 6. Záplaty provedené tímto způsobem se objevily v Linuxu 2.0.32 (vlastně již v pre-2.0.32-4) a 2.1.63.

Jak je vidět, odstranění takovéto chyby popisovaným způsobem nebylo nijak triviální. Naštěstí Linus ve spolupráci s Intelem vymyslel ještě lepší mechanismus odstínění chyby. V CPU se totiž v průběhu vyvolání výjimky pro neplatnou instrukci nejen neresetuje zamčení sběrnice, ale také následující přístup do paměti se provádí jako read-write přesně tak, jak by jej prováděla skutečná instrukce lock cmpxchg8b. Takže pokud je IDT namapována pouze pro čtení, dojde k zavolání výjimky "porušení ochrany paměti". Příslušná obslužná rutina pak zjistí, že byl proveden pokus o zápis do IDT na položku číslo 6 a detekuje tak jednoznačně pokus o zneužití chyby F00F.

The new workaround actually depends on the Intel bug not only forgetting to clear the "lock" state of the instruction, it also forgets to clear the fact that the instruction tries to do a read-modify-write cycle. So it will not only do the IDT access as a locked cycle, it will do it as a locked cycle that requires write permissions.

Nová záplata vlastně závisí na chybě Intelu, která nejenže zapomene zrušit "lock" stav instrukce, ale také zapomene změnit informaci o tom, že se instrukce pokouší přistoupit do paměti způsobem čti-modifikuj-zapiš. Takže procesor nejenže vykoná přístup k IDT se zamčenou sběrnicí, ale je vykonán zamčený přístup vyžadující navíc právo zápisu.
Linus Torvalds

Tímto způsobem se kompletně odstíní chyba F00F a navíc tato záplata nemá žádný vliv na výkon systému (na rozdíl od předchozích řešení). V současné době je k dispozici záplata na jádro 2.0.32 i 2.1.x, využívající tohoto mechanismu.

Operační systém Linux byl jedním z prvních, které měly k dispozici záplatu na hardwarovou chybu Pentia. Tato chyba pro současný Linux již nepředstavuje žádné nebezpečí. *


- předchozí článek - následující článek - obsah -