2017-01-27 8 views
1

버퍼 오버 플로우에 첨부 된 정의되지 않은 동작을 조사하기 위해 매우 간단한 프로그램을 작성했습니다. 특히, 할당 된 공간 밖의 데이터에 대한 읽기를 수행 할 때 일어나는 일에 관한 것입니다.정의되지 않은 동작 quirk : 버퍼 외부에서 읽으면 루프가 종료되지 않습니까?

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

내가 비주얼 스튜디오에서이 프로그램을 실행 는, 결과는 정말 놀라운 일이 아니다 있습니다 : 인덱스 10를 읽는 생산 쓰레기 :

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의 온라인 컴파일러로이 프로그램을 공급하는 경우, 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. 

그래서 분명히, 자신의 컴파일러와 함께 하나의 인덱스 버퍼 오버런은 무한 루프를 입력 할 수있는 프로그램을 원인!

다시 말해서, 여기서는 정의되지 않은 동작을 처리하고 있다는 것을 알고 있습니다.. 그럼에도 불구하고, 나는이 현상을 일으키기 위해 무엇이 일어나고 있는지를 알고 싶다. 버퍼 오버런을 물리적으로 수행하는 코드는 여전히 4 바이트의 읽기를 수행하고 읽는 내용이 무엇이든간에 (아마도 더 우수한 보호를받는) 버퍼에 쓰고 있습니다. 이러한 문제를 일으키는 컴파일러/CPU는 무엇입니까?

+2

프로그램에 UB가 표시되면 프로그램의 동작에 대해 유용하게 추론 할 수 없습니다. –

+0

@NeilButterworth 만약 그 행동에 대해 * 적은 이유를 사용하고 싶다면? = D – Xirema

+2

노트 질문은 미래의 읽기에 실제로 유용해야합니다. 나는 이것에 어떤 유용성을보기 위해 고투한다. – NathanOliver

답변

4

i < 11이 평가되는 두 개의 실행 경로가 있습니다.

첫 번째는 초기 루프 반복 이전입니다. i이 수표 바로 앞에 0으로 초기화 되었기 때문에 이것은 사실입니다.

두 번째는 성공적인 루프 반복 이후입니다. 루프 반복으로 인해 values[i]이 액세스되고 values은 10 개의 요소 만 가지므로 i < 10 인 경우에만 유효합니다. i < 10 인 경우 i++ 이후에는 i < 11도 true 여야합니다.

이것은 Ideone의 컴파일러 (GCC)가 감지하는 것입니다. 유효하지 않은 프로그램이 없으면 조건 i < 11이 거짓 일 수있는 방법이 없으므로이를 최적화 할 수 있습니다. 동시에 컴파일러는 GCC/clang의 -fsanitize=undefined과 같은 추가 옵션을 제공하지 않는 한 유효하지 않은 프로그램이 있는지 여부를 검사하지 않습니다.

이것은 구현해야하는 구현입니다. 유효하지 않은 프로그램에 대해 이해할 수있는 행동을 선호하거나 유효한 프로그램에 대해 원시 속도를 선호 할 수 있습니다. 또는 둘 다 혼합합니다. GCC는 적어도 기본적으로 후자에 중점을 둡니다.

+1

나는이 특별한 경우 컴파일러가 경고 할 것이라고 생각한다. 적극적으로 최적화하는 대신 이 경우 수행해야하는 최적화는 분명한 프로그램을 생성합니다 (한 번에 10 + 1이 11보다 작다고 가정해야합니다). 물론 경고는 표준에 의해 강제되는 것은 아니지만 최적화도 마찬가지이며 경고는 사용자의 이익에보다 도움이됩니다. – SergeyA

+0

Godbolt를 사용하여 테스트를했는데 설명이 정확한 것처럼 보입니다. [확인 값이 11 일] (https://godbolt.org/g/FrCPVN) 일 때 컴파일러가 어셈블리를 완전히 제거합니다. 루프인데 비해 [10의 값으로] (https://godbolt.org/g/oQJR0q) 두 명령이 다시 추가됩니다. – Xirema

+0

@SergeyA에 동의합니다. 컴파일러는 프로그래머가 작성한 조건이 항상 true라는 것을 이해하기에 너무 똑똑합니다. 최적화하기 전에 가난한 프로그래머에게 "항상 사실 인 상태를 확인하고 싶습니까?"라고 경고해야합니다. –