Beginnend mit C23, der Standardkopfzeile <stdckdint.h>
bietet die folgenden drei funktionsähnlichen Makros:
bool ckd_add(type1 *result, type2 a, type3 b);
bool ckd_sub(type1 *result, type2 a, type3 b);
bool ckd_mul(type1 *result, type2 a, type3 b);
où type1
, type2
y type3
ein beliebiger Ganzzahlentyp sind. Diese Funktionen addieren, subtrahieren oder multiplizieren jeweils a und b mit beliebiger Genauigkeit und speichern das Ergebnis in *result
. Wenn das Ergebnis nicht genau durch die folgende Formel dargestellt werden kann type1
gibt die Funktion true
("die Rechnung ist übergelaufen"). (Beliebige Genauigkeit ist eine Illusion; die Berechnungen sind sehr schnell, und fast alle seit den frühen 1990er Jahren verfügbare Hardware kann sie in nur ein oder zwei Anweisungen ausführen).
Das Beispiel von OP umschreiben:
unsigned long b, c, c_test;
// ...
if (ckd_mul(&c_test, c, b))
{
// returned non-zero: there has been an overflow
}
else
{
c = c_test; // returned 0: no overflow
}
c_test enthält in allen Fällen das Ergebnis der Multiplikation, das möglicherweise überlaufen ist.
Lange vor C23, GCC 5+ und Clang 3.8+ bieten Built-Ins, die auf die gleiche Weise funktionieren, mit der Ausnahme, dass der Ergebniszeiger als letzter statt als erster übergeben wird: __builtin_add_overflow
, __builtin_sub_overflow
y __builtin_mul_overflow
. Diese funktionieren auch bei Typen kleiner als int
.
unsigned long b, c, c_test;
// ...
if (__builtin_mul_overflow(c, b, &c_test))
{
// returned non-zero: there has been an overflow
}
else
{
c = c_test; // returned 0: no overflow
}
Clang 3.4+ führte Arithmetic-Overflow-Builtins mit festen Typen ein, aber sie sind viel weniger flexibel, und Clang 3.8 ist schon seit langem verfügbar. Suchen Sie nach __builtin_umull_overflow
wenn Sie diese trotz der neueren und bequemeren Alternative verwenden müssen.
Visual Studio 's cl.exe hat keine direkten Entsprechungen. Für vorzeichenlose Additionen und Subtraktionen, einschließlich <intrin.h>
ermöglicht Ihnen die Verwendung von addcarry_uNN
y subborrow_uNN
(wobei NN die Anzahl der Bits ist, wie addcarry_u8
ou subborrow_u64
). Ihre Unterschrift ist ein wenig stumpf:
unsigned char _addcarry_u32(unsigned char c_in, unsigned int src1, unsigned int src2, unsigned int *sum);
unsigned char _subborrow_u32(unsigned char b_in, unsigned int src1, unsigned int src2, unsigned int *diff);
c_in
/ b_in
ist das Carry/Borrow-Flag am Eingang, und der Rückgabewert ist der Carry/Borrow am Ausgang. Es scheint keine Entsprechungen für Operationen mit Vorzeichen oder Multiplikationen zu geben.
Ansonsten ist Clang für Windows jetzt produktionsreif (gut genug für Chrome), also könnte das auch eine Option sein.
31 Stimmen
Informationen, die zu diesem Thema nützlich sein können: Kapitel 5 von "Secure Coding in C and C++" von Seacord - http://www.informit.com/content/images/0321335724/samplechapter/seacord_ch05.pdf SafeInt-Klassen für C++ - http://blogs.msdn.com/david_leblanc/archive/2008/09/30/safeint-3-on-codeplex.aspx - http://www.codeplex.com/SafeInt IntSafe-Bibliothek für C: - [ blogs.msdn.com/michael_howard/archiv
3 Stimmen
Seacord's Secure Coding ist eine großartige Ressource, aber verwenden Sie nicht IntegerLib. Siehe blog.regehr.org/archives/593 .
45 Stimmen
Die gcc-Compileroption
-ftrapv
führt dazu, dass er bei einem (vorzeichenbehafteten) Integer-Überlauf einen SIGABRT erzeugt. Siehe aquí .3 Stimmen
Das beantwortet zwar nicht die Frage des Überlaufs, aber eine andere Möglichkeit, das Problem anzugehen, wäre die Verwendung einer BigNum-Bibliothek wie GMP um zu gewährleisten, dass Sie immer über genügend Präzision verfügen. Sie müssen sich keine Sorgen über einen Überlauf machen, wenn Sie im Voraus genügend Ziffern zuweisen.
1 Stimmen
Die von @HeadGeek in seiner Antwort gegebenen Informationen entsprechen ziemlich genau dem, was ich auch sagen würde. Allerdings mit einem Zusatz. Die Art und Weise, wie Sie die Überfliegung bei einer Multiplikation jetzt erkennen, ist wahrscheinlich die schnellste. Auf ARM, wie ich in HeadGeeks Antwort kommentiert habe, können Sie die
clz
Anweisung oder die__clz(unsigned)
Funktion, um den Rang der Zahl zu bestimmen (wo ihr höchstes Bit ist). Da ich nicht weiß, ob diese Funktion auf x86 oder x64 verfügbar ist, gehe ich davon aus, dass sie es nicht ist, und sage, dass die Ermittlung des höchstwertigen Bits im schlimmsten Falllog(sizeof(int)*8)
Anweisungen.0 Stimmen
Eine Möglichkeit zur statischen Erkennung von Integer-Überläufen (mit False Positives): usenix.org/conference/osdi12/technical-sessions/presentation/