gcc会跳过对有符号整数溢出的检查吗?
Will gcc skip this check for signed integer overflow?
例如,给定以下代码:
int f(int n)
{
if (n < 0)
return 0;
n = n + 100;
if (n < 0)
return 0;
return n;
}
假设你传递的数字非常接近整数溢出(小于100),编译器会产生代码,会给你一个负返回?
Simon Tatham的《The Descent to C》节选如下:
" GNU C编译器(gcc)为这个函数生成代码,它可以返回一个负整数,如果你传入(例如)最大可表示的' int '值。因为编译器在第一个if语句之后知道n是正数,然后它假设没有发生整数溢出,并使用该假设得出结论,即加法后的n值必须仍然是正数,所以它完全删除第二个if语句并返回未检查的加法结果。"
这让我想知道是否同样的问题存在于c++编译器中,如果我应该小心,我的整数溢出检查不被跳过。
简短回答
编译器肯定会优化你的例子中的检查,我们不能说所有的情况下,但我们可以做一个测试对gcc 4.9
使用godbolt交互式编译器与以下代码(看到它现场):
int f(int n)
{
if (n < 0) return 0;
n = n + 100;
if (n < 0) return 0;
return n;
}
int f2(int n)
{
if (n < 0) return 0;
n = n + 100;
return n;
}
,我们看到它为两个版本生成相同的代码,这意味着它确实省略了第二个检查:
f(int):
leal 100(%rdi), %eax #, tmp88
testl %edi, %edi # n
movl $0, %edx #, tmp89
cmovs %edx, %eax # tmp88,, tmp89, D.2246
ret
f2(int):
leal 100(%rdi), %eax #, tmp88
testl %edi, %edi # n
movl $0, %edx #, tmp89
cmovs %edx, %eax # tmp88,, tmp89, D.2249
ret
长回答
当你的代码显示未定义行为或依赖于潜在的未定义行为(在这个例子中有符号整数溢出),那么是的,编译器可以做出假设并围绕它们进行优化。例如,它可以假设没有未定义的行为,从而根据该假设进行优化。最臭名昭著的例子可能是在Linux内核中删除空检查。代码如下:
struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;
... use s ..
使用的逻辑是,由于s
被解引用,它必须不是空指针,否则将是未定义的行为,因此它优化了if (!s)
检查。链接的文章说:
问题是第2行中对s的解引用允许编译器要推断s不为空(如果指针为空,则函数没有定义;编译器可以简单地忽略这种情况)。因此,第3行中的Null检查被默默地优化了,现在是内核如果攻击者可以找到调用的方法,则包含可利用的错误这段代码带有一个空指针。
这同样适用于C和c++,它们都有类似的关于未定义行为的语言。在这两种情况下,标准都告诉我们,未定义行为的结果是不可预测的,尽管两种语言中具体未定义的内容可能不同。c++标准草案对未定义行为的定义如下:
本国际标准未规定的行为
,并包括以下注释(强调我的):
在本国际标准中可能会出现未定义的行为省略行为的任何显式定义,或者当程序使用错误的结构或错误的数据。允许的未定义行为从完全忽视情况到不可预测. results表示在转换或程序执行期间的行为环境特征的文件化方式(有或没有)(发出诊断消息),终止翻译或执行(发出诊断消息)。很多错误的程序构造不会产生未定义的行为;他们是需要诊断
C11标准草案有类似的语言。
正确的带符号溢出检查
您的检查不是防止有符号整数溢出的正确方法,您需要在执行操作之前检查,如果执行操作会导致溢出,则不要执行操作。Cert有一个关于如何防止各种操作的有符号整数溢出的很好的参考。对于添加的情况,它建议如下:
#include <limits.h>
void f(signed int si_a, signed int si_b) {
signed int sum;
if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||
((si_b < 0) && (si_a < (INT_MIN - si_b)))) {
/* Handle error */
} else {
sum = si_a + si_b;
}
如果我们将这段代码插入godbolt中,我们可以看到检查被省略了,这是我们期望的行为。
- 优化正在杀死我在 clang 6 中的整数溢出检查
- 从双精度转换为整数的显式类型是否始终检查整数溢出?
- 将字符串转换为整数类型T,检查是否存在溢出
- 如何检查溢出将字符*[]转换为无符号短短
- MSVC C6029 警告:缓冲区可能溢出,使用未经检查的值.检查缓冲区大小时,警告不会消失
- 向下施放到UINT32_T时,如何正确检查溢出
- 检查Android NDK COD中的整数溢出
- 如何通过 LLVM IR 检查溢出
- 当我尝试将char*转换为C 中的双重溢出时,如何检查双重溢出
- C 迭代器操作未检查溢出
- 如何在功能中的理智检查过程中处理整数的溢出
- 为什么 C 和 C++ 没有内置方法来检查整数溢出?
- 检查 addc 功能的溢出
- 检查计算是否会导致溢出 c++
- 提升精神不检查 int 溢出
- SSE2 整数溢出检查
- 正在检查多个数字是否溢出
- 检查c++中的int溢出
- gcc会跳过对有符号整数溢出的检查吗?
- 如果必须检查溢出或有条件地执行操作,则std::原子冗余