这个代码真的未定义,像Clang似乎表明? [英] Is this code really undefined, as Clang seems to indicate?

查看:236
本文介绍了这个代码真的未定义,像Clang似乎表明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我使用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:运行时错误:值为4294967221的负载,该值无效值'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:运行时错误:加载值4294967221,它不是类型std :: _ Ios_Fmtflags的有效值

这发生在clang 3.6.0 上,以及clang的朋友 3.4-1ubuntu3 。它不会发生在我gcc版本 4.9.2



那么这里是什么?

解决方案

这是一个 libstdc ++中的错误,从 cfe- dev 具有标题 -fsanitize =未定义和共享库的邮件列表线程说:


这是libstdc ++中的一个错误。你将能够用一个
的消毒剂黑名单文件,一旦Will的补丁的土地,但现在,
现在,手动过滤它可能是你最好的选择。



这里有一个补丁来修复它;我会在接下来的几天里把这个推向
libstdc ++上游。 [...]


正如我在注释中提到的,在系统中, clang 使用 libstdc ++ 而不是 libc ++ ,如果我们在 Coliru明确使用libstdc ++ 通过 -stdlib = libstdc ++ 我们



以下 libstdc ++ 错误报告:运算符在ios_base.h中计算的错误枚举值覆盖此问题,并说:


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

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

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


参考 [expr.static.cast] p10


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


和hvd说这是正式未指定的行为,但Richard指出,在实践中最终是未定义的行为。



TC通过 DR 1766指出此内容已从未指定的行为更改为未定义的行为:枚举值范围之外的值


虽然问题1094澄清了枚举表达式的值类型可能不在转换为枚举类型之后的枚举值的范围内(参见5.2.9 [expr.static.cast]第10段),结果只是一个未指定的值。考虑到未定义的行为使表达式不恒定的事实,这应该可能被加强以产生未定义的行为。另请参见9.6 [class.bit]第4段。 //github.com/cplusplus/draft/blob/master/papers/n4431.pdfrel =nofollow> N4431 。


I switched on -fsanitize=undefined on my project which uses Catch, the unit testing library. One line from Catch was signalled as causing undefined behaviour by this flag. I managed to make an isolated example:

#include <iomanip>
#include <sstream>

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

Compiled with:

clang++ -fsanitize=undefined main.cpp

If I run this, the following print is given:

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

This happens for me on clang 3.6.0 and for a friend with clang 3.4-1ubuntu3. It does not happen for me on gcc version 4.9.2

So what is up here? Is this code actually bad, or is there something fishy going on on clang's end?

解决方案

This is a bug in libstdc++, from the cfe-dev mailing list thread with title -fsanitize=undefined and shared libraries says:

This is a bug in libstdc++. You will be able to work around it with a sanitizer blacklist file, once Will's patch for that lands, but for now, filtering them out manually is likely to be your best option.

Here's a patch to fix it; I'll be looking into pushing this to libstdc++ upstream in the next few days. [...]

As I noted to dyp in the comments it is not uncommon to see systems where clang uses libstdc++ as opposed to libc++ and if we test this on Coliru explicitly using libstdc++ via -stdlib=libstdc++ we indeed can reproduce the issue.

The following libstdc++ bug report: bad enum values computed by operator~ in ios_base.h covers this issue and says:

The overloaded operator~s defined for the enumerations in ios_base.h have the following form:

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

The ~ creates values outside the range of values of the enumeration type, so the cast back to the Enum type has an unspecified value (see [expr.static.cast]p10), and in practice it produces an Enum value outside the range of representable values for the Enum type, so behavior is undefined.

For reference [expr.static.cast]p10 says:

A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting value is unspecified (and might not be in that range). A value of floating-point type can also be converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration (4.9), and subsequently to the enumeration type.

and as hvd says this is formally unspecified behavior but Richard points out that in practice is ends up being undefined behavior.

T.C. points out this was changed from unspecified to undefined behavior by DR 1766: Values outside the range of the values of an enumeration:

Although issue 1094 clarified that the value of an expression of enumeration type might not be within the range of the values of the enumeration after a conversion to the enumeration type (see 5.2.9 [expr.static.cast] paragraph 10), the result is simply an unspecified value. This should probably be strengthened to produce undefined behavior, in light of the fact that undefined behavior makes an expression non-constant. See also 9.6 [class.bit] paragraph 4.

The new wording appears in the draft standard in N4431.

这篇关于这个代码真的未定义,像Clang似乎表明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆