PKRU-safe: automatically locking down the heap between safe and unsafe languages


Paul Kirth, Stephen Crane, Adrian Dabrowski, Stijn Volckaret, Michael Franz
EuroSys '22: Seventeenth European Conference on Computer Systems

개요

Rust와 같은 safe language model은 unsafe와 같은 영역에서 memory corruption bug가 일어날 수도 있다. 이를 해결하기 위해서 Intel MPK를 사용하여서 Rust의 unsafe에서 발생하는 메모리 버그를 safe하게 만들었다.

Motivation

대부분의 컴퓨터 소프트웨어의 버그는 메모리 취약점을 이용한 버그이다. 이러한 메모리의 취약점은 해를 지나도 지속적으로 발견되고 있으며, 브라우저나 커널처럼 안전이 중요한 시스템에서는 더욱더 큰 보안 취약점으로 다가오게 된다. 특히 크롬의 취약점중 90%는 메모리 취약점일 정도로, prodection level응용 프로그램에서 메모리 취약점을 막기 위한 노력이 지속적으로 이루어지고 있다.

Memory bug를 없애기 위해서 Rust와 같은 safe langauge들을 사용하지만, unsafe code의 부분은 지속적으로 메모리 버그에 취약하다는 문제가 있다. 특히 이러한 unsafe part는 legacy code나 device driver, javascript virtual machine처럼 피할 수 없이 사용되어야 하는 곳이 있다는 점에서 없앨 수 없는 부분이다.

Importance

기존의 Unsafe한 부분을 Safe한 부분으로부터 보호하는 기법은 OS의 수정을 필요로 하거나, 아니면 Application의 수정을 필요로 하였다. 그러나 PKRU-Safe은 이러한 수정없이 Unstrusted component를 Memory protection key를 이용하여서 적은 overhead로 Trusted componenet으로부터 분리하였다.

Main Idea

Intel MPK를 사용하여서 Unsafe부분의 Memory bug를 Safe의 메모리 부분으로부터 분리한다.

Design

Overall design
PKRU-Safe는 Control flow integrity에 는 Treat model로 두지 않았다. 또한 이 논문은 Heap에 대한 공격을 방어하는 것이 목적이기 때문에 Shadow stack은 Future work으로 남겨두었다.
Developer는 코드의 어떤 부분이 Safe이고 어떤 부분이 Unsafe인지를 표시한다. 이 정보는 컴파일러에 의해서 해석되어서, Trusted와 Untrusted를 분리하는 trampoline을 삽입하여 Heap corruption을 막는다.
PKS의 granualirty가 4096이기 때문에 4096보다 작은 오브젝트들은 성공적으로 방어할 수 없다. 이 문제를 해결하기 위해서 Data-flow에 대한 분석을 하여서, 시스템이 사용하는 Memory allocator를 분리 시켰다.
Dynamic Analysis
Rust처럼 Static analysis를 수행하는 것이 아니라 Runtime에 Metadata를 가지고 수행하는 Dynamic analysis를 수행하였다. 모든 Memory allocation은 우선 LLVM에 의해서 메타데이터에, Unique한 AllocID를 통해서 소프트웨어의 어떤 지점에서 Allocation이 수행되었는지를 기록한다. 실제 Allocation이 일어날때, Allocator는 이 메타데이터에 Address base, size를 추가적으로 기록한다. Trusted zone에서 기록된 Memory들은 Unsafe에서는 PKS를 통해서 Page fault를 발생시키고, Page fault handler에서는 접근이 Valid한지를 검증해서 (즉 Allocation할때 사용한 size에 합치하는지를 검사), 합치한다면 Log에 기록을 남기고 현재 instruction다음에 trap instruction을 두어서, 그 인스트럭션만 PKU를 끄고 접근하게 해주고, SIGTRAP핸들러에서 다시 PKU를 켜서 접근을 하게 해준다. 만약 합치하지 않는다면 Log에 기록을 남기고 프로그램을 종료한다.

Result

  1. 마이크로 벤치마크는 최대 8배 느려지며 매크로 밴치에서는 큰 Overhead를 보이지 않았다. (최대 30.75%의 성능감소가 있었음)

Personal criticizing for research purposes (Can be wrong)

  1. Control flow integrity에 대한 고려가 있다면 Overhead가 커질것이며, CFI체크가 없으면 임의의 malicious한 user가 attack을 할 수도 있을 것이다.
  2. Intel MPK는 16개의 key limitation이 존재하는데, 이에 대한 해결방법을 다룬 reference가 없다.
  3. API Access control과 Data control flow에 대한 고려가 없기 때문에, 잘못된 API호출이나, Data flow에 의해서 시스템에 공격을 가할 수도 있다.
  4. PKS page fault에 해당하는 Overhead가 매우 클것 같은데, 반복해서 PKS로 보호받는 Memory에 접근하는 경우 Overhead가 어떻게 되는지 궁금하다.

Reference