未定义行为怪癖:在缓冲区外读取会导致循环永不终止? [英] Undefined Behavior quirk: reading outside a buffer causes a loop to never terminate?

查看:150
本文介绍了未定义行为怪癖:在缓冲区外读取会导致循环永不终止?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个非常简单的程序来尝试检查与缓冲区溢出相关的未定义行为。具体来说,关于在分配的空间之外执行数据读取时会发生什么情况。

I wrote a very trivial program to try to examine the undefined behavior attached to buffer overflows. Specifically, regarding what happens when you perform a read on data outside the allocated space.

#include <iostream>
#include<iomanip>

int main() {
    int values[10];
    for (int i = 0; i < 10; i++) {
        values[i] = i;
    }

    std::cout << values << " ";
    std::cout << std::endl;
    for (int i = 0; i < 11; i++) {
        //UB occurs here when values[i] is executed with i == 10
        std::cout << std::setw(2) << i << "(" << (values + i) << "): " << values[i] << std::endl;
    }
    system("pause");
    return 0;
}

当我在Visual Studio上运行此程序时,结果并不令人惊讶:读取索引 10 会产生垃圾:

When I run this program on Visual Studio, the results aren't terribly surprising: reading index 10 produces garbage:

000000000025FD70 
 0(000000000025FD70): 0
 1(000000000025FD74): 1
 2(000000000025FD78): 2
 3(000000000025FD7C): 3
 4(000000000025FD80): 4
 5(000000000025FD84): 5
 6(000000000025FD88): 6
 7(000000000025FD8C): 7
 8(000000000025FD90): 8
 9(000000000025FD94): 9
10(000000000025FD98): -1966502944
Press any key to continue . . . 

但是当我将此程序输入Ideone.com的在线编译器时,我的行为异常奇怪:

But when I fed this program into Ideone.com's online compiler, I got extremely bizarre behavior:

0xff8cac48 
0(0xff8cac48): 0
1(0xff8cac4c): 1
2(0xff8cac50): 2
3(0xff8cac54): 3
4(0xff8cac58): 4
5(0xff8cac5c): 5
6(0xff8cac60): 6
7(0xff8cac64): 7
8(0xff8cac68): 8
9(0xff8cac6c): 9
10(0xff8cac70): 1
11(0xff8cac74): -7557836
12(0xff8cac78): -7557984
13(0xff8cac7c): 1435443200
14(0xff8cac80): 0
15(0xff8cac84): 0
16(0xff8cac88): 0
17(0xff8cac8c): 1434052387
18(0xff8cac90): 134515248
19(0xff8cac94): 0
20(0xff8cac98): 0
21(0xff8cac9c): 1434052387
22(0xff8caca0): 1
23(0xff8caca4): -7557836
24(0xff8caca8): -7557828
25(0xff8cacac): 1432254426
26(0xff8cacb0): 1
27(0xff8cacb4): -7557836
28(0xff8cacb8): -7557932
29(0xff8cacbc): 134520132
30(0xff8cacc0): 134513420
31(0xff8cacc4): 1435443200
32(0xff8cacc8): 0
33(0xff8caccc): 0
34(0xff8cacd0): 0
35(0xff8cacd4): 346972086
36(0xff8cacd8): -29697309
37(0xff8cacdc): 0
38(0xff8cace0): 0
39(0xff8cace4): 0
40(0xff8cace8): 1
41(0xff8cacec): 134514984
42(0xff8cacf0): 0
43(0xff8cacf4): 1432277024
44(0xff8cacf8): 1434052153
45(0xff8cacfc): 1432326144
46(0xff8cad00): 1
47(0xff8cad04): 134514984
... 
//The heck?! This just ends with a Runtime Error after like 200 lines.

因此,显然,对于他们的编译器而言,将缓冲区溢出单个索引会使程序进入无限

So apparently, with their compiler, overrunning the buffer by a single index causes the program to enter an infinite loop!

现在,重申一下:我意识到我正在处理未定义的行为。但是尽管如此,我还是想知道幕后到底发生了什么导致这种情况。物理上执行缓冲区溢出的代码仍在执行4字节读取,并将读取的内容写入(可能受到更好保护的)缓冲区。导致这些问题的编译器/ CPU做什么?

Now, to reiterate: I realize that I'm dealing with undefined behavior here. But despite that, I'd like to know what on earth is happening behind the scenes to cause this. The code that physically performs the buffer overrun is still performing a read of 4 bytes and writing whatever it reads to a (presumably better protected) buffer. What is the compiler/CPU doing that causes these issues?

推荐答案

有两条执行路径会导致条件<$ c $我< 11 正在被评估。

There are two execution paths leading to the condition i < 11 being evaluated.

第一个是在初始循环迭代之前。由于在检查之前 i 已被初始化为 0 ,所以这确实是正确的。

The first is before the initial loop iteration. Since i had been initialised to 0 just before the check, this is trivially true.

第二个是在成功的循环迭代之后。由于循环迭代导致访问 values [i] ,并且 values 仅包含10个元素,因此只能如果 i< 10 。如果 i< 10 ,在 i ++ 之后, i< 11 也必须为真。

The second is after a successful loop iteration. Since the loop iteration caused values[i] to be accessed, and values only has 10 elements, this can only be valid if i < 10. And if i < 10, after i++, i < 11 must also be true.

这是Ideone的编译器(GCC)正在检测的内容。条件 i< 11 永远是假的,除非您有一个无效的程序,因此可以对其进行优化。同时,除非您提供其他选项告知编译器,否则编译器不会全力以赴检查您是否有无效的程序(例如 -fsanitize = undefined

This is what Ideone's compiler (GCC) is detecting. There is no way the condition i < 11 can ever be false unless you have an invalid program, therefore it can be optimised away. At the same time, your compiler doesn't go out of its way to check whether you might have an invalid program unless you provide additional options to tell it to do so (such as -fsanitize=undefined in GCC/clang).

这是实现必须权衡的结果。他们可能赞成无效程序的可理解行为,或者可能赞成有效程序的原始速度。或两者兼而有之。至少在默认情况下,GCC绝对将重点放在后者上。

This is a trade off implementations must make. They can favour understandable behaviour for invalid programs, or they can favour raw speed for valid programs. Or a mix of both. GCC definitely focuses greatly on the latter, at least by default.

这篇关于未定义行为怪癖:在缓冲区外读取会导致循环永不终止?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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