12

-fsanitize=undefined単体テスト ライブラリである Catch を使用するプロジェクトに切り替えました。Catch からの 1 行は、このフラグによって未定義の動作を引き起こしていることが通知されました。私は孤立した例を作ることができました:

#include <iomanip>
#include <sstream>

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

以下でコンパイル:

clang++ -fsanitize=undefined main.cpp

これを実行すると、次の出力が表示されます。

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

これは、私と clang3.6.0の友人に起こり3.4-1ubuntu3ます。gccバージョンでは起こりません4.9.2

それで、ここに何がありますか?このコードは本当に悪いのでしょうか? それとも、clang の最後で怪しいことが起こっているのでしょうか?

4

1 に答える 1

12

これはlibstdc++ のバグで、タイトルが -fsanitize=undefinedcfe-devのメーリング リスト スレッドからのもので、共有ライブラリは次のように 述べています。

これは libstdc++ のバグです。Will がその土地にパッチを当てれば、サニタイザーのブラックリスト ファイルを使用して回避できますが、今のところ、手動​​でフィルター処理するのが最善の選択肢である可能性があります。

これを修正するパッチがあります。今後数日で、これを libstdc++ アップストリームにプッシュすることを検討します。[...]

コメントで dyp を指摘したように、これとは対照的にclang使用されているシステムを見ることは珍しくありません。これを libstdc++経由で明示的に使用して Coliru でテストすると、実際に問題を再現できます。libstdc++libc++-stdlib=libstdc++

次のlibstdc++バグ レポート: ios_base.h の operator~ によって計算された悪い列挙値は、この問題をカバーし、次のように述べています。

ios_base.h の列挙に対して定義されたオーバーロードされた operator~s は、次の形式を持ちます。

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

~ は、列挙型の値の範囲外の値を作成するため、Enum 型へのキャスト バックには未指定の値が含まれ ([expr.static.cast]p10 を参照)、実際には、列挙型の範囲外の Enum 値が生成されます。 Enum 型の表現可能な値であるため、動作は未定義です。

参考までに [expr.static.cast]p10は次のように述べています。

整数型または列挙型の値は、明示的に列挙型に変換できます。元の値が列挙値 (7.2) の範囲内にある場合、値は変更されません。それ以外の場合、結果の値は指定されていません (その範囲にない可能性があります)。浮動小数点型の値も列挙型に変換できます。結果の値は、元の値を列挙型 (4.9) の基になる型に変換し、続いて列挙型に変換するのと同じです。

hvd が言うように、これは正式には未指定の動作ですが、Richard は実際には未定義の動作になると指摘しています。

TC は、これがDR 1766: 列挙値の範囲外の値によって未指定から未定義の動作に変更されたことを指摘しています。

issue 1094 は、列挙型への変換後に列挙型の式の値が列挙型の値の範囲内にない可能性があることを明確にしました (5.2.9 [expr.static.cast] パラグラフ 10 を参照)。は単に未指定の値です。これはおそらく、未定義の動作が式を非定数にするという事実に照らして、未定義の動作を生成するように強化する必要があります。9.6 [class.bit] パラグラフ 4 も参照してください。

新しい文言は、N4431の規格草案に記載されています。

于 2015-05-08T11:37:12.923 に答える