mtu -= hlen + sizeof(struct frag_hdr);

if (overflow_usub(mtu, hlen + sizeof(struct frag_hdr), &mtu) || mtu <= 7)

goto fail_toobig;

hlen + sizeof(struct frag_hdr)

Linus' Kritik ist im Wesentlichen: Ist schlecht lesbar, ist ineffizient und immer noch unsicher.

Ich glaube, dass er bei allen drei Kritikpunkten irrt. Hier ist Linus' Gegenvorschlag, wie der Code lesbarer aussehen könnte:

if (mtu < hlen + sizeof(struct frag_hdr) + 8)

goto fail_toobig;

mtu -= hlen + sizeof(struct frag_hdr);

if (mtu < hlen + 8 + sizeof(struct frag_hdr))

Aber der Standard garantiert keine Reihenfolge der Auswertung. Der Compiler dürfte das meines Wissens machen. Und würde damit Linus' Check kaputtmachen.

Die Compiler-Builtins, die hinter diesem Makro stehen, sind hier beschrieben. gcc hat das ursprünglich von clang geklaut, aber hat es an einer wichtigen Stelle besser und damit überhaupt erst ordentlich benutzbar gemacht. Bei clang musste man anfänglich je nach Datentyp ein anderes Builtin aufrufen, und das ist natürlich auch voll für den Arsch, denn diese Subtilitäten sind ja gerade die Fehlerquelle, die wir durch sowas ausschließen wollen. Heute gibt es auch bei clang __builtin_add_overflow, und das sieht in Beispielcode so aus:

#include <stdlib.h> int s(int a,int b) {

int c;

if (__builtin_add_overflow(a,b,&c))

exit(1);

return c;

}

s:

movl %edi, %eax

addl %esi, %eax

jo .L9

rep ret

.L9:

[… Error handling …]

Was ich damit sagen will: Nein, Linus, die Builtins generieren keinen schlechten Code. Wenn der Code an der Stelle mehr tut als einen Conditional Jump einfügen, der im Normalfall nicht genommen wird (und damit fast kostenlos ist), dann war der Compiler möglicherweise smarter als du an der Stelle und hat erkannt, dass die beiden reinkommenden Typen nicht gleich breit waren und einen zusätzlichen Check eingefügt, ob ein Zwischenergebnis abgeschnitten wird.

So. Also nochmal zu den drei Argumenten. Unlesbar bestreite ich, weil bei dem Makro explizit und offensichtlich ist, was der Programmierer erreichen will. Das ist immer besser als eine Packung Ultrasmart-Bitpfriemel-Kram, bei dem der Auditor zehn Minuten braucht, bis er versteht, was hier der Trick und das gewünschte Ergebnis ist, und ob der Weg dahin überhaupt korrekt ist.

Das zweite Argument war Ineffizienz. Das habe ich gerade widerlegt.

Das dritte Argument war Unsicherheit. Nun, Linus, das mag jetzt schmerzen, aber der Code ist sicherer als der vorgeschlagene Ersatzcode :-)

Daher, liebe Leser: Wenn ihr C programmiert, und euch fragt, ob ihr lieber die Builtins verwenden sollt oder auch alte Compiler unterstützten, dann NEHMT DIE BUILTINS. Ich bin sogar dafür, gar keinen Fallback-Pfad für alte gcc-Versionen anzubieten. Wer neuen Code mit alten Compilern übersetzt, hat verdient, dass das kracht. Diese ganze Debian-Antiquitätenhändler-Fraktion braucht mal ein paar Warnschüsse vor den Bug. Das ist ja schlimmer als Windows XP!

Wenn ihr überlegt, ob ihr lieber selber Overflow-Checks hackt oder lieber die Builtins nehmen sollt, NEHMT DIE BUILTINS.

Wenn ihr überlegt, ob ihr Overflow-Checks braucht oder nicht, dann NEHMT DIE BUILTINS. Die Checks kosten so gut wie nichts. Nein, wirklich. Da muss man sich anstrengen, um das überhaupt mit geschickten Mikrobenchmarks messen zu können. Und wenn der Check an der Stelle überflüssig wäre, und gcc das erkennen kann, dann fliegen auch die eh schon fast kostenlosen Checks noch raus.

Es gibt keine Ausreden. Nehmt die Builtins. Jedes einzelne Mal. Insbesondere, wenn es um Multiplikation geht. Das verkacken so viele Programmierer da draußen, dass ich das naheliegende Trinkspiel aufgeben musste. Probiert es gar nicht erst. Nehmt die Builtins. Und ich empfehle euch das, obwohl mein Lebensunterhalt davon abhängt, dass es Leute gibt, die nicht die Builtins benutzen.

Update: Wie sich rausstellt, sagt der C-Standard doch was zur Assoziativität von arithmetischen Operatoren. Der Compiler darf sich also nicht aussuchen, in welcher Reihenfolge er was addiert.