Bug-Wahn
Kaum zwei Wochen nachdem bekannt wurde, wie man beim Cyrix-Prozessor mit wenigen Bytes einen internen `Deadlock´ provoziert, machte ein sehr ähnlicher Fehler im Pentium-Prozessor die Runde. Zufall? - Wohl kaum. Ein Intel-Sprecher deutete an, hier habe wohl ein anderer Prozessor-Hersteller etwas geplaudert.
- Andreas Stiller
In der Tat, der sogenannte F0-Bug (heißt bei Intel auch IIE-Bug für Invalid Instruction Exception) ist dermaßen einfach zu reproduzieren, daß wohl kaum ein Prozessorhaus ihn übersehen haben könnte. Schließlich werden hier mit enormem Aufwand Kompatibilitätstests durchgeführt. War beim Cyrix noch die Kombination dreier verschiedener Befehle für den Absturz nötig, so reicht dafür beim Pentium ein einziger, allerdings ungültiger Opcode.
Der F0-Bug betrifft nur den Pentium (mit oder ohne MMX). PPro-, Pentium-II- und 486-Besitzer brauchen diesbezüglich keine Angst zu haben. Cyrixens wie Intels Crash-Codes haben vieles gemeinsam. Sie tauchen in keiner normalen Software auf, sondern spielen nur bei bösartigen Attacken eine Rolle. Sie lassen sich einfach erzeugen und sind somit auch über Internet via ActiveX-Controls oder dank IE-4.0-Bug leicht zu verbreiten. Besonders übel ist, daß beide auch im geschützten User-Modus funktionieren. Sie verursachen keine Rechenfehler, sondern stellen ein Sicherheitsloch dar. Beide lassen sich übrigens relativ leicht `fixen´ (dazu später).
Der normale Endanwender, der Windows 95 oder gar DOS fährt, braucht sowieso nichts `weiter´ zu befürchten. Diese Betriebssysteme erlauben es eh, jederzeit den Prozessor in die vorläufigen Jagdgründe zu befördern. Dafür reicht ein einfaches CLI JMP $ bereits aus. Immerhin kann man solche Endlosschleifen noch mit einem NMI abbrechen, obige Prozessor-Deadlocks sind hingegen NMI-resistent. Daneben kann böswillige Software unter Windows 95 ungestört auf die PCI-Konfigurationsregister und das CMOS-RAM zugreifen und den Rechner bei derartigen Attacken komplett zerkonfigurieren und hierüber gar Schaden an der Hardware anrichten. Unix und Windows NT können zumindest das weitgehend erfolgreich abblocken. Dennoch gab es auch hier immer wieder Sicherheitslöcher, die auch ohne Pentium-Bug den Rechner zum Stillstand bringen konnten. NT 4.0 etwa überprüfte bei einigen API-Funktionen die Parameter nicht - und ließ sich darüber `abschießen´ (ist mit Service Release 3 behoben). Solaris 2.3 bis 2.5 konnte man mit einem schlichten Ping zur Totalaufgabe zwingen und so weiter. Insofern sind Prozessor-Bugs nichts anderes als Me-too-Löcher. Dennoch, gerade in großen Unix-Netzen mit den dort meist üblichen freizügigen Rechten (etwa via Telnet) oder auf Internet-Servern mit CGI-Scripts stellt so ein Absturzloch eine ziemliche Bedrohung dar.
Der Bug ...
Diverse Opcodes sind in der x86-Familie nicht definiert. Benutzt man sie trotzdem, so erzeugt der Prozessor einen Ausnahme-Interrupt (Exception) 6 für Invalid Opcode. Solche Invalid-Opcode-`Fehler´ werden gern als Schnittstelle mißbraucht (etwa kommunizieren DOS und NT auf diese Art miteinander beim Virtual DOS Device). Auch der Internet Explorer 4.0 nutzt massiv (warum auch immer) Invalid Opcodes.
Der F0-Bug tritt bei einem ungültigen CMPCHG8B-Befehl auf mit Lock-Prefix (F0) und einem ModR/M-Byte zwischen C8 und CF. Sein Opcode sieht demnach so aus: F0 0F C7 C8 ... CF. Zwischen dem Prefix F0h und dem eigentlichen Opcode können noch diverse andere Prefixe stehen, etwa Segmentregister-Präfixe wie DS: (3E), ES: (26) oder Adreß- und Registergrößen (66, 67). Das macht die Erkennung von malignem Code etwas schwieriger, da beispielsweise auch folgende Sequenz zum Absturz führt: F0 26 66 0F C7 CE.
Allerdings erfolgt der Absturz nur dann, wenn der aktuelle Stackpointer auf eine Speicheradresse zeigt, die im Daten-Cache residiert. Schaltet man den Cache ab oder sorgt dafür, daß der Stack gerade nicht im Cache ist, so tritt kein Deadlock auf. Wirklich `dicht´ wird die Attacke daher erst, wenn man dem Opcode noch einen Stack-Zugriff, etwa Push/Pop AX (50, 58) voranstellt.
... und sein Fix
Beim Cyrix-Prozessor läßt sich das Bug-Problem unter minimaler Performancesteigerung ganz einfach lösen, indem man die Prozessorkonfiguration ändert (No-Lock einschalten). Beim Pentium muß man sich schon etwas mehr Mühe geben, will man nicht eine völlig unzumutbare Verlangsamung durch Abschalten des Daten-Cache in Kauf nehmen. Wie nicht anders zu erwarten, kam der erste sinnvolle Bugfix aus der Linux-Ecke: Man verlagert die IDT (Interrupt Descriptor Table) so, daß die Interrupts von 0 bis 6 auf eine Speicher-Page, diejenigen ab 7 dann auf die nächste Page zu liegen kommen und setzt dann das Attribut der ersten Page auf `not present´. So tritt in jedem Fall, auch bei dem malignen F0-Opcode zunächst ein Page-Fault ein. Der zuständige Page-Fault-Handler kann die Ursache erkennen und entsprechende Maßnahmen vornehmen (Programm-Abbruch).
Ob Microsoft eine entsprechende Maßnahme für das nächste NT Service Release vornehmen wird, steht noch offen. Jedenfalls sollte Microsoft ein gegebenenfalls nötiges Flushen des Cache nicht mit WBINVD des Inline-Assemblers aus VC++ (2.0 bis 5.0) vornehmen. Dann gibt´s nämlich einen Invalid Opcode Interrupt - denn der WBINVD-Befehl ist schon seit vier C++-Generationen falsch kodiert ... (as) (as)