Die Zahlen, wie viele bislang unerkannte Bugs eine Mammut-Software wie der Linux-Kernel enthält, machen Angst. Linus Torvalds rückt mit Test-Robots, Testern aus Fleisch und Blut, ausgefeilten Werkzeugen und einer strengen Organisation dem Schädlingsbefall in seinem Refugium zu Leibe. Erfolgreich.
“I just found the last Bug”, lautet ein alter Informatikerwitz. Denn dass er tatsächlich gerade den letzten Fehler gefunden hat, kann höchstens der Entwickler eines Mini-Tools behaupten. Reale Software dagegen ist nie fehlerfrei, zwei Bugs pro 1000 Codezeilen gelten schon als guter Wert (Fehlerdichte 2,0).
Die Rechnung auf den Linux-Kernel mit seinen 24 Millionen Codezeilen angewandt ergibt rund 48 000 Fehler. Wow! Jede neue Version bekommt um die 14 000 Patches verpasst, die fast 600 000 neue Zeilen hinzufügen und gut 300 000 löschen. Das macht statistisch alle zehn Wochen grob 300 neue Fehler. Noch mal: Wow!
Zum Glück weisen alle aktuelle Linux-Kernel eine überdurchschnittlich niedrige Fehlerdichte von 0,47 auf. Dies stellt die Linux-Community durch zwei Maßnahmen sicher: Erstens durch einen strukturierten, ausgefeilten Entwicklungsprozess, der extensive, kritische Code-Reviews vorsieht, und zweitens durch umfangreiche Tests.
Von Mammuts und Wanzen
Dabei ist das Sicherstellen von weitestgehender Fehlerfreiheit bei einer solchen Mammut-Software selbst eine Mammut-Aufgabe. Die ergibt sich einerseits durch die große Codemenge, die trotz der geringen Fehlerdichte bedeutet, dass statisch ermittelte 11 280 Wanzen (Bugs) in Kernel hausen, was bedrohlich klingt. Andererseits stellen die Linux-Anwender sehr hohe Anforderungen an Skalierbarkeit, Portabilität und Sicherheit. Der Kernel soll auf einem sicherheitskritischen, eingebetteten System genauso gut arbeiten wie auf einem Großrechner, auf einem Desktop-PC oder wie auf den ARM-Chips in Smartphones.
Darüber hinaus sind da noch die einzelnen Anwender, die Linux auf die eine und eben auch andere Art und Weise bedienen. Die Treiber-Entwickler kämpfen oft genug damit, sich nur eingeschränkt Informationen über Hardwarekomponenten beschaffen zu können.
Gerade im PC-Bereich schauen zudem viele Hersteller nur auf Windows und klammern die Linux-Welt aus. Darum stehen für die Linux-Entwickler Reverse-Engineering sowie Trial and Error auf praktisch jedem Wochenplan.
Trotz aller Herausforderungen zeigt sich Linux im alltäglichen Betrieb robust und stabil. Diesen Kampf gewinnt jeden Tag aufs Neue eine Armee von Testern – unter menschlichen Open-Source-Anhängern rekrutierte, aber stromfressende Söldner machen auch mit – Testbots genannt.
Roboter testen
Die Webseite https://elinux.org/Test_Systems listet etwa 25 Testbots auf, von denen ein Handvoll aktiv betrieben und relevant sind: Kernelci.org, Syzbot, CKI, Lava, Kerneltests, 0day-lkp. Die Roboter-Systeme laden sich selbstständig den aktuellen Quellcode herunter, konfigurieren ihn auf die eine oder andere Weise und generieren dann den Kernel beziehungsweise mehrere für diverse Hardwareplattformen. Sollten dabei Fehler oder Unklarheiten auftreten, entstehen automatisch Bugreports.
Ansonsten booten die Bots die frisch generierten Kernel, werten die Bootmeldungen aus und – falls Auffälligkeiten festzustellen sind – weisen auf diese hin. Ist der Kernel aktiv, startet schließlich die Testsoftware, die sowohl die generelle Funktionalität als auch die Reaktion auf fehlerhafte Parametrierung von Systemcalls und Ähnlichem testen. Die Robots nehmen den Entwicklern damit erheblich Arbeit ab. Sie unterscheiden sich voneinander in ihren Testzielen und durch die Sponsoren beziehungsweise Betreiber.
200 Bugs pro Monat
Als äußerst erfolgreich beim Finden der kleinen Wanzen hat sich bislang Syzbot ([1], Abbildung 1) erwiesen. Laut dem Google-Projekt Syzkaller, dessen Automations-Bot Syzbot ist, findet der Robot rund 200 Bugs (!) pro Monat [2]. Das Projekt Kerneltests nutzt die Python-Software »buildbot«, um mit deren Hilfe für rund 40 unterschiedliche Hardwareplattformen Kernel zu generieren und zu testen. Die Projektseite [3] hält dazu umfangreiche Statistiken zur Einsicht bereit (Abbildung 2).
Kernelci.org [4] ist ein relativ junges Projekt, das sich auf Upstream-Kernel spezialisiert hat. Es testet also neue Patches in einem relativ frühen Stadium, bevor diese an Linus Torvalds gehen, und detektiert per »git bisect« die für Probleme verantwortlichen Patches. Die Option »bisect« zum Sourcecode-Verwaltungstool Git erlaubt auf bequeme Weise die Suche nach einem Quellcode, der für eine Verschlechterung (Regression) verantwortlich ist. Dazu bekommt Git sowohl die letzte bekanntermaßen funktionierende Version und die nicht mehr funktionierende Version übergeben.
Das von Red Hat geförderte Projekt Continuous Kernel Integration (CKI, [5]) folgt einer ähnlichen Mission. Auch CKI fahndet in solchen Patches nach Bugs, die es noch gar nicht bis zu Linus Torvalds geschafft haben. Damit unterstützen die hinter dem Projekt stehenden Kernel-Entwickler und -Tester direkt die Maintainer der Subsysteme und diejenigen, die Patches zuliefern. CKI kompiliert Kernel für vier 64-Bit-Hardwareplattformen und testet sie nach jedem erfolgreichem Boot mit Hilfe der Software des Linux Test Project (LTP, [6]).
Die Software Lava [7] gießt einen Testroboter speziell für jene Plattformen, die auf einem ARM-Prozessor der Architektur-Varianten v7 und v8 ruhen. Linaro betreibt diesen Bot, eine auf ARM-Hardware spezialisierte Firma.
Den Zero Day Service [8], auch bekannt als Linux Kernel Performance (LKP), schließlich betreibt Intel. Er testet verschiedenste Quellcode-Zweige in mehr als 100 Konfigurationen bezüglich Generierung, Booten, Funktion, Performance und Stressfestigkeit. Sobald 0day-lkp Unstimmigkeiten identifiziert, bekommen die Entwickler direkt per E-Mail Bescheid.
Der Zeitrahmen für einen Test liegt bei lediglich rund einer Stunde. Wenn Probleme auftreten, versucht der Dienst, das verursachende Patch zu identifizieren. Anders ausgedrückt: Checkt ein Entwickler ein Patch in sein Git ein, erhält er bereits nach einer Stunde ein erstes Feedback bezüglich möglicher Inkompatibilitäten oder Fehler.
Die Macht der Menge
Neben den Bots sind viele Tester mit biologischem Stoffwechsel aktiv. Linus setzt dabei auf Crowdtesting: Anstatt hinter verschlossenen Türen den Next-Generation-Code nur eingeweihten Testern zu überlassen, hat beim offenen Entwicklungsmodell jeder jederzeit Zugriff auf die aktuelle Entwicklerversion. Das Herunterladen, Generieren und Installieren von eigenen Kerneln ist darum auch für Nicht-Informatiker problemlos möglich [9]. Die vielen Anwender der Crowd testen den Code regelmäßig auf den unterschiedlichsten Plattformen und mit vielerlei Konfigurationen.
Menschliche Tester haben viele Möglichkeiten, Fehler zu finden. Funktionale Probleme beispielsweise sind meist offensichtlich. Wenn mit dem neuen Kernel das Touchpad nicht mehr geht oder eine Applikation, die mit anderen Kernelversionen problemlos verwendbar war, jetzt reproduzierbar abstürzt, dann ist ein fehlerhaftes Verhalten erkannt.
Die überwiegende Anzahl an Problemen ist aber weniger offensichtlich. Hier blickt der Tester hinter die Kulissen und durchforstet die Kernellogs, insbesondere die Datei »/var/log/kern.log«. Er greift auch direkt mit Hilfe des Programms »dmesg« auf die Meldungen im Kernel zu.
Lässt er dabei über die Option »-t« den Zeitstempel weg, kann er schnell Unterschiede in verschiedenen Kernelversionen identifizieren. Dazu speichert er die Ausgaben der nachfolgenden Aufrufe auf dem funktionierenden System und vergleicht sie mit den Ausgaben des problembehafteten Systems. Dabei erscheint es sinnvoll, die einzelnen Warnebenen getrennt zu betrachten:
dmesg -t -l emerg dmesg -t -l crit dmesg -t -l alert dmesg -t -l err dmesg -t -l warn dmesg -t -k dmesg -t
Besondere Aufmerksamkeit verdienen die so genannten Oops-Messages im Kernellog. Sie signalisieren den Absturz einer Kernelkomponente, etwa eines Gerätetreibers. Viele solche Fehler treten erst unter hoher Systemlast auf. Daher praktizieren die Kernelentwickler Stresstests. Die am häufigsten eingesetzten Lasttests sind das Kompilieren des Linux-Kernels selbst und die bereits erwähnte Testsuite vom Linux Test Project.
Kernel-Testing-Tools
Jenseits der Bots bietet der Linux-Kernel auch selbst einiges auf, um zu guten Tests zu kommen. Als wichtige Maßnahme stattet er Testwillige mit speziellen Testwerkzeugen aus – bei denen sich übrigens auch die Robots bedienen.
Im Kernel-Quellcodeverzeichnis findet sich unterhalb von »tools/testing/« mit »fault-injection«, »ktest«, »nvdimm«, »radix-tree«, »scatterlist«, »selftest« und »vsock« eine Batterie von Testkomponenten. Einige der Namen deuten an, um welches Subsystem sich das Werkzeug kümmert.
»selftest« ist die Kernelkomponente, die eine Umgebung für Runtime-Tests im Kernel schafft. Einige Tests, wie für das Hotplugging, sind implementiert. Ziel ist es, diese Tests innerhalb von maximal 20 Minuten auszuführen, sodass der Tester kurzfristig ein Feedback bekommt.
»ktest« ist ein Perl-Skript, um das Generieren und Booten des Kernels zu automatisieren. Sollten Fehler auftreten, versucht es per »git bisect« das verantwortliche Patch ausfindig zu machen.
In-Kernel-Unterstützung
Als weitere eigene Maßnahme hat der Kernel eine Reihe interner Testroutinen implementiert (Abbildung 3). 34 unterschiedliche In-Kernel-Runtime-Tests sind es in Linux 5.1. Der Kernel testet auf Wunsch die Implementierung diverser Datenstrukturen (verkettete Listen, Bäume), mehrerer Kernelfunktionen, beispielsweise »kprintf()«, sowie die Firewall oder das Live-Patching.
Darüber hinaus gibt es auf die einzelnen Subsysteme zugeschnittene Tests, die der Kernelbäcker auswählen darf. Bezüglich Speicherverwaltung bemerkt der Kernel Adress Sanitizer (Kasan, [10]) den (fehlerhaften) Zugriff auf bereits freigegebene Speicherbereiche beziehungsweise den Zugriff außerhalb der reservierten Grenzen. »kmemleak« findet Speicherlöcher, also Speicher, den keiner mehr nutzt, aber auch niemand freigegeben hat. Die Komponente meldet Fehler über »/sys/kernel/debug/kmemleak«.
Die Komponente Code Coverage for Fuzzing (Kcov, [11]) versucht herauszufinden, welche Codeteile der Kernel durchläuft und welche nicht. In der Praxis kommt es üblicherweise zum Einsatz, um Systemcalls zu testen. Seine Ergebnisse exportiert Kcov über das Debug-Filesystem.
Meldung machen
Fehler finden ist aber nur die halbe Miete, genau genommen sogar noch weniger. Denn für die ständigen Kernel-Entwickler und -Maintainer ist die Reproduzierbarkeit entscheidend. Ihnen ist es daher wichtig, direkt die Ursache genannt zu bekommen, und am liebsten, sogar ein Patch zu erhalten. Tester sollten darum versuchen per »git bisect« der Ursache für ein beobachtetes Fehlerbild auf die Spur zu kommen.
Wer eine kleine Wanze findet, kann das auf drei Arten melden: Per E-Mail an die Linux Kernel Mailingliste, über die Mailingliste des zum Fehlerbild gehörenden Subsystems oder über die Webseite https://bugzilla.kernel.org (Abbildung 4). Kennt der Entdecker die zum Fehlerbild führende Datei, kann er über das in den Kernelquellen liegende Perl-Skript »scripts/get_maintainer.pl« unter Angabe des Dateinamens den zugehörigen Maintainer identifizieren.
In jedem Fall sollte er so viel Information zum Fehler liefern wie nur irgend möglich. Die Kernelentwickler haben daher den Meldeprozess im Dokument »report-bugs« als Teil des Admin Guide definiert (Abbildung 5, [12]). Erster Punkt: Sie nehmen keine Bugmeldungen entgegen für Kernel, die End of Life sind, also nicht mehr unterstützt sind. Zweitens muss der Meldende weitgehend sicherstellen, dass niemand vor ihm bereits die Wanze gemeldet hat. Auch darf eine Fehlermeldung nicht zwei unabhängige Fehlerbeschreibungen enthalten.
Der Bugreport soll im Fall einer E-Mail das Subject »PROBLEM: Einzeilige_Beschreibung« tragen. Die Nachricht selbst transportiert dann sämtliche Informationen, so wie sie das Reporting-Bugs-Dokument fordert.
Für Leute, die direkt ein Patch mitliefern können, gelten weitere Vorgaben, die denen für neuen Kernelcode ähneln. Weil die Fehlersuche und Beseitigung stets mit erheblichem Aufwand verbunden sind, ist es die beste Strategie, von vornherein auf eine hohe Codequalität zu setzen und Fehler zu vermeiden.
Fehler vermeiden
Daher unterziehen die Kernelhacker alle neuen Codezeilen einer aufwändigen Code-Review. So gibt es ähnlich dem Bugreport eine Submit-Checkliste [13], die jeder abzuarbeiten hat, der Code für den Kernel beisteuert. Das stellt sicher, dass neuer Code dem Coding Style folgt und auf unterschiedlichen Plattformen und mit unterschiedlichen Kernelkonfigurationen getestet ist.
Torvalds setzt bezüglich Code-Reviews auf ein hierarchisches Entwicklungsmodell. Er selbst ist zwar für die letztendliche Aufnahme von Code an der Spitze der Pyramide zuständig, stützt sich dabei aber auf die Integrationsarbeit von rund 100 Subsystem-Maintainern (Abbildung 6). Diese reviewen jeden neuen Code, bevor Torvalds ihn in der zwei Wochen dauernden Merge-Phase in den Kernel übernimmt (Abbildung 7).
Die Maintainer schreiben einerseits selbst Code, bekommen andererseits auch welchen von den übrigen Entwicklern geliefert, die damit am Fuße der Pyramide stehen. Die Maintainer reichen Code erst dann weiter, wenn sie ihn bereits gut getestet haben. Linus selbst lässt dann jeden Code noch acht Wochen reifen. In dieser Zeit veröffentlicht er diverse Release-Kandidaten, die wiederum die Masse testet, der Crowd. Erst wenn statistisch betrachtet die Masse der Korrekturen zum neuen Code auf ein akzeptables Maß gesunken ist, steht die Kernelfreigabe an.
Torvalds hat zudem die Überprüfung von Standardfehlern automatisiert und hierzu vor mehr als 15 Jahren das Werkzeug Sparse geschrieben. Es analysiert statisch den Code auf korrekte Kernel-spezifische Datentypen. So stellt Sparse beispielsweise fest, ob eine Userland-Adresse (Applikationsadresse) im Kernel dereferenziert ist. Um die Prüfung per »sparse« zu aktivieren, setzt der Linuxer beim Aufruf von »make« die Variable »C« auf 1 oder 2, abhängig davon, ob er alle Quellcode-Dateien prüfen lassen will oder nur die neu zu kompilierenden (»make C=1«).
Kammerjäger
Wie man sieht, hält Linus trotz hohem Entwicklungstempo die Wanzen-Kolonie gut in Schach. Genau genommen ist dies sogar seine Hauptbeschäftigung als Chef-Controller. Fehler zu finden und zu beseitigen hat sich im Linux-Projekt evolutionär entwickelt. Der Prozess ist zwar noch nicht vollkommen, weist aber eine erfreuliche Reife auf. Insbesondere beim schnellen Reagieren auf Fehler, Spectre und Meltdown beispielsweise, zeigt sich, dass offene Entwicklungsmodelle signifikante Vorteile aufweisen.
Infos
-
Syzkaller, Syzbot: https://github.com/google/syzkaller,https://syzkaller.appspot.com/upstream
-
Dmitry Vyukov, “Syzbot and the tale of thousand kernel bugs”: https://events.linuxfoundation.org/wp-content/uploads/2017/11/Syzbot-and-the-Tale-of-Thousand-Kernel-Bugs-Dmitry-Vyukov-Google.pdf
-
Kerneltests: https://www.kerneltests.org
-
Kernelci.org: https://kernelci.org
-
Continuous Kernel Integration: https://cki-project.org
-
Linux Test Project: https://linux-test-project.github.io
-
0-Day Test Service (Intel): https://01.org/lkp/documentation/0-day-test-service
-
Quade, Kunst, “Kern-Technik” – Folge 103 über Kernel generieren 5.0: Linux-Magazin 05/19, S. 88
-
Kasan: https://www.kernel.org/doc/html/v4.14/dev-tools/kasan.html
-
Kcov: https://www.kernel.org/doc/html/v4.15/dev-tools/kcov.html
-
Kernelbugs melden: https://www.kernel.org/doc/html/v5.0/admin-guide/reporting-bugs.html
-
Patches einreichen: https://www.kernel.org/doc/html/v5.0/process/submitting-patches.html













