카테고리: Hardware

네트워크 장비 이야기

현대 CPU의 구조적 결함 – 멜트다운(Meltdown)과 스펙터(Spectre) 그리고 KAISER

 

지난 1월 2일, 미국의 인터넷 커뮤니티인 Reddit의 컴퓨터 서브레딧에 리눅스 커널에서 수상한 수정 작업이 이루어지고 있다는 소식이 올라왔습니다.

리눅스 커널에서 KASLR 관련 패치라는 코멘트를 달고 대규모의 수정과 백포팅이 이루어지고 있으며, 해당 패치를 적용할 시 시스템 콜 성능이 30%~70%까지 저하된다는 이야기였습니다. 이는 곧바로 이슈가 되었고, 그 다음날인 3일, 사태의 전모가 밝혀지게 되었습니다. 바로 멜트다운과 스펙터입니다.

여기서는 상기 링크에 게시된 멜트다운과 스펙터에 대한 논문과 멜트다운의 대응책으로 제시된 KAISER에 대한 논문을 참고하여, 공격 방법과 영향 범위에 대해 이야기해 볼 것입니다.

 

1. 멜트다운 공격

멜트다운과 스펙터는 모두 현대 CPU의 중요 구성요소 중 하나인 분기 예측기(Branch Predictor)의 결함에 근간을 두고 있습니다. 구글 프로젝트 제로(Project Zero)의 연구팀들은 이 결함을 총 3가지의 종류로 분류하였으며, 각각 경계값 검사 우회(CVE-2017-5753), 분기 목표 인젝션(CVE-2017-5715), 악의적인 데이터 캐시 로드(CVE-2017-5754)라는 이름을 붙였습니다. 이 중 앞의 두 가지는 Spectre 공격과 관련이 있고, 마지막 하나는 Meltdown 공격과 관련이 있습니다.

멜트다운 공격은 마이크로옵 단위에서의 레이스 컨디션 공격입니다. 현대 CPU는 길다란 명령어 파이프라인을 가지고 있고, 프론트엔드에서 해석된 명령어는 작은 단위의 명령어(μop)로 쪼개진 뒤 재정렬 버퍼(Re-Order Buffer)로 들어갑니다. 이를 통해 ALU가 데이터 병목 없이 최대한의 병렬성을 가질 수 있도록 합니다. 그리고 명령어 실행이 종료된 뒤, 실행 결과는 다시 정렬됩니다. 이러한 일련의 동작을 비순차적 실행(Out of Order Execution)이라고 합니다. 멜트다운은 인텔 CPU가 가지고 있는 분기 예측기의 결함과, Out-of-Order Execution 매커니즘의 결함을 악용하여 공격을 수행합니다.

멜트다운 공격은 다음과 같은 순서로 이루어집니다.

1. 공격자가 사용자 영역에서 접근이 불가능한 영역의 메모리(Ex. 커널 영역 메모리)를 참조하여 예외(Execption)를 일으킵니다.
2. 예외가 발생하였으므로, 코드의 실행 흐름은 예외 핸들러로 넘어가고, 분기 예측기의 투기적 실행과 비순차적 실행 매커니즘에 따라 로드된 메모리 값은 폐기됩니다.
2-1. 투기적 실행이 실패하였으므로, 파이프라인은 클리어되고 결과는 커밋되지 않습니다. 하지만 CPU의 캐시에는 해당 주소에서 불러온 값이 들어 있습니다.
3. 예외 핸들러가 코드의 실행 흐름을 물고 있는 동안, 다른 프로세스로 Flush+Reload 공격을 가해 캐시 데이터를 추측합니다.
4. 데이터 추출이 완료된 뒤 예외를 적절하게 처리하거나, 인텔의 TSX 명령어를 이용한 예외 억제(Exception suppression)를 통해 공격 속도를 증가시킬 수 있습니다.

결국, 멜트다운 공격은 CPU의 분기 예측기가 투기적인 명령어 실행을 수행할 때, 해당 메모리에 대한 접근 권한을 확인하지 않기에 발생하는 문제입니다. AMD는 이 문제에 대하여 ‘AMD 아키텍처의 분기 예측기 구조는 인텔의 것과는 다르기 때문에 안전하다’는 내용의 공식 성명을 내놓았습니다.

이 문제를 해결하기 위한 방법으로, 구글은 지금 즉시 모든 커널에 KAISER를 적용할 것을 권고하였습니다. KAISER는 본래 캐시 메모리 부채널 공격에 의해 KASLR이 무력화되는 것을 방지하기 위해 제안되었으나, 멜트다운 공격에도 역시 유효하다는 것이 확인되었습니다.

KAISER는 연속적으로 할당되어 있는 커널 영역 메모리와 유저 영역 메모리를 서로 분리하여 참조를 통해 접근하도록 수정하는 것을 골지로 하고 있습니다. 이러한 수정은 커널 영역 메모리에 접근할 때 오버헤드를 가져오게 되는데, CR3 레지스터의 PML4값을 변경하는 것과, TLB(Transaction Lookaside Buffer)의 초기화(Flush)가 그것입니다.

 

첫번째 문제를 해결하기 위해 KAISER는 Shadow Address Space라는 트릭을 제안합니다. 유저와 커널 메모리는 분리하지만, 물리 주소는 특정 값(12번째 비트)을 차로 가지도록 할당한다는 이야기입니다.
논문에서 제안된 대로 12번째 비트를 변경하여 메모리 페이지를 할당하게 된다면, 컨텍스트 스위칭이 발생할 때마다 전체 PML4 값을 갱신하는 것이 아니라, 1비트의 비트 연산을 수행하는 것 만으로 올바른 주소를 계산할 수 있게 됩니다.

다시 말해, CR3 레지스터는 변경되지만 변경에 필요한 연산과 클럭은 최소화될 수 있습니다.

두번째로 TLB에 대해 이야기하려면 먼저 IA-32e 아키텍처에서의 메모리 구조를 알아야 합니다. IA-32e 아키텍처에는 총 5단계에 걸친 메모리 페이지가 존재하고, 이러한 페이지 참조를 빠르게 하기 위해 CPU는 일종의 캐시인 TLB(Transaction Lookaside Buffer)를 가지고 있습니다.

TLB에는 페이지 번호와 페이지 프레임의 주소가 저장되고, TLB를 참조하게 되면 연산 유닛은 페이지 오프셋만을 가지고 정확한 물리 주소를 찾을 수 있게 됩니다. 이러한 이점을 최대화하기 위하여 현대 OS들은 TLB Flush가 최대한 발생하지 않도록 최적화가 이루어져 있습니다. 만약 KAISER를 적용한다면, 컨텍스트 스위칭마다 PML4 값이 변경되므로 TLB Flush가 필연적으로 발생하게 될 것입니다.

하지만, 최근의 연구에서 아키텍처 설명과는 달리 내부적으로 TLB에 대한 최적화가 이루어져서 CR3 레지스터의 변경이 TLB를 Flush하지 않는다는 사실이 밝혀졌습니다. 해당 논문은 이 사실에 근거하여 테스트를 진행, KAISER를 적용할 시의 성능 저하는 0.08%~0.68%에 불과하다고 결론짓습니다.

KAISER는 요청한 커널 페이지가 캐시에 로드되고, 두번째로 페이지 폴트가 발생할 때의 명령어 수행 시간의 차이를 탐지하여 올바른 커널 메모리 주소를 찾아내는 Double Page Fault 공격에 대한 대응책으로 제시되었지만, 결과적으로 커널 메모리 영역에 대한 시간차 기반 캐시 부채널 공격을 차단함으로써, 멜트다운 공격을 방어하는 것에도 역시 유효합니다. 하지만 유저 영역과 유저 영역에 남아있는 일부 커널 메모리 영역은 여전히 멜트다운의 영향을 받습니다.

 

2. 스펙터 공격

 

라우터/스위치의 구조

인터넷이라는 거대한 통신망을 구성하고 있는 여러 프로토콜의 동작을 이해하려면 가장 먼저 실제로 데이터를 전송하는 장비들인 라우터와 스위치에 대한 이해가 필요하다.

이 글은 내가 네트워크 공부를 하면서 느꼈던 가장 큰 어려움인 ‘왜?’라는 질문에 대한 답이며, 그 때의 나와 같은 어려움을 겪고 있을 누군가를 위해 쓰여졌다.

라우터의 구성요소에 대해 체계적이고 자세한 정보를 올려주신 ‘넷매니아즈 (http://netmanias.com)’ 에 무한한 감사를 보낸다.


목차

  1. Introduction
  2. 라우터의 하드웨어 구성 요소
  3. 통신 시나리오

 

 

1. Introduction

우리는 네트워크로 전송된 패킷이 스위치와 라우터 사이를 계속 건너가면서 상대방 컴퓨터에 도달한다고 배웠다. 그리고 그 과정에서 ARP/MAC/IP 등등의 프로토콜을 통해 목적지를 식별할 수 있다고 배웠다.
그러나, 난 이것을 듣고 가장 먼저 의문이 들었다.

“클라이언트는 서버의 MAC을 어떻게 알 수 있을까?”, “IP 주소만으로 어떻게 목적지를 찾을 수 있을까?”

아직 캡술화(Encapsulation)와 라우팅 프로토콜에 대한 개념이 제대로 잡혀있지 않았기 때문에 생긴 의문이지만, 이것을 제대로 이해하는 것에는 상당한 시간과 노력이 필요했다.
그리고 실제로 이 기능을 구현한 장비의 작동 로직을 파악함으로써 완전한 이해에 도달할 수 있었다.

이 글은 아무것도 모르는 사람을 위해 작성된 것은 아니다. 이 글을 이해하려면 최소한 Lan Switching에 대한 지식은 갖추고 있어야 할 것이다.

그렇지만, 만약 당신이 지식은 갖고 있지만 스위칭과 라우팅의 명확한 개념을 잡지 못 하고 있다면, 이 글이 많은 도움이 될 것이라고 나는 확신한다.

 

2. 라우터의 하드웨어 구성 요소

Supervisor Engine
라우터의 핵심 기능을 가지고 있는 모듈로, Layer3 이상의 제어를 담당하며, Control Plane으로 불리기도 한다.
보통 범용 CPU 위에 여러 가지 기능을 수행하는 OS(IOS/JUNOS)가 올라가며, RIB/LSDB/ARP Table/MAC Table 등의 정보와, 라우팅, Management 등의 기능을 수행하는 프로세스들이 동작하는 모듈이다.

Line Card
패킷 송수신, QoS 등의 기능을 수행하는 모듈로, 실제 믈리 계층과의 연동을 맡는다. 이를 위해 FIB(Forwarding Information Base), MAC Table 등이 존재하며, 패킷을 실제로 처리하는 Packet Processor와 입/출력 패킷을 임시로 저장하는 Ingress Packet Buffer와 Egress Packet Buffer가 존재한다.
Data Plane으로 불리기도 한다.

Switch Fabric Module
Line Card간에 패킷을 전달하기 위한 가교 역할을 한다. Module 형태로 구현된 장비들의 경우, Line Card의 개수가 여러 개일 수 있는데, (Ex. Fa 0/1, Fa 1/1) 이들 사이의 데이터 전송에 이용된다.

3. 통신 시나리오

Host A가 Router B를 통해 Host C로 IP 패킷을 전송하는 사나리오를 이용해 라우터가 어떻게 동작하는지 알아보자.

Router_Structure

1. Host A가 Host C로 데이터를 보내려고 하지만 상대방의 MAC Address를 모르므로 먼저 ARP Request를 보낸다.

2-1. Router B의 Line Card #0에 Host A가 보낸 ARP Request가 도착한다.

2-2. Line Card #0의 Packet Processor는 ARP Request의 L2 헤더를 검사, 자신의 MAC Address Table에 Host A의 MAC이 존재하지 않는 것을 확인하고, Source MAC Learning Event를 발생시킨다. 이는 Control Plane으로 전달되어 CP의 MAC Address Table에 Host A의 MAC이 기록되고, Line Card #0의 MAC Address Table에도 같은 정보가 기록된다.

2-3. 해당 패킷이 ARP임을 Ether-Type Field(0x0806)을 통해 확인한 Packet Processor는 이 패킷을 Supervisor Engine으로 올려보낸다.

2-4. Line Card #0에서 보내온 ARP Request를 받은 Supervisor Engine은 자신의 ARP Entry를 확인한 뒤, ARP Table에 Host A의 MAC/IP Address와 인터페이스(Fa 0/1)를 기록한다.

2-5. 해당 요청이 ARP Request임을 확인한(ARP Operation Field) Supervisor Engine은 Line Card들에게 이 패킷이 들어온 인터페이스를 제외한 모든 인터페이스로 이 패킷을 Flooding 할 것을 지시한다.

2-6. Fa 0/0을 제외한 다른 모든 인터페이스로 ARP Request가 전송되고, Fa 1/0에 연결되어 있는 Host B가 이 패킷을 수신, ARP Reply를 내보낸다.

2-7. Line Card #1은 Host B의 ARP Reply를 수신, 자신의 MAC Address Table에 Host B의 MAC이 존재하지 않는 것을 확인하고 MAC Address Learning Event를 발생시킨다.

2-8. –MAC Learning, ARP 식별 설명 생략- Supervisor Engine은 자신의 FIB(Fowarding Information Base)를 참조, 이 Unicast 패킷이 Fa 0/0으로 전달되어야 함을 확인, Line Card #0에게 이 패킷을 Fa 0/0으로 내보낼 것을 지시한다.

2-9. Host A가 Host C의 ARP Reply를 수신, 자신의 ARP Table에 Host C의 MAC을 기록한다.

3. Host A와 C는 Ethernet Switching을 통해 통신한다.