어떻게 CPU는 데이터를 흘리게 됐을까?
'훔쳐볼 수 있다'
보라고 열어 놓은 것도 아니고, 결코 봐서는 안되는 데이터를 들여다 볼 수 있음을 알게 된 사건. 세상을 떠들썩하게 만든 CPU 취약성은 이 한 문장으로 요약할 수 있을 것이다. '설마 누가 보겠어?'라는 지난 수십 년의 방심을 '그래도 누군가 볼 수 있지 않을까?'라는 합리적 의심으로 찾아낸 놀라운 발견이었으나, 동시에 지금 컴퓨터로 작동되는 세상을 혼란에 빠뜨린 전대미문의 사건이 되고 말았다. CPU의 작동 방식으로 인해 메모리에 들어 있는 데이터를 볼 수 있는, 특히 민감한 정보가 들어 있는 운영체제 커널 메모리를 볼 수 있는 사실에 그 위험성을 아는 이들마다 비명을 질렀을 만큼 CPU 취약성의 충격은 컸다.
지금에서야 하는 말이지만, 어쩌면 이렇게 큰 혼란을 부르지 않을 수도 있었다. 지난 해 7월 구글 프로젝트 제로팀과 여러 대학과 기업의 보안 연구원들이 CNE에 취약성을 보고한 이후 업계는 조용히 취약성 패치를 지난 1월 9일까지 준비할 예정이었다. 하지만 그보다 앞선 미디어의 폭로가 나오면서 그들의 시간 설정은 의미 없는 일이 되어 버렸고, 이를 받아들일 준비가 되지 않은 수많은 이용자들과 업계의 절규와 분노 레벨은 한 순간 최고치에 도달할 수밖에 없었다.
CPU 취약성은 이제 수습 단계로 들어서고 있지만, 운영체제와 프로세서, 그 밖의 칩셋 제조사들 중 누구도 완벽한 해결책이 없다는 이야기를 반복한다. CPU가 명령과 데이터를 처리하는 과정에서 메모리 내용이 누설되는 이유가 CPU의 성능 향상을 가져온 비순차적 실행(Out of Order Execution)과 관련된 것이라서다. 말 그대로 순서대로 처리하지 않는 이 처리방식은 오늘날 거의 모든 CPU가 쓰고 있다.
예를 들어 먹구름이 밀려오고 있지만 당장 비는 내리지 않는다고 가정하자. 지금 비가 내리지 않으므로 우산을 가져나갈 이유가 없을 것이다. 하지만 문을 나서는 순간 비가 내린다면 다시 집으로 돌아가 우산을 가져온 뒤 우산을 펴고 집을 나서야 한다. 여기까지만 보면 순차적인 처리다. 그런데 만약 비가 내릴 것을 예상하고 미리 우산을 가져왔다면 곧바로 우산을 펴고 밖으로 나가면 된다. 비가 내릴 것을 대비해 미리 우산을 가져오는 일을 먼저 했으므로 다시 우산을 가지러 집으로 돌아가는 일을 생략한 만큼 우산으로 비를 막는 결과는 같아도 일의 순서는 바뀐 비순차적 실행을 한 셈이다.
비가 내릴 것에 대비해 우산을 챙김으로써 집으로 돌아가 우산을 챙겨오는 시간과 작업을 줄이는 것처럼 CPU도 이렇게 연산 순서를 조금 바꿔 처리함으로써 명령을 처리하는 파이프라인의 사이클을 줄여 성능을 높였다. 명령에 의해 연산을 할 때 뒤에서 처리한 연산 결과가 앞쪽 연산에 영향을 미치는 경우가 있을 수 있으므로 이를 미리 처리해 놓는 것이다. 여기에 분기 예측, 또는 예측 실행(Speculative Execution)으로 실제 필요한 작업이라고 판단하기 전에 먼저 작업을 수행한다. 이것이 올바르면 명령어를 처리하는 사이클의 낭비 없이 연산을 계속 수행하고, 그렇지 않으면 예측 실행의 결과를 버린 채 올바른 명령을 이어서 처리한다.
이렇게 CPU가 비순차적 실행에서 나온 값은 앞쪽 연산을 위해 다양한 메모리에 임시 저장한다. 저장장치에서 램으로 불러와 정렬시킨 연산자와 데이터는 CPU 안 L3, L2, L1 캐시와 레지스터를 오가며 처리되고, 만약 비순차적 실행으로 즉시 처리해야 할 결과는 L1 캐시에 임시로 저장해뒀다가 이 값이 쓰이는 순간을 기다린다.
다만 메모리에 임시로 저장되는 값이 앞에서 실행 중인 연산에 반드시 필요한 값이 아닐 수도 있다는 게 문제다. 앞서 비가 내릴 것을 예측해 우산을 가져왔는데, 비가 내리지 않으면 갖고 온 우산은 쓸모 없게 되는 것과 마찬가지다. 이러한 일들이 CPU 안에서 흔하게 일어난다. 즉, 미리 연산해 놓은 예측된 결과가 앞 연산과 맞으면 처리가 빨라지고, 틀리면 지금 해 놓았던 결과를 포기하고 처음으로 돌아가 작업하는 것이다.
그런데 우산을 꺼낼 때 집주인의 허락을 받아야 하는 조건을 두고 그 허락을 받지 않으면 준비된 우산을 쓰지 않는다고 가정해보자. 일단 집주인이 허락한 상황을 확인하고 우산을 꺼내는 게 올바르지만, 이 실행에서 CPU는 집주인이 우산을 꺼내라고 할 것이라 예상하고 권한에 상관 없이 미리 우산을 꺼내놓는다. 하지만 이후 연산에서 허용되지 않았음을 알고는 우산을 치우는데, 그렇게 우산은 이미 한번 밖으로 나왔다가 다시 들어가는 모양새가 된다. 순서대로 처리하면 먹구름이 끼었어도 일단 밖에 나갔다가 비가 내리는 것을 확인한 다음 다시 집으로 돌아가 허락을 구한 뒤 우산을 꺼내와야 하는데, 이 경우는 우산을 꺼내도 좋다고 허락할 것이라 짐작하고 적극적으로 먼저 우산을 꺼내 버린 셈이다. 꺼내지 말았어야 할 우산은 실행하면 안될 프로세스지만 이미 예측 실행을 마쳤으므로 앞의 작업을 기다리는 동안 캐시 같은 메모리에 남게 되고, 이건 필요 없는 것이라 취소를 하더라도 이미 CPU 캐시 메모리에 남아 있는 그 짧은 시간 사이드 채널 기법 같은 방식으로 탐색될 수 있는 것이다.
이때 권한 없이 꺼낸 우산이 다름 아닌 운영체제의 커널 중 일부라는 게 문제다. 커널은 시스템 전체를 통제할 수 있는 막강한 권한을 갖고 있는데다, 여러 비밀 번호도 포함하고 있어 일반 프로그램의 접근을 허용하지 않고 보호되는 것이 상식이다. 하지만 이렇게 CPU가 권한을 예측하고 실행해 버리면 커널을 보호하는 경계가 녹아내려 일반 프로그램에서 접근해 내용을 볼 수 있는데, 이것이 바로 멜트다운(Meltdown, CVE-2017-5753)이다.
멜트다운은 지나치게 적극적인 예측 실행을 하는 인텔 CPU와 일부 ARM 아키텍처에서 나타나는 문제지만, 또 다른 방법은 거의 모든 프로세서에서 나타난다. 물론 방법은 비슷한데, 적극적인 실행 예측이 아니라 정상적인 절차를 수행함으로써 분기를 예측하는 방법을 이용한다.
CPU가 비순차 실행에서 다음에 실행할 명령을 여러 방법으로 예측하게 되는데, 이때 반복되는 패턴을 보이게 되면 예측 실행의 방향을 결정하는 분기 예측기는 그 다음에 실행할 명령이 무엇인지 정해진 패턴에 의해 짐작한다. 어차피 이전에 했던 대로 또 실행할 것이니까 미리 처리해 놓는 것이다. 정상적인 절차에 따라 권한을 얻어 우산 꺼내기를 여러번 반복하면 CPU는 다음에도 권한을 얻어올 것이라 여기고 습관적으로 또 우산을 미리 꺼낸다. 앞서 멜트다운이 어떤 이유 없이 그 권한까지 짐작하고 적극적으로 실행한 것이라면, 스펙터는 권한을 얻어 처리하는 일의 반복으로 이후 권한이 요청된 작업을 또 할 것이라는 이유를 들어 미리 실행하는 셈이다. CPU가 이같은 분기 처리에 익숙해 있는 상태에서 하려던 작업을 취소하면 앞서 처리해 캐시 메모리에 남아 있던 값은 의미가 없어 지운다. 하지만 지우기 직전 이미 결과는 캐시 메모리에 있었으므로 그 변화를 추적하는 사이드 채널 기법으로 읽어내는 것이 스펙터(Spectre, CVE-2017-5753 및 CVE-2017-5715)다. 스펙터는 자바스크팁트로 짠 프로그램을 실행하는 인터넷 브라우저만 있어도 실행될 수 있기 때문에 인텔 뿐만 아니라 AMD와 ARM 기반 프로세서까지 모두 영향을 받는다.
이처럼 처리 효율을 획기적으로 높여 성능을 발전시킨 기술로 훔쳐 볼 수 있는 취약성을 드러낸 점은 정말 아이러니다. 처리해야 할 데이터가 마지막까지 권한을 위배하는지 아닌지 뭔지 알지 못하는 CPU 구조로 취약성이 들어난 지금이야 얼토당토 않게 처리하고 있다는 사실을 비판받고 있지만, 사실 증가되는 위협에 대항하기 위한 한층 더 깊어진 보안 기술과 관심의 수준이 성능 중심적으로 발전하던 CPU 부문에 새로운 방향을 제시한 것은 그나마 다행인 일이다.
그런데 여기서 짚고 갈 부분은 멜트다운과 스펙터의 영향이 미치는 범위다. CPU에서 처리된 정보를 누구나 볼 수 있다고 생각할 지 모르나 그렇게 간단한 게 아니라서다. 두 취약성은 분명 메모리 정보를 볼 수 있는 위협이지만, 그것을 볼 수 있는 방법은 시스템 안에 한정된다. 멜트다운과 스펙터가 흔적을 남기지 않고 메모리 내용을 볼 수 있는 취약성이기는 해도 외부에서 네트워크로 접속해 흔적 없이 훔쳐보는 것은 어렵다. 단지 이 취약성이 치명적인 것은 하나의 시스템에서 CPU와 메모리를 공유하는 여러 개의 가상 머신이 작동 중일 때 이 가상 머신 중 하나에서 다른 가상 머신의 메모리를 모두 들여다 볼 수 있어서다. 구글이나 아마존, 마이크로소프트 등 대규모 클라우드와 데이터센터를 운영 중인 기업들이 취약성 문제를 해결하려고 은밀하게 진행했던 것도 이런 이유다.
또한 두 취약성은 메모리를 볼 수만 있을 뿐 수정할 수는 없다. 즉, 메모리에 들어 있는 것을 빼낼 수는 있으나 거꾸로 데이터를 넣거나 바꿔칠 수 있는 방법은 아니다. 물론 아직까지 다른 변종에 대한 정보가 없어 공격 가능성이나 다른 증거를 내놓기는 어렵지만, 다른 방식의 공격을 확인하긴 어려운 상황이다. 물론 중요 정보가 샐 수 있는 그 자체 만으로도 얕볼 수는 없는 일인 것은 변함없다.
이번 CPU 취약성이 공개된 이후 프로세서 제조사는 물론 운영체제, 칩셋, 인터넷 브라우저 업체가 멜트다운과 스펙터에 대한 위협을 줄여주는 패치를 개발해 수많은 컴퓨팅 장치에 붙이는 중이다. 취약성 패치가 나오기 전 어느 정도 성능 하락은 예상됐는데, CPU 세대, 운영체제 버전, 칩셋 성능에 따라 패치 이후의 벤치 마크 성능도 천양지차다. 그나마 최신 CPU와 운영체제의 개인 PC 성능 하락은 거의 없는 반면, 가상 머신과 게임 서버를 운영 중인 데이터 센터의 입출력 성능이 크게 떨어져 CPU 점유율이 높아져 아우성이다. 패치로 보안 위협을 조금 완화시켜주기는 하나 다른 문제를 키우고 있는 셈이다.
결국 이번 취약성은 패치로 해결되지 않는, 임시 처방이라는 것을 모두 알고 있다. 그 임시 처방이 가져온 피해와 갈등은 CPU와 CPU를 다루는 모든 환경에 대한 고민을 안겨주는 문제다. 지금까지 수많은 이득이 성능의 관점에서 이야기되어 왔던 것들이, 이제는 위협과 안전의 관점에서 해답을 내놓길 원하는 것이다. 하지만 아직까지 누구도 이에 대한 말을 속시원하게 못한다. 어떤 해결책을 내놓지 한마디만 해주길 바랐던 CPU 업계의 수장은 입을 다물었다. 해답을 찾아야 한다는 건 다 아는데 알려주지 않는, 모든 것을 알려주기 위해 CPU는 작동하는 데, 정작 CPU에 대한 질문은 오늘도 이어지고 언제쯤 그 답을 들을 수 있을 지 아무도 모른다.