24 Punkte von GN⁺ 2025-12-19 | Noch keine Kommentare. | Auf WhatsApp teilen
  • SQLite erhält seine hohe Zuverlässigkeit und Robustheit durch ein gründlich automatisiertes Testsystem; es gibt 590-mal mehr Testcode als Produktivcode
  • Vier unabhängige Test-Harnesses (TCL, TH3, SQL Logic Test, dbsqlfuzz) validieren die Kernbibliothek und führen Hunderte Millionen Tests aus
  • Durch Tests von Ausnahmezuständen (OOM, I/O-Fehler, Crash-Simulationen) und Fuzz-Testing wird geprüft, dass das System auch bei fehlerhaften Eingaben und Systemausfällen stabil arbeitet
  • Es wird ein mehrschichtiger Verifizierungsprozess mit 100 % Branch- und MC/DC-Coverage, Erkennung von Ressourcenlecks, Valgrind, statischer Analyse und Checklisten aufrechterhalten
  • Dank dieser systematischen Tests wird SQLite als Open-Source-Datenbank mit Zuverlässigkeit und Qualität auf kommerziellem DB-Niveau bewertet

1. Überblick

  • Die Zuverlässigkeit und Robustheit von SQLite resultiert aus einem detaillierten Testprozess
    • Mit Stand Version 3.42.0 besteht SQLite aus etwa 155,8 KSLOC C-Code und 92053,1 KSLOC Testcode
  • Das Testsystem umfasst 4 unabhängige Harnesses, 100 % Branch-Coverage und Millionen von Testfällen
    • Enthalten sind unter anderem OOM-, I/O-Fehler-, Crash-, Fuzz-, Grenzwert-, Regressions-, fehlerhafte-DB-Datei- und Tests mit deaktivierten Optimierungen

2. Test-Harnesses

  • TCL Tests
    • Öffentlich verfügbares Testset, das hauptsächlich während der SQLite-Entwicklung verwendet wird
    • Besteht aus 27,2 KSLOC C-Code und 1390 Skriptdateien (23,2 MB)
    • Etwa 50.000 Testfälle; durch Parametrisierung werden bei einer vollständigen Ausführung Hunderte Millionen Tests durchgeführt
  • TH3
    • Kommerzielles C-basiertes Testset, das 100 % Branch- und MC/DC-Coverage erreicht
    • Läuft auch in Embedded-Umgebungen und umfasst 1055,4 KSLOC sowie etwa 50.000 Fälle
    • Bei vollständigen Coverage-Tests etwa 2,4 Millionen Ausführungen, vor Releases 248 Millionen Soak-Tests
  • SQL Logic Test (SLT)
    • Vergleicht Ergebnisse von SQLite mit PostgreSQL, MySQL, SQL Server und Oracle 10g
    • Besteht aus 7,2 Millionen Queries und 1,12 GB Daten
  • dbsqlfuzz
    • libFuzzer-basierter Fuzzer, der SQL und Datenbankdateien gleichzeitig mutiert
    • Führt pro Tag etwa 1 Milliarde Mutationstests durch und prüft die Robustheit gegenüber bösartigen Eingaben
  • Zusätzliche Werkzeuge
    • speedtest1.c, mptester.c, threadtest3.c, fuzzershell.c, jfuzz usw.
    • Ein Release ist nur möglich, wenn alle Tests auf mehreren Plattformen und mit verschiedenen Compiler-Konfigurationen bestehen

3. Tests von Ausnahmezuständen

  • OOM-Tests
    • Simulieren malloc()-Fehlschläge, um zu prüfen, ob bei Speichermangel korrekt wiederhergestellt wird
    • Die Tests werden wiederholt ausgeführt, wobei der Zähler für den Fehlerzeitpunkt schrittweise erhöht wird
  • I/O-Fehlertests
    • Simulieren Plattenfehler mithilfe eines virtuellen Dateisystems (VFS)
    • Nach einem Fehler wird mit PRAGMA integrity_check geprüft, ob Daten beschädigt wurden
  • Crash-Tests
    • Simulieren Stromausfälle und OS-Abstürze
    • Das TCL-Harness basiert auf Kindprozessen, TH3 verwendet ein speicherbasiertes VFS
    • Es wird verifiziert, dass Transaktionen entweder vollständig zurückgerollt oder vollständig abgeschlossen werden
  • Kombinierte Fehlertests
    • Prüfen auch Szenarien, in denen nach einem Crash zusätzlich OOM- oder I/O-Fehler nacheinander auftreten

4. Fuzz-Testing

  • SQL Fuzz
    • Erzeugt syntaktisch gültiges, aber ungewöhnliches SQL, um die Reaktion von SQLite zu prüfen
  • American Fuzzy Lop (AFL)
    • Profilbasierter Fuzzer, eingeführt 2014, der neue Kontrollpfade erkundet
    • Fand zahlreiche assert-Fehlschläge, Abstürze und fehlerhafte Ergebnisse in SQLite
  • Google OSS Fuzz
    • Führt seit 2016 automatisches Fuzzing auf der Google-Infrastruktur durch
    • Erkennt sporadische Probleme in neuen Commits
  • dbsqlfuzz / jfuzz
    • Seit 2018 als interne Fuzzer eingeführt; mutieren SQL und DB-Dateien gleichzeitig
    • Mehr als 500 Millionen Tests pro Tag; Bug-Reports von externen Fuzzern sind fast vollständig verschwunden
    • Seit 2024 ergänzt jfuzz die Validierung von JSONB-Eingaben
  • Fuzzer von Drittanbietern und fuzzcheck
    • Externe Forschende (z. B. Manuel Rigger) fanden zahlreiche Fälle falsch berechneter Ergebnisse
    • Das Hilfsprogramm fuzzcheck validiert erneut Tausende „interessante“ frühere Fuzz-Fälle
  • Spannungsverhältnis zwischen MC/DC und Fuzz-Testing
    • MC/DC minimiert defensiven Code, Fuzzing erfordert defensiven Code
    • SQLite kombiniert beide Ansätze, um Code aufrechtzuerhalten, der sowohl bei normalen als auch bei bösartigen Eingaben robust ist

5. Regressionstests

  • Gemeldete Bugs werden nach der Behebung zwingend als neue Testfälle ergänzt
    • Ziel ist es, das Wiederauftreten früherer Bugs zu verhindern

6. Automatische Erkennung von Ressourcenlecks

  • TCL- und TH3-Harnesses überwachen automatisch Speicher-, Datei-, Thread- und Mutex-Lecks
    • Auch nach OOM- oder I/O-Fehlern darf es keine Speicherlecks geben

7. Testabdeckung

  • Der SQLite-Core erreicht 100 % Branch-Coverage nach TH3
    • Erweiterungen wie FTS3 und RTree sind ausgenommen
  • Statement- vs. Branch-Coverage
    • Branch-Coverage ist strenger als Statement-Coverage und prüft alle Bedingungszweige in beide Richtungen
  • Coverage für defensiven Code
    • Defensive Bedingungen werden mit den Makros ALWAYS() und NEVER() gekennzeichnet
    • Durch wiederholte Tests mit drei Definitionsformen wird Konsistenz verifiziert
  • Grenzwert- und Boolean-Vektor-Tests
    • Mit dem Makro testcase() werden sowohl positive als auch negative Ergebnisse einer Bedingung geprüft
    • 1184 Verwendungen von testcase()
  • Erreichen von MC/DC
    • Über das Makro testcase() wird der unabhängige Einfluss jeder Bedingung validiert
  • gcov-basierte Messung
    • Die Coverage wird mit den Optionen -fprofile-arcs -ftest-coverage gemessen
    • Durch den Vergleich der Ergebnisse werden Compiler-Bugs oder undefiniertes Verhalten erkannt
  • Mutation Testing
    • Ändert Branch-Anweisungen, um zu prüfen, ob die Tests dies erkennen
    • Optimierungszweige (/*OPTIMIZATION-IF-TRUE*/) werden als Ausnahme behandelt
  • Erfahrung mit vollständiger Coverage
    • Dank der Tests aller Branches werden Nebenwirkungen bei Codeänderungen minimiert
    • Die Wartungskosten sind hoch, aber für eine breit eingesetzte Infrastruktur-Bibliothek gerechtfertigt

8. Dynamische Analyse

  • Assert()
    • 6754 assert-Anweisungen prüfen Vor- und Nachbedingungen sowie Schleifeninvarianten
    • Nur in SQLITE_DEBUG-Builds aktiv
  • Valgrind
    • Erkennt Speicherfehler, Stack-Overflows und Zugriffe auf nicht initialisierten Speicher
    • Vor Releases werden veryquick- und TH3-Tests unter Valgrind ausgeführt
  • Memsys2
    • In SQLITE_MEMDEBUG-Builds werden Wrapper zur Überwachung von Speicherfehlern eingefügt
    • Wiederholte Prüfungen sind damit schneller als mit Valgrind möglich
  • Mutex Asserts
    • Validieren Multithreading-Synchronisation mit sqlite3_mutex_held() usw.
  • Journal-Tests
    • Prüfen, ob das Rollback-Journal vor der DB geschrieben wird, und gewährleisten so die Atomizität von Transaktionen
  • Checks auf undefiniertes Verhalten
    • Erkennen undefiniertes Verhalten mit -ftrapv, -fsanitize=undefined, /RTC1 usw.
    • Werden wiederholt auf 32-/64-Bit-Systemen, mit unterschiedlicher Endianness und auf verschiedenen CPU-Architekturen ausgeführt

9. Tests mit deaktivierten Optimierungen

  • Optimierungen können mit sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) deaktiviert werden
    • Unabhängig davon, ob Optimierungen aktiv sind oder nicht, müssen identische Ergebnisse erzeugt werden
    • Einige Tests zur Leistungsmessung sind ausgenommen

10. Checkliste

  • Vor einem Release wird eine manuelle Checkliste mit etwa 200 Punkten geprüft
    • Einige Punkte dauern nur Sekunden, andere mehrere Stunden
    • Wenn Probleme entdeckt werden, werden sofort neue Punkte ergänzt, um das Verfahren kontinuierlich zu verbessern

11. Statische Analyse

  • Kompiliert auf GCC, Clang und MSVC ohne Warnungen
    • Auch der Clang Static Analyzer liefert keine gültigen Warnungen
    • Die tatsächliche Wirksamkeit statischer Analyse bei der Erkennung realer Bugs ist begrenzt

12. Zusammenfassung

  • Trotz seines Open-Source-Status hält SQLite Qualität auf kommerziellem Niveau und eine niedrige Fehlerrate aufrecht
    • Gründliche Tests und Code-Design sind die entscheidenden Faktoren
    • Jedes Release durchläuft die oben genannten Verfahren und wird so als auch in mission-kritischen Umgebungen vertrauenswürdige DB-Engine bereitgestellt

Noch keine Kommentare.

Noch keine Kommentare.