Nullpointer Exception
Informatik ist, wenn ein einziges kleines lächerliches Fehlerchen die halbe Welt zum Stillstand bringen kann.
Eigentlich wollte ich es schon heute morgen schreiben, aber gerade als ich es schreiben wollte, fiel – welch feine Ironie – mein Internetanschluss für einige Stunden aus.
Crowdstrike Analysis:
It was a NULL pointer from the memory unsafe C++ language.
Since I am a professional C++ programmer, let me decode this stack trace dump for you. pic.twitter.com/uUkXB2A8rm
— Zach Vorhies / Google Whistleblower (@Perpetualmaniac) July 19, 2024
Demnach hat ein einfacher, ordinärer Programmierfehler, der Gebrauch eines nicht initialisierten und noch auf NULL stehenden Pointers in einer Programmiersprache (Wohl C oder C++), die diesen Fehler nicht abfängt, dazu geführt, dass das Betriebssytem das ganze Programm beendet hat, weil eine Speicherverletzung ausgelöst wurde, und das dann dazu führte, dass das Betriebssytem gar nicht mehr hochgefahren wurde, weil das in einer frühen Phase passierte.
Nun muss man aufpassen, weil Leser schon mutmaßten, dass das Absicht, also Sabotage war, und mir jede Menge Leser entgegenhalten würden, dass der Pointer ja gar nicht Null war, sondern auf 000000000000009c stand, (Hexademzimal 9c = dezimal 156).
Vorsicht: Auch das fällt unter Nullpointer Exception, nämlich weil in der Regel der Pointer auf einen Speicherbereich zeigt, in dem die Zieldaten liegen, und dann mit einem Offset darauf zugegriffen wird, etwa wenn der Pointer auf einen Record/Struct zeigen soll und man da einen Eintrag lesen will, oder dort ein Array liegen soll, auf den man mit Index zugreift. Wenn da also irgendwelche Datensätze definiert wurden, dann wäre das normal, wenn irgendein Bestandteil des Datensatzes gelesen werden soll, und man dann auf ptr->irgendwas zugreift, dass der Prozessor dann beispielsweise eben auf ptr + 0x9c zugreift, also relativ zur angegebenen Adresse, wo die Daten im Speicher liegen soll. Wenn also der Pointer noch auf Null steht, und man den ungeprüft verwendet, um auf einen Bestandteil eines Datensatzes zuzugreifen, der eben bei Adresse + 0x9c liegen müsste, dann kommt ein Zugriff auf 000000000000009c dabei heraus. Auch wenn die Adresse, auf die zugegriffen wurde, nicht Null ist, riecht das danach, als ob der Pointer, der verwendet wurde, auf Null stand. Denn der Pointer wird geprüft, bevor man den Offset 9c draufaddiert.
Stellt Euch vor, Ihr bekommt GPS-Koordinaten eines Hauses und den Auftrag, dort im Vierten Stock bei Meier etwas abzuholen, und dann steht dort gar kein Haus, weil die Koordinaten im Atlantik liegen, ist das Problem trotzdem, dass die GPS-Koordinaten falsch sind, auch wenn Ihr in den Vierten Stock und nicht in das Erdgeschoss wolltet. Es kommt nicht darauf an, ob Ihr in den vierten oder sechsten Stock wolltet, denn wenn die Koordinaten nicht stimmen und da gar kein Haus ist, ist das der Fehler und nicht, dass das Gebäude ja vielleicht drei oder fünf Stockwerke hätte. Wenn schon die Koordinaten falsch sind, kommt es auf das Geschoss dann auch nicht mehr an. Und dass da kein Haus steht, sie man ja vor dem Betreten und nicht erst im vierten Stock.
Das ist ein Programmierfehler.
Allerdings kann man so noch nicht sagen, ob ein offensichtlicher oder ein sehr subtiler.
Gerade in Sprachen, in denen man diesen Fehler machen kann, ohne dass er abgefangen wird, muss man sehr sorgfältig programmieren und entweder dafür sorgen, dass es nicht passieren kann, oder dass es rechtzeitig erkannt und behandelt wird. Auch ein Programm in einer Programmiersprache, die das abfängt, wie RUST, würde dann mit einer Fehlermeldung abbrechen, weil ja auch das kein reparierbarer Programmlauf mehr ist (wenn man es eben nicht selbst abfängt und weiß, wie es weiter gehen könnte). Es ist aber aus Sicht des Betriebssystems ein riesiger Unterschied, ob sich ein Programm selbst ordnungsgemäß beendet und dann sagt, hier lief was schief, oder das in ein Log schreibt, oder ob das Betriebssystem selbst ein Programm abbrechen muss, weil der Prozessor Alarm gibt, weil da etwas außer Kontrolle geraten ist. Auch Programme in Rust oder Go brechen ab, wenn man einen falschen Pointer verwendet, aber erstes brechen sie sauber ab, und zweitens machen sie es schon sehr, sehr schwer, überhaupt einen ungültigen Pointer haben zu können.
Heise hat inzwischen auch was dazu, hier und hier.
Das wird allerdings in Zukunft öfter passieren, weil
- die Programmierfähigkeiten immer weiter absinken,
- der Arbeitsdruck immer weiter steigt,
- man immer schlechtere Mitarbeiter „toleriert“ und Kritik zurückfährt („quality is a myth“)
- Firmen anfangen, immer mehr Software per KI erstellen zu lassen und nie zu prüfen, also Code läuft, den keiner mehr durchblickt.
Das wird sicher noch lustig.