【发布时间】:2011-10-14 22:26:06
【问题描述】:
不,这不是 How to detect integer overflow? 的副本。问题相同,但问题不同。
gcc 编译器可以优化掉溢出检查(使用 -O2),例如:
int a, b;
b = abs(a); // will overflow if a = 0x80000000
if (b < 0) printf("overflow"); // optimized away
gcc 的人认为这不是错误。根据 C 标准,溢出是未定义的行为,它允许编译器执行任何操作。显然,anything 包括假设永远不会发生溢出。不幸的是,这允许编译器优化溢出检查。
最近的CERT paper 中描述了检查溢出的安全方法。本文建议在添加两个整数之前执行以下操作:
if ( ((si1^si2) | (((si1^(~(si1^si2) & INT_MIN)) + si2)^si2)) >= 0) {
/* handle error condition */
} else {
sum = si1 + si2;
}
显然,当您想确保结果有效时,您必须在一系列计算中的每个 +、-、*、/ 和其他操作之前执行类似的操作。例如,如果您想确保数组索引没有超出范围。这太麻烦了,几乎没有人这样做。至少我从未见过有系统地做到这一点的 C/C++ 程序。
现在,这是一个基本问题:
在访问数组之前检查数组索引很有用,但不可靠。
使用 CERT 方法检查一系列计算中的每个操作是可靠的,但没有用处。
结论:在 C/C++ 中没有有用且可靠的溢出检查方法!
我不相信这是编写标准时的意图。
我知道有某些命令行选项可以解决问题,但这并不会改变我们对标准或对它的当前解释存在根本问题的事实。
现在我的问题是: 当 gcc 允许他们优化溢出检查时,他们是否对“未定义行为”的解释过分了,还是 C/C++ 标准被破坏了?
添加说明: 对不起,你可能误解了我的问题。我不是在问如何解决这个问题 - 已经回答了elsewhere。我在问一个关于 C 标准的更基本的问题。如果没有有用且可靠的方法来检查溢出,那么语言本身就是可疑的。例如,如果我创建一个带有边界检查的安全数组类,那么我应该是安全的,但如果边界检查可以被优化掉,我就不安全了。
如果标准允许这种情况发生,那么要么标准需要修订,要么标准的解释需要修订。
添加注释 2: 这里的人们似乎不愿意讨论“未定义行为”这个可疑的概念。 C99 标准列出了 191 种不同类型的未定义行为 (link),这表明标准草率。
许多程序员欣然接受“未定义行为”允许做任何事情的声明,包括格式化硬盘。我认为标准将整数溢出归入与写入数组边界外相同的危险类别是一个问题。
为什么这两种“未定义行为”不同?因为:
许多程序依赖整数溢出是良性的,但是当你不知道那里有什么时,很少有程序依赖于写入外部数组边界。
在数组边界外写入实际上可以做一些与格式化硬盘一样糟糕的事情(至少在像 DOS 这样不受保护的操作系统中),并且大多数程序员都知道这很危险。 p>
当您将整数溢出置于危险的“任何事情发生”类别时,它允许编译器做任何事情,包括谎报正在做的事情(在溢出检查被优化的情况下)
使用调试器可以发现写入超出数组边界之类的错误,但优化掉溢出检查的错误却不能,因为调试时通常会关闭优化。
gcc 编译器显然会避免在整数溢出的情况下使用“一切正常”的策略。在许多情况下,它会避免优化,例如一个循环,除非它可以验证溢出是不可能的。出于某种原因,gcc 的人已经认识到,如果他们在这里遵循“随便什么”的政策,我们将会有太多的错误,但是他们对优化掉溢出检查的问题有不同的态度。
也许这里不适合讨论这些哲学问题。至少,这里的大多数答案都是离题的。有没有更好的地方来讨论这个?
【问题讨论】:
-
你在这里混淆了两个根本不同的东西——数组溢出检查,除了调试之外很少有用,还有算术溢出检查,经常有用。
-
“我拒绝相信”——这是你的决定,但如果你拒绝相信真实的事情,那么你就会遇到困难。有符号整数类型在 C 或 C++ 中对于可能溢出的计算没有用处。这是该标准旨在避免对“自然”行为与您或我认为的行为不同的机器架构施加开销的结果。为避免实施负担,这种后果被认为是值得付出的。
-
具有数组边界的计算尤其适用于无符号算术,因为您知道,无论如何您都不应该得到负的最终结果。
-
“如果没有有用且可靠的方法来检查溢出,那么语言本身就是可疑的”——那么根据您的定义,该语言是可疑的。如果您发现该语言没有用,请不要使用它(或仅使用提供有关溢出的额外保证的实现和命令行选项)。您与 C 标准委员会分道扬镳的地方可能是您认为在操作之前检查溢出是无用的,而他们认为不是。可能你也认为无符号类型是无用的,而他们没有。
标签: c++ c overflow integer-overflow gcc4