Code-Pointer Integrity

Fast and precise control-flow hijack protection

by Volodymyr Kuznetsov, Laszlo Szekeres, Mathias Payer, George Candea, R. Sekar, Dawn Song
OSDI’14: Paper | Slides (Keynote) | Slides (PDF) | Video | Source Code (outdated)
IEEE S&P’15: Poster Abstract | “Missing the pointer” paper | COOP attack | Proof-of-concept SFI prototype
Source Code: SafeStack in LLVM | CPI on Github

Code-Pointer Integrity (CPI) is a property of C/C++ programs that guarantees absence of control-flow hijack attacks by requiring integrity of all direct and indirect pointers to code. Code-Pointer Separation (CPS) is a simplified version of CPI that provides strong protection against such attacks in practice. SafeStack is a component of CPI/CPS, which can be used independently and protects against stack-based control-flow hijacks.

CPI/CPS/SafeStack can be automatically enforced for C/C++ programs through compile-time instrumentation with low performance overheads of 8.5% / 1.9% / 0.05% correspondingly. The SafeStack enforcement mechanism is now part of the Clang compiler, while CPI and CPS are available as research prototypes.

Frequently Asked Questions

Why should I care about control-flow hijack attacks ?

Control-flow hijack attacks are particularly dangerous ones: they typically enable an attacker to execute arbitrary computation and gain full control over the target system. Unfortunately, as demonstrated on the graph below, the number of such attacks is steadily increasing over the last 10 years. And this graph only shows unique vulnerabilities, each of which might be used by hundreds of exploits!

Does CPI/CPS actually solve the control-flow hijack attacks problem, or is it just another step in the arms race ?

CPI provides full protection against all present or future control-flow hijack attacks caused by memory corruption errors, and our paper sketches a formal proof of it. CPS provides strong and practical protection against such attacks, which prevents all existing control-flow hijack attacks; we believe that constructing attacks against CPS might be impractical in most cases.

How does CPI/CPS compare to other protection mechamisms ?

To our knowledge, CPI is the first mechanism that prevents all control-flow hijack attacks while imposing only 8.4% performance overhead; CPS is the first strong and practical protection mechanism against such attacks that has the overhead below 2%. The graph below shows average performance overheads of various protection mechanism on the y axis, and approximately illustrates the relative strengths of corresponding protection guarantees on the x axis. Full memory safety provides protection beyond control-flow hijack attack, but it simply won’t fit on the graph below as its overhead is 119%. Please refer to our paper for more detailed analysis and comparison.

What is the overhead of CPI/CPS/SafeStack ?

The following graph shows CPI/CPS/SafeStack overhead on SPEC2006 benchmarks. We also benchmarked it using Phoronix benchmarks suite which, being less CPU intensive than SPEC, shows even better results - which you can find in our paper.

Will CPI/CPS/SafeStack work on my program ?

SafeStack will likely work for your program unless it performs low-level operations on stack pointer, which might need patching. In fact, our SafeStack patches are currently under review for inclusion into upstream LLVM, and FreeBSD developers consider providing SafeStack support out of the box. CPS will likely work with some effort, and we’re working to make it as stable as the SafeStack right now. CPI is still in a research prototype state, we will work on making it production ready in the near future. We would greatly appreciate any help!

Where can I get CPI/CPS/SafeStack ?

SafeStack is now a part of the Clang compiler, please refer to its documentation for more information. We maintain SafeStack patches for FreeBSD and some other open-source software on our GitHub repository.

CPI and CPS are only available as an early technology preview for now, we plan to add the to our GitHub repo in the near future.

How does Levee work ?

In order to enforce CPI property, Levee identifies all code pointers (e.g., function pointers, return addresses, etc.) in a program and all pointers that might refer to code pointers indirectly. Levee than rewrites the program so that it stores all such sensitive pointers in an isolated memory area. Levee further insures that all memory accesses in the program that might access such sensitive pointers are instrumented with memory safety checks, and that all other accesses can never access sensitive pointers, even if corrupt. Since the latter accesses do not incur any performance overhead, and the former memory accesses are rather rare, the precise memory safety for sensitive pointers is ensured with low overall performance overhead. For more details, please read our paper.

Does “Missing the point(er)” paper demonstrate that CPI is broken ?

No. This paper presents an attack on one of multiple available implementations of CPI. Please find the detailed explanation in our writeup on the topic.

Is CPI or CPS vulnerable to the COOP attacks ?

No, both CPI and CPS protect programs against the COOP attack. This attack injects C++ objects into the program address space, setting the virtual table pointers for such objects to point to the virtual tables that the attacker chooses. Both CPI and CPS protect virtual table pointers, which prevents an attacker from modifying them at will.