标签:c undefined-behavior gcc compiler-construction 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”中关于这个问题的摘录:
“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编译器中是否存在相同的问题,并且我是否应该小心我的整数溢出检查不会被跳过.
解决方法:
简答
编译器是否肯定会优化你的示例中的检查,我们不能说所有情况,但我们可以使用godbolt interactive compiler使用以下代码(see it live)对gcc 4.9进行测试:
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
答案很长
当您的代码显示undefined behavior或依赖于潜在的未定义行为(在此示例中为有符号整数溢出)然后是,编译器可以进行假设并围绕它们进行优化.例如,它可以假设没有未定义的行为,因此根据该假设进行优化.最臭名昭着的例子可能是removal of a null check in the Linux kernel.代码如下:
struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;
... use s ..
使用的逻辑是,由于s被解除引用,它不能是空指针,否则将是未定义的行为,因此它优化了if(!s)检查.链接的文章说:
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,它们都具有围绕未定义行为的类似语言.在这两种情况下,标准都告诉我们未定义行为的结果是不可预测的,尽管两种语言中具体未定义的结果可能不同. draft C++ standard定义了未定义的行为,如下所示:
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标准草案有类似的语言.
正确的签名溢出检查
您的检查不是防止有符号整数溢出的正确方法,您需要在执行操作之前进行检查,如果导致溢出则不执行操作. Cert有一个关于如何防止各种操作的有符号整数溢出的good reference.对于附加案例,它建议如下:
#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,我们可以看到检查被省略了,这是我们期望的行为.
标签:c,undefined-behavior,gcc,compiler-construction,integer-overflow 来源: https://codeday.me/bug/20190725/1530083.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。