2015-05-08 14 views
12

Ich eingeschaltet -fsanitize=undefined auf meinem Projekt, die Catch, die Unit-Test-Bibliothek verwendet. Eine Zeile von Catch wurde durch dieses Flag als nicht definiertes Verhalten gemeldet. Ich schaffte es ein isoliertes Beispiel zu machen:Ist dieser Code wirklich undefiniert, wie Clang angibt?

#include <iomanip> 
#include <sstream> 

int main() 
{ 
    std::ostringstream os; 
    os << "0x" << std::setfill('0') << std::hex; 
} 

Zusammengestellt mit:

clang++ -fsanitize=undefined main.cpp 

Wenn ich dies ausführen, wird der folgende Druck gegeben:

/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:96:24: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags' 
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:76:67: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags' 

Dies geschieht für mich auf Klirren 3.6.0 und für einen Freund mit Klang 3.4-1ubuntu3. Es passiert nicht für mich auf GCC-Version 4.9.2

Also was ist hier oben? Ist dieser Code wirklich schlecht, oder läuft am Ende des Klangs etwas Fauliges?

+1

Nur 'os << std :: hex;' scheint auch das Problem zu reproduzieren. – dyp

+0

http://stackoverflow.com/questions/20617788/using-memory-sanitizer-with-libstdc Vielleicht das? –

+0

Ich würde einen beschreibenden Titel lieben, aber es fällt mir schwer, einen zu finden. –

Antwort

12

Dies ist ein bug in libstdc++, von dem cfe-dev Mailingliste Thread mit dem Titel -fsanitize = nicht definiert und gemeinsam genutzte Bibliotheken sagt:

Dies ist ein Fehler in libstdC++ ist. Sie werden in der Lage sein, mit einerSanitizer Blacklist-Datei zu umgehen, sobald Wills Patch für diese landet, aber für jetzt, ist es wahrscheinlich Ihre beste Option, sie manuell ausfiltern.

Hier ist ein Patch, um es zu beheben; Ich werde in den nächsten Tagen darüber nachdenken, dies auf libstdC++ zu schieben. [...]

Wie ich festgestellt, in den Kommentaren DYP es nicht ungewöhnlich ist, Systeme zu sehen, wo clang verwendet libstdc++ zu libc++ Gegensatz und wenn wir dies testen auf Coliru explicitly using libstdc++ über -stdlib=libstdc++ in die Tat können wir das Problem reproduzieren.

Der folgende libstdc++ Bugreport: bad enum values computed by operator~ in ios_base.h deckt dieses Problem und sagt:

Der überladene Operator ~ für die Aufzählungen in ios_base.h definiert s haben die folgende Form:

Enum operator~(Enum e) { return Enum(~static_cast<int>(e)); } 

Die ~ erstellt Werte außerhalb des Wertebereichs des Aufzählungstyps , so dass die Umwandlung in den Enum-Typ einen nicht angegebenen Wert hat (siehe [expr.static.cast] p10), und in der Praxis wird pr Stellt einen Enum-Wert außerhalb des Bereichs der darstellbaren Werte für den Enum-Typ her, sodass das Verhalten undefiniert ist.

Als Referenz [expr.static.cast] p10 sagt:

Ein Wert von Integral oder Aufzählungstyp kann explizit auf einen Aufzählungstyp umgewandelt werden. Der Wert ist unverändert, wenn der ursprüngliche Wert im Bereich der Aufzählungswerte (7.2) liegt. Andernfalls ist der resultierende Wert nicht angegeben (und möglicherweise nicht in diesem Bereich).Ein Wert des Fließkommatyps kann auch in einen Aufzählungstyp konvertiert werden: . Der resultierende Wert entspricht dem Konvertieren des ursprünglichen Werts in den zugrunde liegenden -Typ der Enumeration (4.9) und anschließend auf den Aufzählungstyp.

und als hvd sagt dieses Verhalten formell nicht spezifiziert ist, aber Richard weist darauf hin, dass in der Praxis endet als nicht definiertes Verhalten auf.

T.C. Dies weist darauf hin, wurde aus nicht näher bezeichnet zu undefiniertem Verhalten von DR 1766: Values outside the range of the values of an enumeration geändert:

Obwohl Ausgabe 1094 klargestellt, dass der Wert eines Ausdruck von Aufzählungstyp nicht im Bereich der Werte der Aufzählung nach einer Umwandlung in die Aufzählung sein könnte type (siehe 5.2.9 [expr.static.cast] Absatz 10), ist das Ergebnis einfach ein unspezifizierter Wert. Dies sollte wahrscheinlich verstärkt werden, um undefiniertes Verhalten zu erzeugen, angesichts der Tatsache, dass undefiniertes Verhalten einen Ausdruck nicht konstant macht. Siehe auch 9.6 [class.bit] Absatz 4.

Die neue Formulierung erscheint im Norm-Entwurf in N4431.

+0

Oh wirklich interessant. Zu schade, sie manuell auszufiltern, entfernt die Möglichkeit, gdb zu verwenden, um solche Fehler zu umgehen, um einen Stack-Trace zum Reparieren zu erhalten. – Tobias

+0

Die Tatsache, dass die Besetzung einen unspezifizierten Wert erzeugt, bedeutet, dass das Verhalten * definiert * ist. Wenn das Verhalten nicht definiert wäre, würde der Standard sagen, dass das Verhalten nicht definiert ist. (Es ist jedoch immer noch kein nützliches Verhalten, daher sollte der Code immer noch geändert werden.) – hvd

+0

@hvd na ja, was Richard sagt, ist 'in der Praxis produziert es einen Enum-Wert außerhalb des Bereichs der darstellbaren Werte für den Enum-Typ, so Verhalten ist undefiniert zu dem Zeitpunkt, zu dem der Sanitizer es sieht, ist diese Unterscheidung möglicherweise nicht verfügbar. –