gcc是否会跳过此检查以获取有符号整数溢出? [英] Will gcc skip this check for signed integer overflow?

查看:252
本文介绍了gcc是否会跳过此检查以获取有符号整数溢出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如,给定以下代码:

int f(int n)
{
    if (n < 0)
        return 0;
    n = n + 100;
    if (n < 0)
        return 0;
    return n;
}

假设您传入的数字非常接近整数溢出(小于$ 100 $),编译器会生成会给您带来负收益的代码吗?

Assuming you pass in a number that is very close to integer overflow (less than 100 away), will the compiler produce code that would give you a negative return?

以下是此问题的摘录,作者是西蒙·塔瑟姆(Simon Tatham):

Here is an excerpt about this issue from "The Descent to C" by Simon Tatham:


GNU C编译器(gcc)为此函数生成代码,如果您传入,则该代码可以返回负整数。最大值表示有能力的 int值,因为编译器在第一个if语句之后就知道n为正,然后假定未发生整数溢出,并使用该假设得出结论,加法后n的值仍必须为正数,因此它完全删除了第二个if语句并返回未选中的加法结果。

"The GNU C compiler (gcc) generates code for this function which can return a negative integer, if you pass in (for example) the maximum represent able ‘int’ value. Because the compiler knows after the first if statement that n is positive, and then it assumes that integer overflow does not occur and uses that assumption to conclude that the value of n after the addition must still be positive, so it completely removes the second if statement and returns the result of the addition unchecked."

我想知道是否相同C ++编译器中存在问题,如果我应该注意不要进行整数溢出检查

It got me wondering if the same issue existed in C++ compilers, and if I should be careful that my integer overflow checks aren't skipped.

推荐答案

简短答案

编译器肯定会优化您的示例中的检查,虽然我们无法在所有情况下都说完,但是可以使用gcc 4.9 进行测试http://gcc.godbolt.org/ rel = nofollow> godbolt交互式编译器,其代码如下( 实时查看 ):

Will a compiler definitely optimize away the check in your example, we can't say for all cases but we can do a test against gcc 4.9 using the godbolt interactive compiler with the following code (see it live):

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;
}

,我们看到它为两个版本生成相同的代码,这意味着确实取消了第二张支票:

and we see that it generates identical code for both versions, which means it is indeed eliding the second check:

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内核中删除空检查。代码如下:

When your code exhibits undefined behavior or relies on potential undefined behavior(in this example signed integer overflow) then yes, the compiler can make assumptions and optimize around them. For example it can assume there is no undefined behavior and thus optimize according to that assumption. The most infamous example is probably the removal of a null check in the Linux kernel. The code was as follows:

struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;
... use s ..

使用的逻辑是因为 s 被取消引用,不能为空指针,否则将是未定义的行为,因此它优化了 if(!s)检查。链接的文章说:

The logic used was that since s was dereferenced it must not be a null pointer otherwise that would be undefined behavior and therefore it optimized away the if (!s) check. The linked article says:


问题是第2行中对s的取消引用允许编译器
推断s为不为空(如果指针为空,则函数
未定义;编译器可以简单地忽略这种情况)。因此,第3行中的
空检查得到了无声的优化,现在,如果攻击者可以找到一种方法来使用空指针调用
的代码,则内核
包含一个可利用的漏洞。

The problem is that the dereference of s in line 2 permits a compiler to infer that s is not null (if the pointer is null then the function is undefined; the compiler can simply ignore this case). Thus, the null check in line 3 gets silently optimized away and now the kernel contains an exploitable bug if an attacker can find a way to invoke this code with a null pointer.

这同样适用于C和C ++,它们在不确定行为方面都有相似的语言。在这两种情况下,该标准都告诉我们,不确定行为的结果是无法预测的,尽管两种语言中具体未定义的内容可能有所不同。 草稿C ++标准定义了未定义的行为如下:

This applies equally to both C and C++ which both have similar language around undefined behavior. In both cases the standard tells us that the results of undefined behavior are unpredictable, although what is specifically undefined in either language may differ. The draft C++ standard defines undefined behavior as follows:


本国际标准对此没有要求的行为

behavior for which this International Standard imposes no requirements

并包含以下注释(强调我的):


未定义的行为可能是当本国际标准
忽略行为的任何明确定义或程序使用
错误的构造或错误数据时,可以预期到此错误。 允许的不确定行为
范围从完全忽略具有无法预测的
结果的情况
,到在翻译或程序执行过程中表现为环境特征的
行为(带有或在没有
的情况下发出诊断消息),终止翻译或
执行(在发出诊断消息的情况下)。许多错误的
程序构造不会引起未定义的行为。它们是需要被诊断的

Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed.

C11标准草案的语言类似。

The draft C11 standard has similar language.

正确的有符号溢出检查

您的检查不是防止有符号整数溢出的正确方法,您需要在执行操作之前进行检查,如果可能导致溢出,请不要执行操作。证书具有很好的参考资料,介绍了如何防止各种操作的带符号整数溢出。对于加法情况,建议采取以下措施:

Your check is not the proper way to guard against signed integer overflow, you need to check before you execute the operation and not execute the operation if it would cause overflow. Cert has a good reference on how to prevent signed integer overflow for the various operations. For the addition case it recommends the following:

#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中,我们可以看到检查被取消了是我们期望的行为吗?

If we plug this code into godbolt we can see that the checks are elided which is he behavior we would expect.

这篇关于gcc是否会跳过此检查以获取有符号整数溢出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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