2015-05-08 9 views
12

Ho acceso -fsanitize=undefined sul mio progetto che utilizza Catch, la libreria di test delle unità. Una linea di Catch è stata segnalata come causa di un comportamento indefinito da questo flag. Sono riuscito a fare un esempio isolato:Questo codice è davvero indefinito, come sembra indicare Clang?

#include <iomanip> 
#include <sstream> 

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

compilato con:

clang++ -fsanitize=undefined main.cpp 

Se ho eseguito questo, il seguente stampa è data:

/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' 

Questo accade per me su clang 3.6.0 e per un amico con clang 3.4-1ubuntu3. Non succede per me su gcc versione 4.9.2

Allora, che succede qui? Questo codice è veramente cattivo, o c'è qualcosa di sospetto sulla fine del clang?

+1

Solo 'os << std :: hex;' sembra anche riprodurre il problema. – dyp

+0

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

+0

Mi piacerebbe un titolo più descrittivo, ma ho difficoltà a inventarne uno. –

risposta

12

Questo è un bug in libstdc++, dalla lista cfe-dev mailing filo con il titolo -fsanitize = librerie non definiti e condivisi dice:

Si tratta di un bug nel libstdC++. Sarai in grado di aggirare il problema con un file di lista nera sanitizzatore , una volta che la patch di Will per quella terra, ma per ora, filtrarli manualmente è probabilmente la scelta migliore.

Ecco una patch per risolverlo; Cercherò di spingerlo a libstdC++ a monte nei prossimi giorni. [...]

Come ho osservato a dyp nei commenti non è raro vedere i sistemi in cui clang utilizza libstdc++ al contrario di libc++ e se testiamo questo su Coliru explicitly using libstdc++ via -stdlib=libstdc++ abbiamo infatti possibile riprodurre il problema.

Il seguente rapporto libstdc++ bug: bad enum values computed by operator~ in ios_base.h copre questo problema e dice:

L'operatore di overload ~ s definito per le enumerazioni in ios_base.h avere la seguente forma:

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

Il ~ crea valori al di fuori dell'intervallo di valori del tipo di enumerazione , quindi il cast torna al tipo Enum ha un valore non specificato (vedi [expr.static.cast] p10), e in pratica si pr emette un valore Enum al di fuori dell'intervallo di valori rappresentabili per il tipo Enum, quindi il comportamento di non è definito.

Per riferimento [expr.static.cast] p10 dice:

Un valore di tipo integrale o censimento può essere esplicitamente convertito in un tipo di enumerazione. Il valore è invariato se il valore originale è compreso nell'intervallo dei valori di enumerazione (7.2). In caso contrario, il valore risultante non è specificato (e potrebbe non essere compreso in tale intervallo).Un valore del tipo a virgola mobile può anche essere convertito in un tipo di enumerazione. Il valore risultante equivale a convertire il valore originale nel tipo sottostante dell'enumerazione (4.9) e successivamente al tipo di enumerazione.

e come dice hvd questo comportamento è formalmente non specificato, ma Richard sottolinea che in pratica si finisce per essere un comportamento indefinito.

T.C. sottolinea questo è stato cambiato da non specificato al comportamento indefinito da DR 1766: Values outside the range of the values of an enumeration:

Sebbene problema 1094 chiarito che il valore di un'espressione di tipo censimento potrebbe non essere nell'intervallo di valori della enumerazione dopo una conversione per l'enumerazione type (vedi 5.2.9 [expr.static.cast] paragrafo 10), il risultato è semplicemente un valore non specificato. Questo dovrebbe probabilmente essere rafforzato per produrre un comportamento indefinito, alla luce del fatto che un comportamento indefinito rende un'espressione non costante. Vedere anche 9.6 [class.bit] paragrafo 4.

La nuova dicitura appare nella bozza di standard in N4431.

+0

Oh davvero interessante. Troppo male filtrarli manualmente rimuove la possibilità di usare gdb per infrangere tali errori per ottenere uno stacktrace per risolverli. – Tobias

+0

Il fatto che il cast produca un valore non specificato significa che il comportamento * è * definito. Se il comportamento fosse indefinito, lo standard direbbe che il comportamento non è definito. (Comunque, non è ancora un comportamento utile, quindi il codice dovrebbe essere ancora modificato.) – hvd

+0

@hvd bene ciò che Richard dice è "in pratica produce un valore Enum al di fuori dell'intervallo di valori rappresentabili per il tipo Enum, quindi il comportamento non è definito" quando il disinfettante lo vede, questa distinzione potrebbe non essere disponibile. –