正如Clang似乎指出的那样,这段代码是否真的未定义?

时间:2015-05-08 11:16:48

标签: c++ clang undefined-behavior libstdc++ sanitizer

我在使用Catch(单元测试库)的项目上打开了-fsanitize=undefined。来自Catch的一行被发信号通知此标志导致未定义的行为。我设法做了一个孤立的例子:

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

对于clang 3.6.0以及有clang 3.4-1ubuntu3的朋友,我会发生这种情况。在gcc版本4.9.2

上我不会发生这种情况

那么这里有什么?这个代码真的很糟糕,还是clang的结尾有什么可疑的东西?

1 个答案:

答案 0 :(得分:12)

这是bug in libstdc++,来自cfe-dev邮件列表主题,标题为 -fsanitize = undefined,共享库表示:

  

这是libstdc ++中的一个错误。你可以用它来解决它   洗手液黑名单文件,一旦威尔的补丁登陆,但为   现在,手动过滤它们可能是您的最佳选择。

     

这是修补它的补丁;我会考虑推动这个   libstdc ++将在未来几天内上游。 [...]

正如我在评论中提到的那样,看到clang使用libstdc++而不是libc++的系统并且如果我们在Coliru explicitly using libstdc++上通过bad enum values computed by operator~ in ios_base.h测试这种情况的情况并不少见-stdlib=libstdc++我们确实可以重现这个问题。

以下libstdc++错误报告:DR 1766: Values outside the range of the values of an enumeration涵盖了此问题并说:

  

为ios_base.h中的枚举定义的重载运算符   有以下形式:

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

〜创建枚举值范围之外的值   类型,因此返回到枚举类型的具有未指定的值(请参阅   [expr.static.cast] p10),实际上它产生一个Enum值   在Enum类型的可表示值范围之外,所以   行为未定义。

供参考 [expr.static.cast] p10 说:

  

可以将整数或枚举类型的值显式转换为枚举类型。价值是   如果原始值在枚举值(7.2)的范围内,则不变。否则,结果   值未指定(可能不在该范围内)。也可以转换浮点类型的值   到枚举类型。结果值与将原始值转换为基础值相同   枚举的类型(4.9),然后是枚举类型。

正如hvd所说,这是正式未指明的行为,但理查德指出,在实践中最终会成为未定义的行为。

T.C。指出N4431已将此指标从未指定行为更改为未定义行为:

  

虽然问题1094澄清了枚举类型的表达式的值可能不在转换为枚举类型后的枚举值的范围内(参见5.2.9 [expr.static.cast]第10段) ,结果只是一个未指定的值。鉴于未定义的行为使表达式非常量,这应该可以加强以产生未定义的行为。另见9.6 [class.bit]第4段。

新措辞出现在{{3}}标准草案中。