Linux-Kernel Quellcode patchen

Manchmal kann es erforderlich sein, den Quellcode des Linux-Kernels zu patchen - z. B. weil neue Treiber hinzugefügt oder Sicherheitslücken behoben wurde.

In aller Regel übernimmt diese Arbeit der Distributor des Vertrauens - manchmal möchte man jedoch nicht hierauf warten (oder hat eben Spaß daran diese Arbeit selbst zu übernehmen).

Ich bereite mich derzeit auf die LPIC-2 Zertifizierung vor - der nachfolgend geschilderte Vorgang ist hier Bestandteil des Prüfungsziels "201.3 Patching a kernel".

Kernel-Versionen

Im ersten Moment ist es nicht einfach beim Versionsschema des Linux-Kernels durchzublicken. Am Besten lässt sich dieses anhand einiger Beispiele erklären.

Die erste Ziffer bezeichnet das Hauptrelease des Kernels. Seit 2011 ist dieses die Version 3. Frühere Versionen tragen die Versionsnummer 2, antike Versionen sind an einer vorangestellten 1 zu erkennen:

  • 3.12.4 - Kernel des 3.Hauptreleases
  • 2.5.75 - Kernel des 2.Hauptreleases

Die zweite Ziffer bezeichnet das Major-Release des Kernels. Eine gerade Zahl steht für einen stabilen Kernel, der sich für den Einsatz in Produktivumgebungen eignet, während eine ungerade Zahl einen Entwicklungskernel bezeichnet. Diese Regel gilt nur für Kernel der Version 2.6 oder früher - seit Version 3 gilt jedes Major-Release als stabil:

  • 3.12.4 - stabiler Kernel da Major-Release der Version 3, für Produktion geeignet
  • 3.14-rc4 - noch nicht vollständig ausgereifter Kernel, nicht für die Produktion geeignet
  • 2.5.75 - Entwicklungskernel da ungerade Zahl, nicht für die Produktion geeignet

Klassische Entwicklungskernel gibt es mit Version 3 nicht mehr, es gibt jedoch Vorab-Versionen die als "RC" (Release candidate) bezeichnet werden. Diese enthalten u.a neue Funktionen, die noch nicht ausreichend genug erprobt wurden, um endgültig in den stabilen Kernel aufgenmommen zu werden. Einen solchen Kernel sollte man nicht auf Produktivsystemen installieren.

Die dritte Ziffer bezeichnet das Minor-Release, welches wohl am interessantesten ist. Wenn neue Treiber oder Features implementiert werden, ändert sich dieser Wert:

  • 3.12.4 - 4.Funktion/Feature-Update des 3.12-Kernel
  • 2.5.75 - 75.Funktion/Feature-Update des 2.5-Kernel

Manche Kernel haben sogar eine vierte Ziffer. Diese bezeichnet das Security-Release - diese Versionsinformation wurde im Jahr 2005 eingeführt. Wird nach dem Veröffentlichen eines Minor-Release-Updates eine kritische Sicherheitslücke erkannt und behoben, kann diese vor Erscheinen des nächsten Minor-Release-Updates behoben werden. Ein solcher Kernel trägt dann eine vierte Ziffer:

  • 3.12.4-5 - 5.Security-Release des 4.Funktion/Features-Updates des 3.12-Kernel.
  • 2.5.75 - 75.Funktion/Feature-Update des 2.5-Kernel

Bezugsquellen des Linux-Kernels

Prinzipiell können sämtliche Versionen des Linux-Kernels auf der Webseite https://www.kernel.org heruntergeladen werden.

Für jede Kernel-Version gibt es den vollständigen Quellcode, erfahrungsgemäß ist dieses Archiv ca. 100 MB groß (XZ-komprimiert). Diesen Download findet man entweder auf der Startseite oder auf einem der folgenden Unterseiten:

Den Quellcode-Download gibt es jeweils in verschiedenen Kompressionsformaten: gz (GZIP), bz2 (BZIP2) und xz (XZ Utils). In aller Regel ist das mit XZ komprimierte Archiv das kleinste, während das mit GZIP komprimierte Archiv das größte ist. Sofern man über das xz-Programm verfügt, empfiehlt es sich, stets das tar.xz-Archiv herunterzuladen.

Wenn man ein wenig durch die o.g. Ordner blättert, fällt auf, dass es noch weitere Dateien gibt:

  • Changelog-VERSION - Changelog der jeweiligen Kernel-Version, enthält Hinweise auf Fixes und neue Treiber/Features
  • sha256sums.asc - kryptografische Prüfsummen aller Downloads im aktuellen Verzeichnis, kann zur Validierung des fehlerfreien Downloads verwendet werden
  • patch-VERSION.sign - GPG-Key des jeweiligen Downloads zur Überprüfung der Authentizität
  • patch-VERSION.[gz|bz2|xz] - Patch vom jeweiligen Major-Release für die ausgewählte Minor-Version

Insbesondere der letzte Punkt ist interessant. Verfügt ein System über den Quellcode eines **3.12.0-**Kernels kann man diesen durch Anwendung des 3.12.13-Patches auf die Version 3.12.13 aktualisieren.

Warum sollte man hierfür den Patch und nicht das vollständige Quellcode-Archiv herunterladen? Weil der Patch wesentlich kleiner ist (erfahrungsgemäß wenige Kilobyte) - man spart hier Bandbreite. Insbesondere bei der Verwaltung mehrerer Systeme kann dieser Aspekt nicht gerade uninteressant sein.

Was übrigens nicht geht ist das Aktualisieren des Quellcodes eines 3.12.1-Kernels auf 3.12.13 durch Anwenden des eben erwähnten Patches. Dieser Vorgang wird fehlschlagen, da das patch-Kommando (dazu später mehr!) von einem bereits gepatchten Kernel-Quellcode ausgeht:

1# cd /usr/src/linux-3.12.1
2# xzcat ../patch-3.12.13.xz | patch -p1 --dry-run
3...
4Reversed (or previously applied) patch detected!  Assume -R? [n]

Für das Aktualisieren von Quellcodes eines höheren Minor-Releases (wie eben z. B. 3.12.1) gibt es spezielle inkrementelle Patches. Die vorhin erwähnten Patches beziehen sich immer auf ein Major-Release (genau genommen ein 0-Minor-Release). Diese inkrementellen Patche befinden sich in oben erwähnter Ordnerstruktur in einem Unterordner namens incr:

Hier findet sich eine ähnliche Dateistruktur wie bei vollwertigen Quellcode-Downloads. Die Patch-Dateien werden immer für exakt einen Versionssprung erstellt - beispielsweise von 3.12.1 auf 3.12.2. Möchte man nun beispielsweise den Kernel-Quellcode von 3.12.1 auf 3.12.13 aktualisieren muss man die einzelnen inkrementellen Quellcode-Patches herunterladen:

  • patch-3.12.1-2.xz - Patch von 3.12.1 auf 3.12.2
  • patch-3.12.2-3.xz - Patch von 3.12.2 auf 3.12.3
  • patch-3.12.3-4.xz - Patch von 3.12.3 auf 3.12.4
  • ...und so weiter

In diesem Fall muss man 12 verschiedene Patch-Dateien herunterladen, die aber - in der Summe - immer noch nur einen Bruchteil des vollständigen Quellcode-Archivs ausmachen.

Diese einzelnen Patches müssen dann nacheinander auf den bereits vorhandenen Kernel-Quellcode (in diesem Beispiel 3.12.1) angewendet werden.

Patchen des Kernels

Hat man den notwendigen Quellcode-Patch heruntergeladen, kann das eigentliche Patchen beginnen.

Zum Patchen des Quellcodes wird das patch-Kommando verwendet. Im folgenden Beispiel wurde der Quellcode des 3.13-Kernels und der 3.13.4-Patch heruntergeladen. Da es sich beim 3.13-Kernel um das erste Release (und kein höheres Minor-Release) handelt, kann der Quellcode direkt auf 3.13.4 aktualisiert werden.

Die Patch-Datei ist mit XZ komprimiert - bevor sie verwendet werden kann muss sie dekomprimiert werden. Man kann allerdings auch das xzcat-Kommando verwenden und die Ausgabe an das patch-Kommando übergeben. Das ist meiner Meinung nach die schönere Lösung, da man hier keine temporären Dateien auf der Festplatte anlegen muss.

Das patch-Kommando kann anschließend die einzelnen Quellcode-Dateien aktualisieren. Wichtig sind die folgenden Parameter des Programms:

  • -p - gibt die Anzahl der Ordnerstruktur-Ebenen an, die von den Dateinamen entfernt werden sollen. Je größer die Zahl, desto mehr Ebenen werden entfernt. Ohne Angabe des Parameters weiß patch nicht, wo zu patchende Dateien anzufinden sind. Der Patch des Linux-Kernels erfolgt idealerweise immer aus dem Quellcode-Verzeichnis (/usr/src/linux-x.xx) heraus mit Angabe des Parameters -p1. Somit wird die erste Ordnerstruktur abgeschnitten - so wird aus dem Pfad /home/user/a der Pfad home/user/a
  • --dry-run - "Trockentest", es wird lediglich eine Simulation des Vorgangs durchgeführt. Vor jedem Patch empfehle ich die Verwendung dieses Schalters, um Fehler auszuschließen
  • -s - silent, zeigt lediglich Fehlermeldungen an
  • -R - reverse, invertiert die Code-Veränderungen; in diesen Fall wird der Patch wieder entfernt

Zuerst werden jedoch der Kernel-Quellcode und Quellcode-Patch (von 3.13[.0] auf 3.13.4) heruntergeladen und im /usr/src-Verzeichnis gespeichert. Anschließend wird der Kernel-Quellcode entpackt und in den entstandenen Ordner gewechselt:

1# cd /usr/src
2# wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.13.tar.xz
3# wget https://www.kernel.org/pub/linux/kernel/v3.x/patch-3.13.4.xz
4# tar xf linux-3.13.tar.xz
5# cd linux-3.13

Nun wird mittels xzcat der vorher heruntergeladenene Quellcode-Patch ausgegeben und die Ausgabe an das patch-Kommando weitergeleitet. Dieses Programm simuliert die Quellcode-Manipulation:

1# xzcat ../patch-3.13.4.xz | patch -p1 --dry-run
2patching file tools/power/x86/turbostat/Makefile
3patching file tools/power/x86/turbostat/turbostat.c
4patching file virt/kvm/coalesced_mmio.c
5...

Das sieht gut aus! Nun kann der Patch bedenkenlos angewendet werden:

1# xzcat ../patch-3.13.4.xz | patch -p1

Würde man nun versuchen, den Patch erneut anzuwenden, erscheint ein entsprechender Hinweis:

1# xzcat ../patch-3.13.4.xz | patch -p1
2patching file Documentation/devicetree/bindings/ata/marvell.txt
3Reversed (or previously applied) patch detected!  Assume -R? [n]

Beabsichtigt man einen Patch rückgängig zu machen (was auch möglich ist), sollte man hierfür den -R-Schalter verwenden.

Da der Kernel-Quellcode nun auf 3.13.4 aktualisiert wurde, könnte man einen inkrementellen Patch auf die Version 3.13.5 anwenden. Wir erinnern uns: hier würde ein herkömmlicher Patch (patch-3.13.5.xz) nicht zum Ziel führen, da dieser als Ausgangsbasis das Major-Relase (und keine höheren Minor-Releases) verwendet. Der inkrementelle Patch wird also heruntergeladen und wieder über xzcat und patch angewendet. Auch hier empfiehlt sich wieder eine Simulation:

1# wget https://www.kernel.org/pub/linux/kernel/v3.x/incr/patch-3.13.4-5.xz
2# xzcat patch-3.13.4-5.xz|patch -p1 --dry-run
3patching file net/wireless/nl80211.c
4patching file scripts/mod/file2alias.c
5patching file sound/pci/hda/patch_realtek.c
6...

Auch das sieht gut aus - also kann auch dieser Patch angewendet werden. Um die Übersichtlichkeit aufrecht zu erhalten, kann man danach auch den Ordnernamen des Kernel-Quellcodes dem neuen Minor-Release anpassen. So vergisst man nicht, dass der Kernel bereits gepatcht wurde:

1# xzcat patch-3.13.4-5.xz|patch -p1
2...
3# cd ..
4# mv linux-3.13 linux-3.13.5

Nun steht dem munteren Kernel-Übersetzen nichts mehr im Wege. 🙂

Übersetzungen: