Features Download

From: Volodymyr Kuznetsov <vova.kuznetsov <at> epfl.ch>
Subject: [PATCH] Protection against stack-based memory corruption errors using SafeStack
Newsgroups: gmane.comp.compilers.llvm.devel
Date: Monday 3rd November 2014 16:05:49 UTC (over 4 years ago)
Dear LLVM developers,

Our team has developed an LLVM-based protection mechanism that (i) prevents
control-flow hijack attacks enabled by memory corruption errors and (ii)
has very low performance overhead. We would like to contribute the
implementation to LLVM. We presented this work at the OSDI 2014 conference,
at several software companies, and several US universities. We received
positive feedback, and so we've open-sourced our prototype available for
download from our project website (http://levee.epfl.ch).

There are three components (safe stack, CPS, and CPI), and each can be used
individually. Our most stable part is the safe stack instrumentation, which
separates the program stack into a safe stack, which stores return
addresses, register spills, and local variables that are
statically verified to be accessed in a safe way, and the unsafe stack,
which stores everything else. Such separation makes it much harder for an
attacker to corrupt objects on the safe stack, including function pointers
stored in spilled registers and return addresses. A detailed description of
the individual components is available in our OSDI paper on code-pointer
integrity (http://dslab.epfl.ch/pubs/cpi.pdf).

The overhead of our implementation of the safe stack is very close to zero
(0.01% on the Phoronix benchmarks and 0.03% on SPEC2006 CPU on average).
This is lower than the overhead of stack cookies, which are supported by
LLVM and are commonly used today, yet the security guarantees of the safe
stack are strictly stronger than stack cookies. In some cases, the safe
stack improves performance due to better cache locality.

Our current implementation of the safe stack is stable and robust, we used
it to recompile multiple projects on Linux including Chromium, and we also
recompiled the entire FreeBSD user-space system and more than 100 packages.
We ran unit tests on the FreeBSD system and many of the packages and
observed no errors caused by the safe stack. The safe stack is also fully
binary compatible with non-instrumented code and can be applied to parts of
a program selectively.

We attach our implementation of the safe stack as three patches
against current SVN HEAD of LLVM (r221153), clang (r221154) and compiler-rt
(r220991). The same changes are also available on
https://github.com/cpi-llvm <http://github.com/cpi-llvm> in the
safestack-r221153 branches of corresponding repositories. The patches make
the following changes:

-- Add the safestack function attribute, similar to the ssp, sspstrong and
sspreq attributes.
-- Add the SafeStack instrumentation pass that applies the safe stack to
all functions that have the safestack attribute. This pass moves all unsafe
local variables to the unsafe stack with a separate stack pointer, whereas
all safe variables remain on the regular stack that is managed by LLVM as
-- Invoke the pass as the last stage before code generation (at the same
time the existing cookie-based stack protector pass is invoked).
-- Add -fsafe-stack and -fno-safe-stack options to clang to control safe
stack usage (the safe stack is disabled by default).
-- Add __attribute__((no_safe_stack)) attribute to clang that can be used
to disable the safe stack for individual functions even when enabled
-- Add basic runtime support for the safe stack to compiler-rt. The runtime
manages unsafe stack allocation/deallocation for each thread.
-- Add unit tests for the safe stack.

You can find more information about the safe stack, as well as other parts
of or control-flow hijack protection technique in our OSDI paper. FYI here
is the abstract of the paper:

<< Systems code is often written in low-level languages like C/C++,
which offer many benefits but also delegate memory management
to programmers. This invites memory safety bugs that attackers can exploit
to divert control flow and compromise the system. Deployed defense
mechanisms (e.g., ASLR, DEP) are incomplete, and stronger defense
mechanisms (e.g., CFI) often have high overhead and limited guarantees.

We introduce code-pointer integrity (CPI), a new design point
that guarantees the integrity of all code pointers in a program
(e.g., function pointers, saved return addresses) and thereby prevents
all control-flow hijack attacks, including return-oriented programming.
We also introduce code-pointer separation (CPS), a relaxation of CPI
with better performance properties. CPI and CPS offer substantially
better security-to-overhead ratios than the state of the art, they
are practical (we protect a complete FreeBSD system and over 100
packages like apache and postgresql), effective (prevent all attacks in
the RIPE benchmark), and efficient: on SPEC CPU2006, CPS averages
1.2% overhead for C and 1.9% for C/C++, while CPI’s overhead is 2.9% for
C and 8.4% for C/C++. >>

(This is joint work with V. Kuznetsov, L. Szekeres, M. Payer, G. Candea, R.
Sekar, and D. Song)

We look forward to your feedback and hope for a prompt merge into LLVM, to
make the software built with clang more secure.

- Volodymyr Kuznetsov & the CPI team
CD: 6ms