Im Falle von Integerüberläufen Was ist das Ergebnis von (unsigned int) * (int)? unsigned oder int?

8

Was ist das Ergebnis von (unsigned int) * (int) im Fall von Integer-Überläufen? unsigned oder int ? Welchen Typ akzeptiert der Array-Indexoperator ( operator[] ) für char* : int , unsigned int oder etwas anderes?

Ich auditierte die folgende Funktion und plötzlich stellte sich diese Frage. Die Funktion hat eine Sicherheitslücke in Zeile 17.

%Vor%

Beachten Sie, dass sowohl w als auch h sehr große Ganzzahlen ohne Vorzeichen sind. Die Multiplikation in Zeile 9 hat eine Chance, die Validierung zu bestehen.

Jetzt ist das Problem in Zeile 17. Multiply int i mit unsigned int w : Wenn das Ergebnis int ist, ist es möglich, dass das Produkt negativ ist, was zu einer Position führt, die vor buf liegt. Wenn das Ergebnis unsigned int ist, ist das Produkt immer positiv, was den Zugriff auf eine Position nach buf ermöglicht.

Es ist schwer, Code zu schreiben, um dies zu rechtfertigen: int ist zu groß. Hat jemand Ideen dazu?

Gibt es eine Dokumentation, die den Typ des Produkts angibt? Ich habe danach gesucht, aber bis jetzt noch nichts gefunden.

Ich nehme an, dass, was die Sicherheitslücke betrifft, ob (unsigned int) * (int) unsigned int oder int erzeugt, spielt keine Rolle, weil sie in der kompilierten Objektdatei nur Bytes sind. Der folgende Code funktioniert unabhängig vom Typ des Produkts gleich:

%Vor%

Daher ist es egal, welchen Typ die Multiplikation zurückgibt. Es kommt darauf an, ob die Verbraucherfunktion int oder unsigned benötigt.

Die Frage hier ist nicht wie schlecht die Funktion ist oder wie man die Funktion verbessert, um sie zu verbessern. Die Funktion hat zweifellos eine Schwachstelle. Die Frage ist das genaue Verhalten der Funktion, basierend auf dem vorgeschriebenen Verhalten der Standards.

    
yinyueyouge 06.04.2009, 15:01
quelle

13 Antworten

2

Um Ihre Frage zu beantworten: Der Typ eines Ausdrucks, der ein int und ein unsigned int multipliziert, ist ein unsigned int in C / C ++.

Um Ihre implizite Frage zu beantworten, gibt es einen vernünftigen Weg, mit einem möglichen Überlauf bei der Ganzzahlarithmetik umzugehen, indem Sie den " IntSafe " Satz von Routinen von Microsoft verwenden:

Ссылка

Sie ist im SDK verfügbar und enthält Inline-Implementierungen, sodass Sie untersuchen können, was sie tun, wenn Sie sich auf einer anderen Plattform befinden.

    
Michael Burr 06.04.2009, 15:31
quelle
4

Führe die w * h-Berechnung lange aus, überprüfe ob größer als MAX_UINT

ist

EDIT: Alternative: wenn überflogen (w * h) / h! = w (ist das immer der Fall?! sollte das sein, oder?)

    
qwerty 06.04.2009 15:13
quelle
2

Stellen Sie sicher, dass w * h nicht überläuft, indem Sie w und h begrenzen.

    
starblue 06.04.2009 15:08
quelle
2

Der Typ von w*i ist in Ihrem Fall nicht signiert. Wenn ich den Standard richtig lese, lautet die Regel, dass die Operanden in den größeren Typ (mit seiner Signedness) konvertiert werden, oder vorzeichenloser Typ, der dem signierten Typ entspricht (in Ihrem Fall unsigned int ).

Aber selbst wenn es nicht vorzeichenbehaftet ist, verhindert es nicht die Umgehung (Schreiben in den Speicher vor buf ), da dies der Fall sein kann (auf der i386-Plattform), dass p[-1] dasselbe ist %Code%. Wie auch immer, in Ihrem Fall wären sowohl p[-1u] als auch buf[-1] ein undefiniertes Verhalten, daher ist die Frage mit der Signatur / unsigned nicht so wichtig.

Beachten Sie, dass in anderen Kontexten signierte / unsignierte Angelegenheiten - z. buf[big unsigned number] gibt unterschiedliche Ergebnisse, abhängig von den Typen von (int)(x*y/2) und x , auch wenn kein undefiniertes Verhalten vorliegt.

Ich würde Ihr Problem lösen, indem Sie in Zeile 9 nach einem Überlauf suchen. da 4096 eine ziemlich kleine Konstante ist und 4096 * 4096 auf den meisten Architekturen nicht überläuft (das muss man überprüfen), würde ich

machen %Vor%

Dies lässt den Fall aus, wenn y oder w 0 sind, sollten Sie bei Bedarf nachsehen.

Im Allgemeinen könnten Sie wie folgt nach einem Überlauf suchen:

%Vor%     
jpalecek 06.04.2009 15:38
quelle
2

In C / C ++ ist die Schreibweise p[n] wirklich eine Abkürzung, um *(p+n) zu schreiben, und diese Zeigerarithmetik berücksichtigt das Vorzeichen. So ist p[-1] gültig und bezieht sich auf den Wert unmittelbar vor *p .

Also ist das Zeichen hier wirklich wichtig, das Ergebnis des arithmetischen Operators mit Integer folgt einem Satz von Regeln, die vom Standard definiert werden, und dies wird Integer-Promotions genannt.

Sehen Sie sich diese Seite an: INT02-C. Verstehen Sie ganzzahlige Konvertierungsregeln

    
Ismael 06.04.2009 15:40
quelle
1

2 Änderungen machen es sicherer:

%Vor%

Beachten Sie auch, dass es keine schlechte Idee ist, in das Pufferende zu schreiben oder von ihm zu lesen. Die Frage ist also nicht, ob i w negativ werden kann, sondern ob 0 & lt; = i h + w & lt; = 4096 gilt.

Es kommt also nicht auf den Typ an, sondern auf das Ergebnis von h * i. Zum Beispiel macht es keinen Unterschied, ob dies (vorzeichenlos) 0x80000000 oder (int) 0x80000000 ist, das Programm wird trotzdem seg-fault.

    
Ingo 06.04.2009 15:30
quelle
1

Für C, siehe "Übliche arithmetische Umwandlungen" (C99: Abschnitt 6.3.1.8, ANSI C K und R A6.5) für Details, wie die Operanden der mathematischen Operatoren behandelt werden.

In Ihrem Beispiel gelten die folgenden Regeln:

C99:

  

Ansonsten, wenn der Typ des Operanden   mit vorzeichenbehafteten Integer-Typ kann darstellen   alle Werte des Typs der   Operand mit vorzeichenloser Ganzzahl,   dann der Operand mit vorzeichenloser Ganzzahl   Der Typ wird in den Typ des Typs konvertiert   Operand mit Ganzzahl mit Vorzeichen.

     

Ansonsten werden beide Operanden konvertiert   zum vorzeichenlosen Integer-Typ   entsprechend dem Typ der   Operand mit Ganzzahl mit Vorzeichen.

ANSI C:

  

Andernfalls wird, wenn einer der beiden Operanden unsigned int ist, der andere in unsigned int konvertiert.

    
Trent 06.04.2009 15:58
quelle
0

Warum deklariere ich nicht einfach i als unsigned int? Dann verschwindet das Problem.

In jedem Fall ist i * w garantiert & lt; = 4096, da der Code dies testet, so dass es niemals überläuft.

    
Steve Melnikoff 06.04.2009 15:07
quelle
0

memcpy (& amp; buf [i w & gt; -1? i w & lt; 4097? i w: 0: 0], init, w); Ich glaube nicht, dass die dreifache Berechnung von i w die Leistung verschlechtert

    
06.04.2009 15:11
quelle
0

w * h könnte überlaufen, wenn w und / oder h ausreichend groß sind und die folgende Validierung bestehen könnte.

%Vor%

Bei int, unsigned int gemischten Operationen wird int auf unsigned int gesetzt, wobei in diesem Fall ein negativer Wert von 'i' zu einem großen positiven Wert würde. In diesem Fall

%Vor%

würde auf einen nicht gebundenen Wert zugreifen.

    
Indy9000 06.04.2009 15:20
quelle
0

Vorzeichenlose Arithmetik wird als modular (oder wrap-around) ausgeführt, so dass das Produkt von zwei großen vorzeichenlosen Ints leicht kleiner als 4096 sein kann. Die Multiplikation von int und unsigned int führt zu einem vorzeichenlosen int (siehe Abschnitt 4.5 der C ++ - Standard).

Daher können Sie bei gegebenem großem w ​​und einem geeigneten Wert von h tatsächlich in Schwierigkeiten geraten.

Es ist schwierig sicherzustellen, dass Ganzzahlarithmetik nicht überläuft. Ein einfacher Weg besteht darin, zu Gleitkomma zu konvertieren und eine Gleitkomma-Multiplikation durchzuführen und zu sehen, ob das Ergebnis überhaupt in Ordnung ist. Wie von QWERTY vorgeschlagen, wäre long long verwendbar, wenn es auf Ihrer Implementierung verfügbar wäre. (Es ist eine gemeinsame Erweiterung in C90 und C ++, existiert in C99 und wird in C ++ 0x sein.)

    
David Thornley 06.04.2009 15:21
quelle
0

Es gibt 3 Absätze im aktuellen C1X-Entwurf zur Berechnung (UNSIGNED TYPE1) X (SIGNED TYPE2) in 6.3.1.8 Übliche arithmetische Umsetzungen, N1494,

AG 14: C - Projektstatus und Meilensteine ​​

  

Andernfalls, wenn der Operand, der einen Integer-Typ ohne Vorzeichen hat, den Rang größer oder hat   gleich dem Rang des Typs des anderen Operanden, dann der Operand mit   Der vorzeichenbehaftete ganzzahlige Typ wird in den Typ des Operanden konvertiert, der nicht vorzeichenbehaftet ist   Integer-Typ.

     

Andernfalls, wenn der Typ des Operanden mit vorzeichenbehafteten Integer-Typ darstellen kann   alle Werte des Typs des Operanden mit vorzeichenlosen Integer-Typ, dann   Der Operand mit vorzeichenloser Ganzzahl wird in den Typ der. konvertiert   Operand mit Ganzzahl mit Vorzeichen.

     

Andernfalls werden beide Operanden in den vorzeichenlosen Integer-Typ konvertiert   entspricht dem Typ des Operanden mit Vorzeichen vom Typ Integer.

Wenn also a nicht vorzeichenbehaftet ist und b int ist, sollte das Parsen von (a * b) Code erzeugen (a * (unsigned int) b). Wird überlaufen, wenn b & lt; 0 oder a * b & gt; UINT_MAX.

Wenn a unsigned int ist und b eine längere Größe hat, sollte (a * b) ((long) a * (long) b) erzeugen. Wird überlaufen, wenn a * b & gt; LONG_MAX oder a * b & lt; LONG_MIN.

Wenn a nicht vorzeichenbehaftet int ist und b lang ist und dieselbe Größe hat, sollte (a * b) erzeugen ((vorzeichenlos lang) a * (vorzeichenlos lang) b). Wird überlaufen, wenn b & lt; 0 oder a * b & gt; ULONG_MAX.

Bei Ihrer zweiten Frage zu dem Typ, der von "indexer" erwartet wird, erscheint die Antwort "integer type", die jeden (vorzeichenbehafteten) ganzzahligen Index zulässt.

  

6.5.2.1 Array-Subskribierung

     

Einschränkungen

     

1 Einer der Ausdrücke muss den Typ "Zeiger auf den vollständigen Objekttyp" haben, der andere   Der Ausdruck muss einen Integer-Typ haben, und das Ergebnis hat den Typ '' type ''.

     

Semantik

     

2 Ein postfix-Ausdruck gefolgt von einem Ausdruck in eckigen Klammern [] ist ein subskribierter Ausdruck   Bezeichnung eines Elements eines Array-Objekts. Die Definition des tiefgestellten Operators []   ist, dass E1 [E2] identisch zu (* ((E1) + (E2))) ist. Wegen der Konvertierungsregeln das   Wenden Sie sich an den binären Operator +, wenn E1 ein Array-Objekt ist (äquivalent, ein Zeiger auf die   Anfangselement eines Array-Objekts) und E2 ist eine ganze Zahl, E1 [E2] bezeichnet die E2-te   Element von E1 (von Null ausgehend).

Es liegt am Compiler, eine statische Analyse durchzuführen und den Entwickler vor der Möglichkeit eines Pufferüberlaufs zu warnen, wenn der Zeigerausdruck eine Arrayvariable ist und der Index negativ sein kann. Gleiches gilt für die Warnung über mögliche Überschreitungen der Array-Größe, selbst wenn der Index positiv oder unsigniert ist.

    
eel ghEEz 18.09.2010 01:11
quelle
-1

Um Ihre Frage tatsächlich zu beantworten, ohne die Hardware anzugeben, auf der Sie laufen, wissen Sie nicht, und in Code, der portabel sein soll, sollten Sie sich nicht auf ein bestimmtes Verhalten verlassen.

    
Charlie Martin 06.04.2009 15:19
quelle

Tags und Links