Speculation Execution, Meltdown, Spectre에 대하여

2021. 4. 17. 13:35Layer7/Hardware

728x90

- Speculation Execution이란?

 

Speculation Execution은 프로그램의 논리 흐름상

실행 여부가 불확실한 상태에서, 예측 실행을 하는 기법이다. 

 

이렇게만 말하면 이해가 잘 되지 않을 것이다.

그러므로 아래의 예시 if문 코드를 보며 설명을 적어보겠다.

 

#include<cstdio>
#include<iostream>
using namespace std;

int main(){
    int a,b=1;
    cin>>a;
    if(a>15){
    	b=5;
    }
    cout<<b;
    return 0;
}

 

int형 변수 a,b를 선언하고 a를 입력받아 if 조건문에 넣어 확인한 뒤 b를 출력하는 간단한 코드다.

여기서 if문을 실행 할 때 Speculation Execution을 활용한다고 해보자.

a>15를 실행이 시간이 오래 걸리는 작업이라고 가정을 하고

Speculation Execution을 활용할 때의 처리 순서는 아래와 같다.

 

  1. b=5;
  2. a>15 (true는 3번으로, false는 4번으로 이동)
  3. 완료
  4. b=5 실행값 폐기

이렇게 실행을 했을 때의 장점은 시간 단축이다.

위의 과정에서 2단계가 true값으로 나왔으면 굉장한 시간 단축이 될 것이고

false가 나오면 b=5의 실행값을 폐기하여 없던일로 처리한다.

 

Speculation Execution의 좋은점은 예측이 틀렸을 때도

프로그램의 실행 속도와 결과에 영향을 미치지 않는다는 것이다.

 

 

- Meltdown_Rogue Data Cache Load

 

Out of Order Execution을 이용하여 원래는 읽을 권한이 없는 메모리를

읽어내는 취약점 공격 방법이다. *root 권한이 없어도 *커널 메모리 영역까지 읽을 수 있다.

 

* root(루트)는 UNIX에 존재하는 특권 사용자의 이름을 말한다.

* 운영체제의 핵심. 시스템의 모든 것을 통제하는 곳으로, 사용자가 접근하지 못한다.

  접근하려면 커널 모드로 전환을 해야한다.

 

해커가 커널 메모리 영역을 읽는 방법의 순서는 아래와 같다.

 

  1. 커널 메모리 영역에 대한 읽기를 실행한다. (여기서 Exception. 즉, 권한 위배 발생)
    • Exception이 실행되기 전에 Out of Order Execution에 의해서 커널 메모리 영역 읽기가 실행된다.
  2. 알고리즘에서의 약점을 찾아내고 그에 따라 계산된 메모리 인덱스를 수정하여 읽는다.
  3. *캐시 상태가 변경된다.
  4. 변경된 *캐시 상태를 확인하면서 원하는 커널 영역의 값을 알아낸다.

* 데이터나 값을 미리 복사해 놓는 임시 장소

 

 

- Spectre_Bound Check Bypass

Speculation Execution을 이용하여 *array의 값을 벗어난 값을

읽어내는 취약점 공격 방법이다.

 

* 배열

 

이를 설명하기 위해서 if문을 활용한 예시 코드를 적어보겠다.

 

if(x<array1_size){
    y=array2[array1[x]*4096];
}

 

 

아까 말했듯이 Speculation Execution 방법에서는

if에 있는 조건식이 아닌, 실행문이 먼저 실행된다.

그리고 if문의 값이 false로 나오면 아래의 실행문의 값을 버리게 되는데

여기서 주목해야 할 점이 있다.

 

실행문의 '값'을 버리는거지 저장된 '캐시'를 버리는 것이 아니다.

 

이 개념을 이용하여 Bound Check Bypass의 실행 순서를 정리해보면 아래와 같다.

 

  1. y=array2[array1[x]*4096]; 이 Spenculation Execution 방법에 의해 먼저 실행된다.
  2. array1[x]*4096으로 접근이 허가되지 않은 인덱스 범위를 만들어낸다.
  3. array2의 인덱스 값에 위에서 만들어진 값을 집어넣는다. 
  4. 3번 과정에서의 값이 *캐싱된다.
  5. array2의 모든 배열에 접근해보면서 시간이 얼마나 걸리는지 확인한다.
    • 여기서 시간이 가장 적게 걸린 인덱스가 *캐싱되었던 인덱스다.
  6. array1[x]의 값을 확인한다.

속도가 느린 디스크의 데이터를 속도가 빠른 메모리로 가져와서 메모리상에서 읽고 쓰는 작업

 

728x90