12

我打开了-fsanitize=undefined使用单元测试库 Catch 的项目。来自 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'

这发生在我和 clang3.6.0的朋友身上3.4-1ubuntu3。我在 gcc 版本上不会发生这种情况4.9.2

那么这里是什么?这段代码真的很糟糕,还是在 clang 的结尾有什么可疑的地方?

4

1 回答 1

12

这是libstdc++ 中的一个错误,来自标题为-fsanitize=undefined 和共享库cfe-dev的邮件列表线程 说:

这是 libstdc++ 中的一个错误。一旦威尔的补丁用于该土地,您将能够使用消毒剂黑名单文件解决它,但目前,手动过滤它们可能是您的最佳选择。

这是一个修复它的补丁;在接下来的几天里,我将考虑将其推送到上游的 libstdc++。[...]

正如我在评论中提到的 dyp 所指出的,看到clang使用的系统并不罕见,如果我们在 Coliru 上明确地使用 libstdc++ 进行测试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 回答