인텔 시피유 버그에 대하여 기술 분석

intel
meltdown

#1

취약점 1: 시피유 명령의 분기 예측 메커니즘을 악용할 수 있다.

이번 취약점은 기본적으로 투기 실행과 관련이 있다. 파이프 라인화된 실행 유닛을 가진 CPU는 “명령어 처리가 끝난 후” 다음 명령을 읽는 다면 실행 시간이 지연이 발생하게 된다. 따라서 기본적으로 명령어를 처리하는 동안 다른 다음 명령과 다음 다음 명령을 점점 미리 읽기(pre-fetch) 한다.

이것을 파이프라인을 이용한 패치, 또는 디코딩 처리라고 한다. 일반적으로 프로그램은 반복된 실행을 하는 부분이 있게되고 어딘가에서 분기가 들어가게 된다. 분기의 경우 “명령의 실행 결과에서 분기 할 주소가 결정된다.” 그래서 프로세서가 파이프 라인를 사용하지 않는 다면 문제가 되지 않는다. 하지만 실행 속도를 효과적으로 빠르게 만다는 파이프 라인을 사용하지 안는 시피유는 거의 없다고 보면 된다. 즉 파이프 라인화되어 있으면, 결과가 나올 때까지 아무것도 하지 않고 기다리는 것은 시간 낭비가 된다. 그래서 “분기 결과를 미리 예측하여두고 그 분기 결과에 따라 다음 명령을 실행하는” 과정으로 파이프 라인 이용을 효과적으로 할 수 있도록 실행 처리를 구현한다.

이를 위해서는 분기 결과를 정확하게 예측해야만 한다. 시피유 제조사는 각사마다 분기 예측 메커니즘에 주력하고 있으며, 분기 명령이 있다면, 그 직후의 명령에 관해서는 투기 실행이라는 방법을 주로 사용한다. 분기 예측을 통해서 여기의 명령이 실행될 것이다라고 예측하여 먼저 실행하는 것이다. 여기서 판단이 잘못되면 그때까지 투기 적으로 실행했던 결과를 버리고 새로운 명령을 다시 읽는 데서 시작된다. 취약점 1 (Variant 1)은 이 투기 실행을 악용한 것이다.

원래의 프로그램이 본래 허용된 범위를 초과하여 메모리 액세스하려고 한 것이라고한다. 물론 이것은 CPU의 검사기구에 걸리는 위해 도중에 멈추는 것 (결과적으로 실행되지 않습니다) 하지만 투기 실행 메커니즘 덕분에 "허용되는 메모리 범위를 초과했는지"확인까지는 투기 메모리 액세스를 수행하게된다. 이렇게 되면 “본래 허용되지 않는 메모리 주소에 데이터가 데이터 캐시에 로드되어 버리게 된다.” 게다가 이 때 "로드 되었는지 여부는? 읽기 속도를 측정하여 추정할 수 있다. 본래 액세스 메모리 영역이므로 페이지 오류가 발생했다면 당연히 캐시 액세스의 경우보다 늦어지기 때문이다.

취약점 2 : 분기 된 메모리 주소를 추정 할 수있다.

다음은 Variant 2이다. 방금 전 분기 예측한다는 말을했지만, 분기 예측시 사용되는 것이 BTB (Branch Target Buffer)이다. 이것은 자주 이용되는 분기 대해서는 그 때 분기 대상 주소를 저장하는 것이 더욱 신속한 분기 예측을 수행할 수 있도록 하자는 것이다. 문제는이 BTB는 하나 밖에 없다, 즉 OS의 내부에서 사용하는 분기 주소도 응용 프로그램에서 사용하는 분기 주소도 동일시되어 저장되어있다. BTB의 구조 자체는 공개되어 있지 않지만, Google의 Zero Project는 Haswell의 구조를 연구하고 Indirect Branch, 간접 분기의 경우에 동일한 물리적 CPU에 속하는 스레드가 다른 하나의 스레드에 영향을 미치는 하는 것을 발견했다. 이를 이용하여 다른 스레드에 할당 된 메모리 주소를 추정 할 수 있는 것으로 밝혀되었다.

Variant 1/2 대해서는 확실히 문제가 있지만, 엄격한 것은 아니다. 어디 까지나 다른 프로세스에 할당 된 메모리 주소를 추정 할 수 있다는 수준의 것 밖에 없기 때문이다.

취약점 그 3 : 잘못된 캐시 액세스가 가능하다.

이에 대해 Variant 3은 좀 더 심각하다. Variant 3은 Variant 1의 연장에있다. Meltdown에 관한 논문 은 다음과 같은 샘플 코드에서이를 설명하고있다.

1 ; rcx = kernel address
2 ; rbx = probe array
3 retry:
4 mov al, byte [rcx]           (rcx에 표시된 주소의 내용 1Byte를 al 레지스터에 저장)
5 shl rax, 0xc                 (rax 레지스터의 내용을 12bit 오른쪽 시프트)
6 jz retry                     (shl의 처리 결과가 0이되면 retry : 로 이동
7 mov rbx, qword [rbx + rax]   (rbx에 rbx + rax 주소의 내용을 포함)

지금 샘플 코드는 일반 사용자 메모리 주소에서 작동하고있다. 행 번호 4 “mov al, byte [rcx]”(rcx 레지스터에 표시된 주소의 데이터 1byte를 al 레지스터에 로드)를 실시한 경우 권한 위반 (커널 공간의 주소에 액세스 한)에서 예외 처리로 이동하게 된다. 단, 예외 처리에 날 전에 실제로 4-7의 처리가 CPU 내부에서 실행되어 그 결과 캐시는 rcx에서 나타난 어드레스에서 시작하는 데이터가 받아 들여진다는 것이다. 일단 캐시로 옮겨 버리면 후 캐시 내용을 다시 가져 다시 것으로, 본래 보호되어야 커널 주소가 그대로 읽힌다.

실제로 Meltdown 팀은 이 기술을 이용하여 암호를 읽는 데모를 공개했다. Variant 1/2에 비해 이 Variant 3의 영향은 매우 크다. 이것이 가능한 이유는 CPU 내부의 Page Table (가상 메모리의 주소와 물리 메모리 주소의 대응을 기록한 테이블)이 OS 커널과 사용자 프로세스 용으로 함께되어있는 것이 원인이다. 먼저 목록으로 돌아 오면 rcx 커널 주소를 지정하면 해당 주소를 Page Table로 변환 할 수 없는 경우 물리적 메모리의 주소를 모르기 때문에 캐시에 원래 캡처 수 없게 된다. 대책으로는 OS 커널 위한 PTE와 사용자 프로세스를 위한 Page Table을 분리하기만 하면 된다. 이 기술은 KPTI (Kernel Page-Table Isolation)라고 한다. 원래 Linux에서는 KASLR (Kernel Address Space Layout Randomization)라는 만일 커널에 액세스 되어도 영향을 적게 줄이는 기술이 2014 년경부터 구현되어 있으며,이를 더욱 개량 한 KAISER (Kernel Address Isolation to have Side -channels Efficiently Removed)는 PTE를 분리하는 대책을 세운 것이 2017 년에 준비되어 있었다. 이번 Meltdown에 대한 대책으로서이 KAISER를 기반으로 한 것이 KPTI 패치로 제공되고있다. 또한 Windows와 Android, macOS 등에 대해서도 마찬가지로 OS 커널과 사용자 프로세스 용으로 Page Table을 분리하면 대책 수있는 것은 이미 패치가 준비되었다. 이 KPTI 대책을하면 안전성은 확보 할 수 있는 원인으로 Variant 1/2에 대해서도 동시에 패치가 제공되는데, 예를 들어 AMD는 Variant 2는 인텔의 것과 BTB의 구조가 다른 데다 지금 공격당한 사례도 없기 때문에 거의 위험 0 하다고 하고있다.

순수하게 계산만하고있는 분은 대부분 성능이 저하가 크지 않지만 I / O를 많이 행하는 경우에는 성능 저하가 현저하게 나오게 된다. Postgresql에서의 성능 저하도 HDD를 만큼 돌리는 SQL 데이터베이스에서의 결과라는 것을 생각하면 확실히 의미 있는 성능 차이가 보고되었다. 지금이 영향을 받는 것은 인텔의 비순차실행을 지원하는, Out-of-Order 기계, 즉 펜티엄 이후의 전부 (Atom 대해서는 이전 것은 괜찮겠지만,비 순차 실행을 지원하는 Silvermont 이후에도 위험성이 있다.), ARM의 Cortex-A75가 현재 대상이 되었다. 다만 ARM의 경우 아키텍처 라이선스를 받아 공급 업체 독자적으로 구현 한 제품도 많기 때문에 향후 대상이 늘어날 가능성도 있다.또한 이번에는 나와 있지 않지만, IBM의 Power와 MIPS 또는 IBM / NXP의 PowerPC 등도 잠재적으로 이번 취약점을 가지고있을 가능성이 있는 것으로 각 벤더의 보고서를 기다려봐야 한다.