SQLite hatte 16 Jahre lang einen Korruptionsfehler – warum Ihre Datenbank wahrscheinlich sicher ist und was Sie trotzdem prüfen sollten.
Diese Woche veröffentlichte Tailscale einen Postmortem-Bericht, den jedes Team, das SQLite einsetzt, lesen sollte. Sechs Monate lang, beginnend letzten August, korruptierten ihre Steuerungsebene immer wieder Datenbanken. Neunzehn separate Vorfälle, kein offensichtlicher Auslöser, keine kürzliche Codeänderung, die eine Erklärung geboten hätte. Die Ursache stellte sich als Data Race heraus, der seit Version 3.7.0 in SQLite steckte – veröffentlicht im Juli 2010. Er verbarg sich dort für rund sechzehn Jahre.
Das SQLite-Team bezeichnet es als WAL-Reset-Bug. Er tritt nur bei Datenbanken im WAL-Modus (Write-Ahead Logging) auf, wenn zwei oder mehr Verbindungen dieselbe Datei in separaten Threads oder Prozessen geöffnet haben und gleichzeitig versuchen zu schreiben oder einen Checkpoint durchzuführen. Wenn das Timing stimmt, kann ein Checkpoint ein Flag im WAL-Index setzen, das behauptet, ein Teil des Logs sei bereits in die Hauptdatenbankdatei kopiert worden – obwohl das nicht der Fall war. Ein späterer Checkpoint überspringt dann diese Daten, und die Datei wird korrumpiert. Der Fehler wurde am 13. März 2026 in SQLite 3.51.3 behoben, mit Backports auf 3.44.6 und 3.50.7.
Das ist der Teil, über den es sich lohnt, nachzudenken. Die SQLite-Entwickler konnten diesen Bug nicht absichtlich reproduzieren. Sie mussten ihren eigenen Quellcode patchen, um einen Callback genau zum falschen Zeitpunkt während eines Checkpoints auszulösen. Ihre eigene Telemetrie beziffert die reale Trefferquote auf oder unterhalb der Rate von SSD-Ausfällen und kosmischen Strahlen-Bitfehlern. Wenn Sie SQLite in einer normalen Konfiguration betreiben, waren Sie höchstwahrscheinlich nie betroffen und sind es wahrscheinlich immer noch nicht. Tailscale wurde getroffen, weil sie die manuelle Kontrolle über das Checkpointing übernahmen und es aggressiv für schnelle Backups ausführten. Sie verließen den ausgetretenen Pfad, und ein Eins-zu-einer-Milliarde-Race wurde zu einem monatlichen Ereignis.
Was wir tatsächlich dagegen unternehmen würden
Führen Sie ein Upgrade durch, aber keine Panik. Aktualisieren Sie auf SQLite 3.51.3 oder neuer – oder einen der Backports – nach Ihrem eigenen Zeitplan. Das Problem ist, dass »Ihre SQLite-Version« selten offensichtlich ist. SQLite ist fast überall eingebettet, und Sie installieren es nicht direkt. Es steckt in Ihrer Sprachlaufzeit, Ihrem ORM, Ihrem mobilen Betriebssystem und Ihrer Edge-Plattform.
Bei unserer nativen iOS- und Android-Entwicklung ist das leicht zu vergessen. Das System-SQLite ist an die Betriebssystemversion gebunden, die Ihre Nutzer verwenden – nicht an die Version, gegen die Sie gebaut haben –, und Sie können nicht bestimmen, wann diese aktualisiert wird. Wenn Ihre App mit Verbindungen oder Checkpoints etwas Ungewöhnliches macht – etwa ein Hintergrund-Sync-Thread schreibt, während die Benutzeroberfläche liest –, bündeln Sie Ihren eigenen SQLite-Build, damit Sie die Version kontrollieren, anstatt das zu übernehmen, was das Gerät mitliefert. Genau das haben wir in Offline-First-Healthcare- und IoT-Apps getan, wo ein korrumpierter lokaler Datenspeicher Daten bedeutet, die der Nutzer nie zurückbekommt.
Auf der Backend- und Edge-Seite: Prüfen Sie, womit Ihr Treiber tatsächlich verknüpft ist. Aus unserer PHP- und Docker-Arbeit wissen wir, dass die SQLite-Version, die in ein Basis-Image eingebettet ist, Monate hinter dem Upstream zurückbleiben kann, und »wir nutzen das neueste Framework« sagt Ihnen nichts über die darunter liegende C-Bibliothek. Serverless- und Edge-SQLite – also D1, Turso, LiteFS und Verwandte – verdienen einen genauen Blick, weil diese Plattformen stark auf WAL-Modus und Concurrent Access setzen. Das ist genau die Form, die dieser Bug benötigt.
Die größere Lektion betrifft Backups
Das Detail, das mich beschäftigt, ist nicht die Race Condition. Es ist, dass Tailscale es nur bemerkte, weil eine separate Pipeline ihre S3-Backups las und PRAGMA integrity_check dagegen ausführte. Ihre Live-Datenbanken sahen gesund aus. Die Korruption steckte bereits in den Dateien, die sie für später gespeichert hatten.
Bei unseren Security- und Pentesting-Engagements sind Backups der Ort, an dem wir die erschreckendsten Lücken finden. Teams erstellen sie pflichtbewusst und stellen sie nie wieder her. Ein Backup, das Sie nicht wiederhergestellt haben, ist eine Hypothese, kein Sicherheitsnetz. Wenn Sie SQLite-Dateien speichern, führen Sie PRAGMA integrity_check planmäßig auf einer Kopie aus und stellen Sie gelegentlich in einer Testumgebung wieder her. Korruption, die erst an dem Tag auftaucht, an dem Sie das Backup brauchen, ist die schlimmste Art, das zu lernen.
Noch etwas aus dem Bericht, das leicht zu übersehen ist. Nachdem Tailscale den Fix bereitgestellt hatte, löste eine Rundungsänderung im Release falsche Korruptionswarnungen bei Datenbanken aus, die Ausdrucksindizes verwendeten. Sie bemerkten es, weil sie zuerst auf einige Canary-Shards ausrollten, dann auf den Rest. Phasenweise Rollouts sind keine Bürokratie. Sie sind die Art und Weise, wie man den zweiten Bug findet, den die Behebung des ersten eingeführt hat.
Was Sie diese Woche tun können
Finden Sie heraus, welche SQLite-Version Ihre Produktionssysteme und ausgelieferten Apps tatsächlich ausführen. Nicht die Framework-Version, sondern die eigentliche SQLite-Bibliothek. Wählen Sie dann ein Backup und beweisen Sie, dass Sie es sauber wiederherstellen können. Das ist ein Nachmittag Arbeit, und für die meisten Teams ist es mehr wert als das Upgrade selbst.
Wenn Ihnen das Herausfinden, welche SQLite-Version Ihre Apps und Services tatsächlich ausführen – und ob Ihre Backups eine Wiederherstellung überstehen würden –, vertraut klingt, sprechen wir darüber.