Race Condition
Was ist eine Race Condition?
Eine Race Condition ist eine unerwünschte Situation, die eintritt, wenn ein Gerät oder System versucht, zwei oder mehrere Vorgänge gleichzeitig auszuführen, die jedoch aufgrund der Beschaffenheit des Geräts oder Systems in der richtigen Reihenfolge ausgeführt werden müssen, um korrekt zu sein.
Race Conditions werden am häufigsten mit Informatik und Programmierung in Verbindung gebracht. Sie treten auf, wenn zwei Computerprogrammprozesse oder Threads versuchen, gleichzeitig auf dieselbe Ressource zuzugreifen und Probleme im System verursachen.
Race Conditions werden als ein häufiges Problem bei Multithreading-Anwendungen angesehen.
Was sind Beispiele für Race Conditions?
Ein einfaches Beispiel für eine Race Condition ist ein Lichtschalter. In manchen Häusern gibt es mehrere Lichtschalter, die mit einer gemeinsamen Deckenleuchte verbunden sind. Bei dieser Art von Schaltkreisen spielt die Position des Schalters keine Rolle mehr. Wenn das Licht eingeschaltet ist, wird das Licht ausgeschaltet, wenn einer der Schalter aus seiner aktuellen Position bewegt wird. Wenn das Licht ausgeschaltet ist, schaltet das Bewegen eines Schalters aus der aktuellen Position das Licht ein.
Stellen Sie sich nun vor, was passieren könnte, wenn zwei Personen gleichzeitig versuchen würden, das Licht mit zwei verschiedenen Schaltern einzuschalten. Eine Anweisung könnte die andere aufheben oder die beiden Aktionen könnten den Schutzschalter auslösen.
In Computer-Memory kann es zu einer Race Condition kommen, wenn Befehle zum Lesen und Schreiben einer großen Datenmenge fast zur gleichen Zeit eingehen und die Maschine versucht, einige oder alle alten Daten zu überschreiben, während die alten Daten noch gelesen werden. Das Ergebnis kann eines oder mehrere der folgenden sein:
- der Computer stürzt ab oder stellt eine illegale Operation des Programms fest
- Fehler beim Lesen der alten Daten
- Fehler beim Schreiben der neuen Daten
Eine Race Condition kann auch auftreten, wenn Anweisungen in der falschen Reihenfolge verarbeitet werden.
Angenommen, zwei Prozesse müssen einen Bit-Flip an einer bestimmten Memory-Stelle durchführen. Unter normalen Umständen sollte der Vorgang wie in Abbildung 1 dargestellt ablaufen.
Wenn eine Race Condition auftritt, die dazu führt, dass sich diese beiden Prozesse überschneiden, könnte die Abfolge der Operationen möglicherweise eher wie in Abbildung 2 aussehen.
Race Conditions treten gelegentlich bei Logikgattern auf, wenn die Eingänge miteinander in Konflikt geraten.
Da der Ausgangszustand des Gatters eine endliche Zeitspanne ungleich Null benötigt, um auf Änderungen der Eingangszustände zu reagieren, können empfindliche Schaltungen oder Geräte, die dem Gatter folgen, durch den Zustand des Ausgangs getäuscht werden und nicht ordnungsgemäß funktionieren.
Welche Arten von Race Conditions gibt es?
Es gibt einige Arten von Race Conditions. Es gibt zwei Kategorien, die die Auswirkungen von Race Conditions auf ein System definieren: kritisch und unkritisch:
- Eine kritische Race Condition führt dazu, dass sich der Endzustand des Geräts, Systems oder Programms ändert. Wenn zum Beispiel zwei Lichtschalter, die mit einer gemeinsamen Lampe verbunden sind, gleichzeitig umgelegt werden, kommt es zu einem Kurzschluss, was als kritische Race Condition betrachtet wird. In Software ist eine kritische Race Condition gegeben, wenn eine Situation zu einem Fehler mit unvorhersehbarem oder undefiniertem Verhalten führt.
- Eine unkritische Race Condition hat keine direkten Auswirkungen auf den Endzustand des Systems, Geräts oder Programms. In dem Beispiel mit dem Licht: Wenn das Licht ausgeschaltet ist und das gleichzeitige Umlegen beider Schalter das Licht einschaltet und die gleiche Wirkung hat wie das Umlegen eines Schalters, dann handelt es sich um eine unkritische Race Condition. In Software führt eine unkritische Race Condition nicht zu einem Fehler.
Kritische und unkritische Race Conditions sind nicht auf die Elektronik oder Programmierung beschränkt. Sie können in vielen Arten von Systemen auftreten, in denen Race Conditions vorkommen.
In der Programmierung treten zwei Hauptarten von Race Conditions in einem kritischen Codeabschnitt auf, das heißt in einem Codeabschnitt, der von mehreren Threads ausgeführt wird. Wenn mehrere Threads versuchen, eine Variable zu lesen, und dann jeder auf sie einwirkt, kann eine der folgenden Situationen eintreten:
- Lesen-Ändern-Schreiben (Read-Modify-Write). Diese Art von Race Condition tritt auf, wenn zwei Prozesse einen Wert in einem Programm lesen und einen neuen Wert zurückschreiben. Dies verursacht oft einen Softwarefehler. Wie im obigen Beispiel wird erwartet, dass die beiden Prozesse nacheinander ablaufen – der erste Prozess erzeugt seinen Wert und der zweite Prozess liest diesen Wert und gibt einen anderen zurück.
Wenn zum Beispiel Schecks auf einem Girokonto nacheinander verarbeitet werden, stellt das System sicher, dass auf dem Konto genügend Mittel vorhanden sind, um zuerst Scheck A zu verarbeiten, und prüft dann erneut, ob nach der Verarbeitung von Scheck A genügend Mittel für Scheck B vorhanden sind.
- Prüfen und dann handeln (Check-Then-Act). Diese Race Condition tritt auf, wenn zwei Prozesse einen Wert prüfen, auf den sie jeweils eine externe Aktion ausführen werden. Die Prozesse prüfen beide den Wert, aber nur ein Prozess kann den Wert mitnehmen. Der später auftretende Prozess liest den Wert als Null. Dies führt dazu, dass eine potenziell veraltete oder nicht verfügbare Beobachtung verwendet wird, um zu bestimmen, was das Programm als nächstes tun wird. Wenn beispielsweise eine Kartenanwendung zwei Prozesse gleichzeitig ausführt, die dieselben Ortsdaten benötigen, nimmt einer den Wert zuerst, damit der andere ihn nicht verwenden kann. Der spätere Prozess liest die Daten als Null.
Welche Sicherheitsschwachstellen können durch Race Conditions entstehen?
Ein Programm, das für die Bearbeitung von Aufgaben in einer bestimmten Reihenfolge ausgelegt ist, kann Sicherheitsprobleme aufweisen, wenn es zwei oder mehr Vorgänge gleichzeitig ausführen soll. Ein Bedrohungsakteur kann die Zeitspanne zwischen dem Start des Dienstes und dem Wirksamwerden einer Sicherheitskontrolle ausnutzen, um eine Deadlock- oder Threadblock-Situation zu schaffen.
Eine Deadlock-Schwachstelle ist eine schwere Form einer Denial-of-Service-Schwachstelle. Sie kann auftreten, wenn zwei oder mehr Threads darauf warten müssen, dass ein anderer eine Sperre in einer zirkulären Kette erhält oder freigibt. Diese Situation führt zu einem Deadlock, bei dem das gesamte Softwaresystem zum Stillstand kommt, da solche Sperren niemals erworben oder freigegeben werden können, wenn die Kette zirkulär ist.
Ein Threadblock kann auch die Anwendungsleistung erheblich beeinträchtigen. Bei dieser Art von Gleichzeitigkeitsfehler ruft ein Thread eine lang laufende Operation auf, während er eine Sperre hält und das Vorankommen der anderen Threads verhindert.
Wie man Race Conditions erkennt
Die Erkennung und Identifizierung von Race Conditions gilt als schwierig. Sie sind ein semantisches Problem, das aus vielen möglichen Fehlern im Code entstehen kann. Am besten ist es, den Code so zu gestalten, dass diese Probleme von vornherein vermieden werden.
Programmierer verwenden dynamische und statische Analyse-Tools, um Race Conditions zu erkennen. Statische Testwerkzeuge scannen ein Programm, ohne es auszuführen. Sie produzieren jedoch viele falsche Berichte. Dynamische Analysewerkzeuge geben weniger falsche Berichte aus, aber sie erkennen möglicherweise keine Race Conditions, die nicht direkt im Programm ausgeführt werden.
Race Conditions werden manchmal durch Data Races erzeugt, die auftreten, wenn zwei Threads gleichzeitig auf dieselbe Memory-Stelle zugreifen und mindestens einer davon ein Schreibvorgang ist. Data Races sind leichter zu erkennen als Race Conditions, da für ihr Auftreten bestimmte Bedingungen erforderlich sind. Tools wie der Data Race Detector des Go-Projekts überwachen Situationen, in denen Datenrennen auftreten. Race Conditions sind enger mit der Anwendungssemantik verknüpft und werfen umfassendere Probleme auf.
Wie kann man Race Conditions verhindern?
Es gibt zwei Möglichkeiten, wie Programmierer Race Conditions in Betriebssystemen und anderer Software verhindern können:
- Vermeiden Sie gemeinsam genutzte Zustände (Shared States). Dies bedeutet, dass der Code überprüft wird, um sicherzustellen, dass bei gemeinsam genutzten Ressourcen in einem System oder Prozess atomare Operationen vorhanden sind, die unabhängig von anderen Prozessen ablaufen, und dass Sperren verwendet werden, um die atomare Operation kritischer Codeabschnitte zu erzwingen. Es können auch unveränderliche Objekte verwendet werden, die nach ihrer Erstellung nicht mehr verändert werden können.
- Nutzen Sie die Thread-Synchronisierung. In diesem Fall kann ein bestimmter Teil des Programms nur von einem Thread zur gleichen Zeit ausgeführt werden.
Die Verhinderung von Race Conditions ist auch mit anderen Technologien möglich:
Storage und Memory
Die Serialisierung von Memory- oder Storage-Zugriffen verhindert ebenfalls Race Conditions. Das heißt, wenn Lese- und Schreibbefehle nahe beieinander empfangen werden, wird der Lesebefehl standardmäßig zuerst ausgeführt und abgeschlossen.
Networking
In einem Netzwerk kann eine Race Condition auftreten, wenn zwei Benutzer gleichzeitig auf einen Kanal zugreifen wollen und keiner der beiden Computer eine Benachrichtigung erhält, dass der Kanal belegt ist, bevor das System den Zugriff gewährt. Statistisch gesehen tritt diese Art von Situation vor allem in Netzen mit langen Verzögerungszeiten auf, zum Beispiel in Netzen, die geostationäre Satelliten verwenden.
Um eine solche Race Conditions zu verhindern, muss ein Prioritätsschema entwickelt werden, das einem Benutzer exklusiven Zugang gewährt. So kann beispielsweise der Teilnehmer, dessen Benutzername oder Nummer mit dem ersten Buchstaben des Alphabets oder der niedrigsten Ziffer beginnt, Vorrang erhalten, wenn zwei Teilnehmer innerhalb eines bestimmten Zeitintervalls versuchen, auf das System zuzugreifen.
Fazit
Race Conditions treten auf verschiedene Weise in Software, Memory, Speicher und Netzwerk auf. Sie proaktiv zu überwachen und zu verhindern, ist ein entscheidender Teil der Software- und Technologieentwicklung und -konzeption.
Die Verhinderung von Race Conditions ist besonders wichtig, da Hacker Schwachstellen in Race Conditions ausnutzen können, um sich unbefugten Zugang zu Netzwerken zu verschaffen. Ein bemerkenswertes Beispiel für eine auf Race Conditions basierende Schwachstelle ist Dirty Cow, die eine Schwachstelle im Memory-Subsystem des Linux-Kernels ausnutzt, um eine Race Condition zu schaffen, bei der der Angreifer Schreibrechte für Nur-Lese-Speicher-Mappings erhält.