본문 바로가기

ANYTHING

왜 RASPBERRY PI가 SPECTRE 나 MELTDOWN에 취약하지 않은지

이 문서는 영문으로된 내용을 구글 번역기를 활용하여 번역한 내용입니다. 
원문과 내용이 다를시 책임지지 않으며, 저작권 문제가 발생시 언제든 삭제 될 수 있습니다. 

원문보기 : https://www.raspberrypi.org/blog/why-raspberry-pi-isnt-vulnerable-to-spectre-or-meltdown/


지난 며칠 동안 Spectre와 Meltdown 이라는 별명의 보안 취약점에 대해 많은 논의가 있었습니다 . 이는 모든 최신 Intel 프로세서 및 Spectre의 경우 많은 AMD 프로세서 및 ARM 코어에 영향을 미칩니다 . Spectre를 사용하면 공격자가 현재 주소 공간의 임의 위치에서 데이터를 읽도록 소프트웨어 검사를 우회 할 수 있습니다. 붕괴로 인해 공격자는 운영 체제 커널의 주소 공간 (일반적으로 사용자 프로그램에서 액세스 할 수 없어야 함)의 임의 위치에서 데이터를 읽을 수 있습니다.

두 가지 취약점은 많은 현대 프로세서에 공통적인 성능 기능( 캐싱추측 실행 )을 이용하여 소위 사이드 채널 공격을 통해 데이터를 누출시킵니다. 행복하게도, 우리가 사용하는 특정 ARM 코어 때문에 Raspberry Pi는 이러한 취약점에 취약하지 않습니다.

이유를 이해하는 데 도움이되도록 최신 프로세서 설계의 일부 개념에 대한 약간의 입문서를 작성했습니다. 다음과 같은 간단한 구문을 사용하여 이러한 개념을 설명합니다.

t = a + b
u = c + d
v = e + f
w = v + g
x = h + i
y = j + k

컴퓨터의 프로세서가 Python을 직접 실행하지는 않지만 여기의 명령문은 단일 기계 명령어와 대략 일치 할만큼 간단합니다. 우리는 프로세서 설계자에게 매우 중요한 몇 가지 세부 사항 (특히 파이프 라이닝레지스터 이름 변경 )에 대해 설명 하겠지만 Spectre 및 Meltdown의 작동 방식을 이해하는 데 반드시 필요한 것은 아닙니다.

프로세서 디자인과 현대 컴퓨터 아키텍처의 다른 측면에 대한 포괄적 인 설명을 보려면 Hennessy와 Patterson의 고전적인 컴퓨터 아키텍처 : 정량적 접근법 보다 더 잘 할 수는 없습니다 .

스칼라 프로세서 란 무엇입니까?

가장 간단한 종류의 최신 프로세서는 사이클당 하나의 명령을 실행합니다. 이것을 스칼라 프로세서라고 부릅니다. 위의 예제는 스칼라 프로세서에서 6 주기로 실행됩니다.

스칼라 프로세서의 예로는 Raspberry Pi 1 및 Raspberry Pi Zero에서 사용되는 Intel 486 및 ARM1176 코어가 있습니다.

슈퍼 스칼라 프로세서 란 무엇입니까?

스칼라 프로세서 (또는 실제로 모든 프로세서)를 빠르게 실행하는 확실한 방법은 클럭 속도를 높이는 것입니다. 그러나 우리는 곧 프로세서 내부의 논리 게이트가 얼마나 빨리 작동 할 수 있는지에 대한 한계에 도달합니다. 따라서 프로세서 설계자들은 여러 가지 일을 한 번에 수행하는 방법을 모색하기 시작했습니다.

인-오더 수퍼 스칼라 프로세서는 들어오는 명령어 스트림을 검사하고 명령어 간의 종속성에 따라 여러 파이프 라인 중 하나 (단순히 파이프)에서 한 번에 하나 이상의 명령을 실행하려고 시도합니다. 예를 들어 양방향 수퍼 스칼라 프로세서가 다음 예와 같이 여섯 가지 명령어를 쌍으로 (또는 이중 발행 ) 만들 수 있다고 생각할 수 있습니다 .

t, u = a + b, c + d
v, w = e + f, v + g
x, y = h + i, j + k

그러나 이것은 의미가 없습니다. 우리가 w를 계산할 수 있기 전에 v를 계산 해야하기 때문에 세 번째와 네 번째 명령을 동시에 실행할 수 없습니다. 양방향 수퍼 스칼라 프로세서는 실제로 세 번째 명령어와 쌍을 이루는 것을 찾을 수 없기 때문에 이 예제는 네 가지 사이클로 실행됩니다.

t, u = a + b, c + d
v = e + f # 두 번째 파이프는 아무 것도하지 않습니다.
w, x = v + g, h + i
y = j + k

수퍼 스칼라 프로세서의 예로는 Intel Pentium과 Raspberry Pi 2 및 Raspberry Pi 3에 사용 된 ARM Cortex-A7 및 Cortex-A53 코어가 있습니다. Raspberry Pi 3는 Raspberry Pi 2보다 클럭 속도가 33 % 높지만 성능은 대략 두 배입니다. Cortex-A7보다 넓은 범위의 명령어를 이중 발행하는 Cortex-A53의 성능 덕분에 추가 성능이 부분적으로 향상되었습니다.

out-of-order 프로세서 란 무엇입니까?

우리의 예제로 돌아 가면, vw 사이의 의존 관계가 있지만 프로그램의 뒷부분에 있는 다른 독립적 인 지침이 있으므로 두 번째 사이클에서 빈 파이프를 채우는 데 잠재적으로 사용할 수 있음을 알 수 있습니다. 비 순차 슈퍼 스칼라 프로세서는 파이프를 계속 사용하기 위해 들어오는 명령어의 순서를 뒤섞는 기능 (종속성에 다시 종속 됨)이 있습니다.

out-of-order 프로세서는 다음과 같은 예제에서 wx의 정의를 효과적으로 교환할 수 있습니다.

t = a + b
u = c + d
v = e + f
x = h + i
w = v + g
y = j + k

세 가지 사이클에서 실행할 수 있습니다.

t, u = a + b, c + d
v, x = e + f, h + i
w, y = v + g, j + k

out-of-order 프로세서의 예로는 Intel Pentium 2 (및 일부 Atom 및 Quark 장치를 제외한 대부분의 후속 Intel 및 AMD x86 프로세서) 및 Cortex-A9, -A15, -A17 등의 최신 ARM 코어가 있습니다. 및 -A57.

분기 예측 자 (branch predictor) 란 무엇입니까?

위의 예는 직선 코드입니다. 실제 프로그램은 당연히 이와 같지 않습니다. 즉 if, 명령문 과 같은 조건부 연산을 구현하는 데 사용되는 전방 분기 와 루프를 구현하는 데 사용되는 후방 분기를 모두 포함합니다 . 분기는 무조건 (항상 수행 됨)이거나 조건부 (계산 된 값에 따라 결정되거나 취해지지 않음) 일 수 있습니다.

명령어를 가져 오는 동안, 프로세서는 아직 계산되지 않은 값에 의존하는 조건부 분기를 만날 수 있습니다. 스톨 (stall)을 피하기 위해 다음 명령을 추측해야합니다 : 메모리 순서의 다음 명령 (untaken 브랜치에 해당) 또는 브랜치 타겟에있는 명령 (taken 브랜치에 해당). 분기 예측은 프로세서가 분기가 아닌지 여부에 대한 지능적인 추측을 할 수 있습니다. 과거에 특정 분기를 얼마나 자주 가져 왔는지에 대한 통계를 수집하여이를 수행합니다.

현대 분기 예측자는 매우 정교하며 매우 정확한 예측을 생성 할 수 있습니다. Raspberry Pi 3의 추가 성능은 부분적으로 Cortex-A7과 Cortex-A53 간의 분기 예측 향상으로 인한 결과입니다. 그러나 제작 된 일련의 분기를 실행하면 공격자가 분기 예측자를 잘못 훈련하여 예측이 잘못 될 수 있습니다.

추측이란 무엇입니까?

순차 명령어 순서 변경은 명령어 수준의 병렬 처리를 더 많이 복구하는 강력한 방법이지만 프로세서가 더 넓어 질수록 (3 중 또는 4 중 명령어로 실행 가능) 모든 파이프를 바쁘게 유지하는 것이 어려워집니다. 따라서 현대의 프로세서는 추측 할 수있는 능력을 키웠다 . 추측 적 실행은 우리가 필요로하지 않을 수도있는 지시를 내릴 수있게 해준다. (파이프가 분기되어있을 수 있기 때문에) 파이프를 바쁘게 유지 (사용하거나 잃는다!)하고, 명령이 실행되지 않는다고 판명되면, 결과를 버릴 수 있습니다.

불필요한 명령어 (및 추측 및 재정렬을 지원하는 데 필요한 인프라)를 과도하게 실행하면 추가 에너지가 소비되지만 많은 경우 단일 단일 스레드 성능을 얻으려면 보완적인 트레이드 오프로 간주됩니다. 분기 예측기는 프로그램을 통해 가장 가능성이 높은 경로를 선택하여 추측 가능성을 극대화합니다.

추측의 이점을 설명하기 위해 다른 예를 살펴 보겠습니다.

t = a + b
u = t + c
v = u + d
if v :
  w = e + f
  x = w + g
  y = x + h

이제 우리는 t에서 u에서 v까지, 그리고 w에서 x에서 y까지의 의존성을 가지므로 추측이 없는 양방향의 순서가 없는 프로세서는 두 번째 파이프를 채울 수 없습니다. t, uv를 계상하는데 3사이클을 소비한 후 if문이 실행되는지 여부를 알 수 있습니다. 이 경우 w, xy를 계산하는데 3사이클을 소비합니다. if(분기 명령에 의해 구현 된) 명령이 한 사이클을 차지 한다고 가정하면 이 예제에서는 4 사이클 ( v가 0 인 경우) 또는 7 사이클 ( v가 0이 아닌 경우) 중 하나를 취합니다 .

분기 예측자가 if명령문 본문이 실행될 가능성이 높다고 표시하면 추측은 프로그램을 다음과 같이 효과적으로 뒤섞습니다.

t = a + b
u = t + c
v = u + d
w_ = e + f
x_ = w_ + g
y_ = x_ + h
if v :
  w, x, y = w_, x_, y_

이제 우리는 파이프를 계속 사용하기 위해 추가 명령 수준 병렬 처리를 수행합니다.

t, w_ = a + b, e + f
v, y_ = u + d, x_ + h
if v :
  w, x, y = w_, x_, y_

사이클 계산은 out-of-order 프로세서에서는 덜 정의되어 있지만, w, xy의 분기 및 조건부 업데이트는 (대략) 무료이므로 예제는 (대략) 3사이클에서 실행합니다.

캐시 란 무엇입니까?

옛날*에는 프로세서의 속도가 메모리 액세스 속도와 잘 일치했습니다. 2MHz 6502를 장착한 나의 BBC 마이크로는 대략 2초(마이크로초)마다 명령을 실행할 수 있었고 메모리주기 시간은 0.25초였다. 이후 35 년 동안 프로세서는 훨씬 빨라졌지만 메모리는 적당히 적당합니다. Raspberry Pi 3의 단일 Cortex-A53은 약 0.5ns (나노 초)마다 명령을 실행할 수 있지만 최대 100ns까지 소요될 수 있습니다.

언뜻 보기에 이것은 재앙처럼 들린다. 우리가 기억에 접근할 때마다, 우리는 100ns가 결과를 되찾기를 기다리게 될 것이다. 이 경우,이 예는 다음과 같습니다.

a = mem [0]
b = mem [1]

200ns가 걸린다.

그러나 실제로 프로그램은 상대적으로 예측 가능한 방식으로 메모리에 액세스하는 경향이 있습니다. 일시적인 지역성 (위치에 액세스하면 곧 다시 액세스 할 가능성이 높습니다)과 공간적 지역성 (위치에 액세스하면 가능성이 있습니다. 가까운 위치에 곧 액세스). 캐싱은 이러한 속성을 활용하여 메모리 액세스의 평균 비용을 줄입니다.

캐시는 프로세서에 가까운 작은 온칩 메모리로 최근 사용 된 위치 (및 이웃)의 내용 사본을 저장하므로 후속 액세스에서 신속하게 사용할 수 있습니다. 캐싱을 사용하면 위의 예제가 100ns를 약간 넘는 시간 내에 실행됩니다.

a = mem [0] # 100ns 지연, mem [0:15]를 캐시에 복사합니다.
b = mem [1] # mem [1]이 (가) 캐시에 있습니다.

Spectre and Meltdown의 관점에서 중요한 점은 메모리 액세스 소요 시간을 측정 할 수있는 경우 액세스 한 주소가 캐시에 있는지 (짧은 시간) 또는 그렇지 않은지 (오랜 시간) 여부를 확인할 수 있다는 것입니다.

사이드 채널이란 무엇입니까?

위키 백과에서 :

"... 측면 채널 공격은 무차별 공격이나 알고리즘의 이론적 약점 (암호 해독 비교)보다는 암호 시스템의 물리적 구현에서 얻은 정보를 기반으로 한 공격입니다. 예를 들어, 타이밍 정보, 전력 소비, 전자기 누설 또는 심지어 소리는 정보를 추가로 제공 할 수 있으며 이로 인해 시스템을 파괴 할 수 있습니다. "

Spectre and Meltdown은 일반적으로 액세스 할 수 없는 메모리 위치의 내용을 타이밍을 사용하여 추측하여 액세스 가능한 다른 위치가 캐시에 있는지 여부를 관찰하는 측면 채널 공격입니다.

함께 모아서

이제 우리의 프로세서에 대한 Meltdown과 같은 공격을 허용하기 위해 추측과 캐싱이 어떻게 결합되는지 살펴 보겠습니다. 불법 (커널) 주소에서 읽을 때가 있어 오류 (충돌)가 발생하는 사용자 프로그램인 다음 예제를 생각해보십시오.

t = a + b
u = t + c
v = u + d
if v :
  w = kern_mem [주소] # 우리가 여기에 오면, 잘못
  x = w & 0x100
  y = user_mem [x]

이제, 우리가 분기 예측자를 훈련시켜 v가 0이 아닌 것으로 믿게한다면, 우리의 순서가 맞지 않는 양방향(out-of-order two-way) 슈퍼 스칼라 프로세서는 다음과 같이 프로그램을 뒤섞습니다.

t, w_ = a + b, kern_mem [주소]
u, x_ = t + c, w_ & 0x100
v, y_ = u + d, user_mem [x_]

if v :
  # 오류
  w, x, y = w_, x_, y_ # 우리는 결코 여기에 도달하지 않는다.

프로세서가 항상 커널 주소를 추측 적으로 읽더라도 v가 0이 아님을 알 때까지 결과 오류를 연기해야 합니다. 그 때문에, 이것은 안전하다고 느낀다.

  • v 는 0이므로 불법적인 읽기의 결과는 w에 커밋되지 않습니다.

  • v 는 0이 아니지만 읽기가 w에 커밋되기 전에 오류가 발생합니다.

그러나 우리는 코드를 실행하기 전에 우리의 캐시를 플러시하고 v가 실제로 0이 되도록 a, b, c, 및 d를 정렬한다고 가정합니다. 이제 세 번째 사이클에서의 투기적 읽기 :

v, y_ = u + d, user_mem [x_]

불법 읽기 결과의 8 번째 비트에 따라 사용자 랜드 주소 0x000또는 주소 0x100에 액세스 하고 해당 주소 및 이웃을 캐시로 로드합니다. 왜냐하면 v가 0 이니까 추측 명령어의 결과는 무시되고 실행은 계속됩니다. 이러한 주소 중 하나에 대한 후속 액세스를하면 캐시에있는 주소를 확인할 수 있습니다. 축하합니다. 커널의 주소 공간에서 단 하나의 비트를 읽었습니다!

실제 Meltdown 익스플로잇은 실질적으로 이것보다 훨씬 복잡합니다 (특히 분기 예측기를 잘못 조정해야하는 것을 피하기 위해 저자는 무조건 읽기를 무조건 실행하고 결과 예외를 처리하는 것을 선호합니다). 원칙은 동일합니다. Spectre는 유사한 접근법을 사용하여 소프트웨어 배열 범위 검사를 파괴합니다.

결론

현대 프로세서는 추상화를 유지하기 위해 메모리를 직접 액세스하는 순차적 인 스칼라 머신을 유지하면서 캐싱, 명령어 순서 변경 및 추측과 같은 다양한 기술을 사용하여 단순한 프로세서보다 훨씬 높은 성능을 제공 할 수 있습니다 달성하기. 붕괴와 스펙터는 우리가 그 추상화의 맥락에서 보안에 대해 논할 때 일어나는 일의 예이며, 그 다음 추상화와 현실 사이에 작은 불일치가 발생합니다.

Raspberry Pi에 사용 된 ARM1176, Cortex-A7 및 Cortex-A53 코어의 추측이 부족하여 이러한 종류의 공격에 면역이됩니다.

* 일은 오래되지 않았거나 좋은 일이 아닐 수도 있습니다.