Chyba v procesorech Intel Pentium -- softwarové řešení
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Have you heard about the new Cray? It's so fast, it requires
TWO halt instructions to stop it!
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/piie/index.html 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čí.
výheň