/* basquiat's lovely winter riot */: a unique and beautiful snowflake in your heart's lovely winter riot

My first OpenBSD kernelbug

Mit ‘pf’, ‘pfsync’ und ‘CARP’ bietet OpenBSD eine vergleichsweise einfach zu administrierende, leistungsfähige Infrastruktur für das Failover redundant aufgesetzter Firewalls. Neben dem essentiellen Paketfilter ‘pf’ kümmert sich ’pfsync’ in diesem Kontext um das synchrone Statehandling der verschiedenen Nodes, um bei einem Wechsel des aktiven Firewallrechners im Cluster bestehende Netzwerkverbindungen aufrecht erhalten zu können. Der Einsatz des Common Address Redundancy Protocols CARP erledigt auf OSI-Schicht 2 und 3 dabei das eigentliche Procedere zur Hochverfügbarkeit, bei dem es klassischerweise zwischen MASTER- und BACKUP-Rollen auf Interface-Ebene unterscheidet. Eine detailreichere doch kurz gehaltene Übersicht bietet der Artikel “Firewall Failover with pfsync and CARP”.

Mit dem lange überfälligen Upgrade eines schon etwas in die Jahre gekommenen OpenBSD 3.7 Clusters auf Release 4.1 erhielt ich die einmalige Chance, meinen ersten OpenBSD-Kernelbug - wenn auch eher unfreiwillig in gewohnt unpassendster Situation - zu entdecken; das sequentielle Neuladen der Regeln des auf zwei Clusternodes verteilten Paketfilters führte nach wenigen Iterationen regelmäßig zum Absturz des Systems:

kernel: page fault trap, code = 0
Stopped at	pfsync_insert_net_state+0x472:	movl	0(%eax,%edx,4),%edx

Für Neulinge unter den digitalen Kammerjägern beschreibt die Kurzanleitung “How to debug kernel crashes” das Erstellen verwertbarer Bugreports für die Entwickler - in der Regel sollte man das fehlerhafte Verhalten jedoch zuerst unter Einsatz unmodifizierter GENERIC-Kernel reproduzieren können. Gesagt, getan; nun folgend das übersetzte Kochrezept.

Mit der Analyse des Trace-Outputs (im OpenBSD-Kerneldebugger ’ddb’ per ‘trace’ aufgerufen) kann die betreffende Funktion erkannt und im Quellcode schnell lokalisiert werden. Im Falle des gestorbenen Firewallnodes lässt folgende Ausgabe das Problem auf das Sourcefile ‘sys/net/if_pfsync.c’ zurückführen:

pfsync_insert_net_state(e34d4038,1,8,e34d4038) at pfsync_insert_net_state+0x472

pfsync_input(e3486a00,14,0,0,d0d1a034) at pfsync_input+0xa21
ipv4_input(e3486a00,d0d0e900,0,d08ab000,30) at ipv4_input+0x4f1
ipintr(d0640058,d30010,d08a0010,d08a0010,d08ab000) at ipintr+0x70
Bad frame pointer: 0xd08ace24
Erneut mit Debuginformationen kompiliert und disassembliert finden wir per ‘grep’ die fehlerhafte Funktion und addieren der dort angegebenen Speicheradresse den Offset aus unserem Trace-Output hinzu:
# grep “<pfsync_insert_net_state>” if_pfsync.dis
> 000002f4 <pfsync_insert_net_state>

Adam Riese addiert die Hexadezimalzahlen 0x2f4 + 0x472 zu 0x766 - genau in jener Zeile sollte sich innerhalb unseres disassemblierten Codes die Instruktion aus unserem Kerneltrap finden, und siehe da:

/usr/src/sys/net/if_pfsync.c:248
      756:       a1 b4 04 00 00          mov    0x4b4,%eax
      757:       R_386_32   pf_main_anchor
      75b:       66 c1 ca 08             ror    $0x8,%dx
      75f:       c1 ca 10                ror    $0x10,%edx
      762:       66 c1 ca 08             ror    $0x8,%dx
      766:       8b 14 90                mov    (%eax,%edx,4),%edx
      769:       89 55 ec                mov    %edx,0xffffffec(%ebp)
      76c:       e9 e5 fb ff ff          jmp    356 <pfsync_insert_net_state+0x62>
      771:       8d 76 00                lea    0x0(%esi),%esi

Damit haben wir die genaue Zeilenangabe des betreffenden Codeteils innerhalb der Funktion ‘pfsync_insert_net_state’ gewonnen, welche wir schon im Sourcefile ‘sys/net/if_pfsync.c’ festmachen konnten. Mit etwas Kontext sprechen also alle Indizien das folgende Konstrukt schuldig:

/*
* If the ruleset checksums match, it’s safe to associate the state
* with the rule of that number.
*/
if (sp->rule != htonl(-1) && sp->anchor == htonl(-1) && chksum_flag)
        r = pf_main_ruleset.rules[
            PF_RULESET_FILTER].active.ptr_array[ntohl(sp->rule)];
else
        r = &pf_default_rule;

Tiefer bewanderte Kerneldeveloper identifizieren hier eine Racecondition zwischen den Ruleset-Reloads beider Maschinen und stellen - keine 24 Stunden nach Meldung des Bugs - den ersten Patch zur Evaluation, der mittlerweile mit Revision 1.83 im CVS des MAIN-Branches enthalten ist - und auch hier erfolgreich unter Stress gesetzt wurde:

/*
* If the ruleset checksums match, it’s safe to associate the state
* with the rule of that number.
*/
if (sp->rule != htonl(-1) && sp->anchor == htonl(-1) && chksum_flag &&
    ntohl(sp->rule) <
    pf_main_ruleset.rules[PF_RULESET_FILTER].active.rcount)
        r = pf_main_ruleset.rules[
            PF_RULESET_FILTER].active.ptr_array[ntohl(sp->rule)];
else
        r = &pf_default_rule;

Das Fazit: Selbst durchaus unerfreuliche Vorkommnisse bieten bei Verfügbarkeit des Quellcodes das Potential, ungekannte Hintergründe zu verstehen und von Ihnen manchesmal Neues zu erlernen. Die Kommunikation mit den Entwicklern freier Software lässt darüber hinaus das gute Gefühl entstehen, selbst als einfacher Bote einer schlechten Nachricht beim Prozess der stetigen Verbesserung der quelloffenen Produkte positiv mitwirken zu können. Denn auch gerade davon lebt Open Source - den ausführlichen Bugreports dankbarer User.

1713 Klicks

Quo vadis, Linux?

Wie schon berichtet wird seit dem Developers Summit in Ottawa ein neues Entwicklungsmodell für den Linux Kernel diskutiert, das bei seiner Durchsetzung eine Abkehr von der bisher geltenden Unterscheidung zwischen Stable- und Development Branch bedeuten würde. Ziel scheint es nun nicht mehr zu sein, einen gereiften Referenzkernel, der in seiner als stabil gekennzeichneten Version auch wegen der regelmäßig eingepflegten Security Updates Ausgangsbasis für viele Installationen und weiterführende Patchsets ist, anzubieten, sondern die Integration möglichst vieler Features in kürzester Zeit voranzubringen. Auch das Entfernen aktuellen und potentiell von Anwendern eingesetzten Codes aus der laufenden Stable Linie scheint legitim, so dieser keinen Maintainer mehr hat oder aus technischen Gründen als obsolet angesehen wird.

Für die Stabilität sollen in Zukunft die Distributoren verantwortlich sein - ein Unding, guckt man sich verschiedenste Distributionskernel und ihre Unterschiede an. Es darf die Frage gestellt werden, welchen Kernel der Endnutzer als stabile Referenz heranziehen soll - und in wie weit es sinnvoll ist, die Stabilität des Systems in die Hände konkurrierender, kommerzieller Unternehmen zu geben, die in manchen Fällen nicht unbedingt immer durch eine geglückte Politik des Patchens freier Software aufgefallen sind. Den Verweisen auf die Existenz freier Distributionen stelle ich in diesem Kontext den Mangel an Manpower eben dieser gegenüber - so sucht z.B. Gentoo zur Zeit händeringend Maintainer für die eigenen Kernelpatches.

Einen ersten Höhepunkt erreichte die Diskussion um das neue Entwicklungsmodell, als Grek Kroah einen neuen Patch veröffentlichte, der den kompletten “devfs”-Code aus dem Kernel entfernte:

Mehr »
3204 Klicks

Baustelle 2.6

Wie Golem berichtet, wird es in naher Zukunft keine neue Entwicklungslinie des Linux Kernels in Version 2.7 geben. Bisher galt die Regel, das größere Neuerungen in einem separaten Strang eingepflegt und auf ihre Tauglichkeit getestet werden, bevor sie unter anderer Versionsnummer in ein gereiftes, zuverlässiges und berechenbares Release für den Endnutzer gelangen.

Zur Unterscheidung eines solchen gereiften und deshalb als ausreichend stabil erachteten Endnutzer Kernels von seiner Entwicklungslinie diente seit jeher die Versionsnummerierung: Ist sie ungerade (2.3.x, 2.5.x), so handelt es sich offiziell um eine Entwicklerversion, in der größere Änderungen die sonst so robusten Fundamente hin und wieder durchaus ins Wanken geraten lassen können. Bei geradzahligen Releases (2.4.x, 2.6.x) wird davon ausgegangen, dass der Kernel stabil läuft und keine unangenehmen Überraschungen beherbergt - was so bisher allerdings auch nicht immer der Fall sein musste. Bis heute scheuen sich viele User davor, Linux 2.6.x auf Produktivsystemen zum Einsatz zu bringen.

Die beiden Hauptverantwortlichen für die Entwicklung der Version 2.6 des Linux Kernels, Linus Torvalds und Andrew Morton, sehen trotz monatlicher Patches im Umfang von bis zu 10 MByte keine Notwendigkeit, schon jetzt eine neue Entwicklungslinie 2.7 abzuspalten, um damit den Weg für einen kommenden Nachfolger in der Version 2.8 zu ebnen. Beide sind mit den Ergebnissen ihrer derzeitigen Zusammenarbeit zufrieden und wollen auch tiefgreifendere Änderungen direkt in die aktuelle “stable” Linie einfließen lassen - allerdings sollen diese erst in Andrew Mortons eigenem Entwicklungsstrang, dem sogenannten mm-Kernel, reifen und evaluiert werden.

Ziel dieser Politik ist eine schnellere Evolution des Linux Kernels, der so in kürzerer Zeit mehr neue Features unterstützen soll, als dies bisher der Fall war. Eine solche Politik allerdings kann durchaus von Nachteil für die viel beschworene Stabilität des Kernels sein, was gerade konservative User nicht mit ungeteilter Freude kommentieren dürften.

Relevante Links:

New dev model (Linux Kernel Mailinglist)

3188 Klicks