code-pointer integrity (cpi)

18
This paper is included in the Proceedings of the 11th USENIX Symposium on Operating Systems Design and Implementation. October 6–8, 2014 • Broomfield, CO 978-1-931971-16-4 Open access to the Proceedings of the 11th USENIX Symposium on Operating Systems Design and Implementation is sponsored by USENIX. Code-Pointer Integrity Volodymyr Kuznetsov, École Polytechnique Fédérale de Lausanne (EPFL); László Szekeres, Stony Brook University; Mathias Payer, Purdue University; George Candea, École Polytechnique Fédérale de Lausanne (EPFL); R. Sekar, Stony Brook University; Dawn Song, University of California, Berkeley https://www.usenix.org/conference/osdi14/technical-sessions/presentation/kuznetsov

Upload: vanhanh

Post on 08-Jan-2017

235 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: code-pointer integrity (CPI)

This paper is included in the Proceedings of the 11th USENIX Symposium on

Operating Systems Design and Implementation.October 6–8, 2014 • Broomfield, CO

978-1-931971-16-4

Open access to the Proceedings of the 11th USENIX Symposium on Operating Systems

Design and Implementation is sponsored by USENIX.

Code-Pointer IntegrityVolodymyr Kuznetsov, École Polytechnique Fédérale de Lausanne (EPFL);

László Szekeres, Stony Brook University; Mathias Payer, Purdue University; George Candea, École Polytechnique Fédérale de Lausanne (EPFL);

R. Sekar, Stony Brook University; Dawn Song, University of California, Berkeley

https://www.usenix.org/conference/osdi14/technical-sessions/presentation/kuznetsov

Page 2: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 147

Code-Pointer Integrity

Volodymyr Kuznetsov∗, Laszlo Szekeres‡, Mathias Payer†,§

George Candea∗, R. Sekar‡, Dawn Song†

∗Ecole Polytechnique Federale de Lausanne (EPFL),†UC Berkeley, ‡Stony Brook University, §Purdue University

AbstractSystems code is often written in low-level languages

like C/C++, which offer many benefits but also dele-gate memory management to programmers. This invitesmemory safety bugs that attackers can exploit to divertcontrol flow and compromise the system. Deployed de-fense mechanisms (e.g., ASLR, DEP) are incomplete,and stronger defense mechanisms (e.g., CFI) often havehigh overhead and limited guarantees [19, 15, 9].

We introduce code-pointer integrity (CPI), a new de-sign point that guarantees the integrity of all code point-ers in a program (e.g., function pointers, saved return ad-dresses) and thereby prevents all control-flow hijack at-tacks, including return-oriented programming. We alsointroduce code-pointer separation (CPS), a relaxation ofCPI with better performance properties. CPI and CPSoffer substantially better security-to-overhead ratios thanthe state of the art, they are practical (we protect acomplete FreeBSD system and over 100 packages likeapache and postgresql), effective (prevent all attacks inthe 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++.

A prototype implementation of CPI and CPS can beobtained from http://levee.epfl.ch.

1 IntroductionSystems code is often written in memory-unsafe lan-guages; this makes it prone to memory errors that arethe primary attack vector to subvert systems. Attackersexploit bugs, such as buffer overflows and use after freeerrors, to cause memory corruption that enables them tosteal sensitive data or execute code that gives them con-trol over a remote system [44, 37, 12, 8].

Our goal is to secure systems code against all control-flow hijack attacks, which is how attackers gain remotecontrol of victim systems. Low-level languages likeC/C++ offer many benefits to system programmers, andwe want to make these languages safe to use while pre-serving their benefits, not the least of which is perfor-mance. Before expecting any security guarantees fromsystems we must first secure their building blocks.

There exist a few protection mechanism that can re-duce the risk of control-flow hijack attacks without im-posing undue overheads. Data Execution Prevention(DEP) [48] uses memory page protection to prevent theintroduction of new executable code into a running appli-cation. Unfortunately, DEP is defeated by code reuse at-tacks, such as return-to-libc [37] and return oriented pro-gramming (ROP) [44, 8], which can construct arbitraryTuring-complete computations by chaining together ex-isting code fragments of the original application. Ad-dress Space Layout Randomization (ASLR) [40] placescode and data segments at random addresses, making itharder for attackers to reuse existing code for execution.Alas, ASLR is defeated by pointer leaks, side channelsattacks [22], and just-in-time code reuse attacks [45]. Fi-nally, stack cookies [14] protect return addresses on thestack, but only against continuous buffer overflows.

Many defenses can improve upon these shortcomingsbut have not seen wide adoption because of the overheadsthey impose. According to a recent survey [46], these so-lutions are incomplete and bypassable via sophisticatedattacks and/or require source code modifications and/orincur high performance overhead. These approaches typ-ically employ language modifications [25, 36], compilermodifications [13, 3, 17, 34, 43], or rewrite machine codebinaries [38, 54, 53]. Control-flow integrity protection(CFI) [1, 29, 53, 54, 39], a widely studied technique forpractical protection against control-flow hijack attacks,was recently demonstrated to be ineffective [19, 15, 9].

Existing techniques cannot both guarantee protectionagainst control-flow hijacks and impose low overheadand no changes to how the programmer writes code. Forexample, memory-safe languages guarantee that a mem-ory object can only be accessed using pointers prop-erly based on that specific object, which in turn makescontrol-flow hijacks impossible, but this approach re-quires runtime checks to verify the temporal and spatialcorrectness of pointer computations, which inevitablyinduces undue overhead, especially when retrofitted tomemory-unsafe languages. For example, state-of-the-artmemory safety implementations for C/C+ incur ≥ 2×

1

Page 3: code-pointer integrity (CPI)

148 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

overhead [35]. We observe that, in order to rendercontrol-flow hijacks impossible, it is sufficient to guaran-tee the integrity of code pointers, i.e., those that are usedto determine the targets of indirect control-flow transfers(indirect calls, indirect jumps, or returns).

This paper introduces code-pointer integrity (CPI), away to enforce precise, deterministic memory safety forall code pointers in a program. The key idea is to splitprocess memory into a safe region and a regular region.CPI uses static analysis to identify the set of memory ob-jects that must be protected in order to guarantee memorysafety for code pointers. This set includes all memoryobjects that contain code pointers and all data pointersused to access code pointers indirectly. All objects in theset are then stored in the safe region, and the region isisolated from the rest of the address space (e.g., via hard-ware protection). The safe region can only be accessedvia memory operations that are proven at compile timeto be safe or that are safety-checked at runtime. The reg-ular region is just like normal process memory: it can beaccessed without runtime checks and, thus, with no over-head. In typical programs, the accesses to the safe regionrepresent only a small fraction of all memory accesses(6.5% of all pointer operations in SPEC CPU2006 needprotection). Existing memory safety techniques cannotefficiently protect only a subset of memory objects in aprogram, rather they require instrumenting all potentiallydangerous pointer operations.

CPI fully protects the program against all control-flowhijack attacks that exploit program memory bugs. CPIrequires no changes to how programmers write code,since it automatically instruments pointer accesses atcompile time. CPI achieves low overhead by selectivelyinstrumenting only those pointer accesses that are neces-sary and sufficient to formally guarantee the integrity ofall code pointers. The CPI approach can also be used fordata, e.g., to selectively protect sensitive information likethe process UIDs in a kernel.

We also introduce code-pointer separation (CPS), a re-laxed variant of CPI that is better suited for code withabundant virtual function pointers. In CPS, all codepointers are placed in the safe region, but pointers used toaccess code pointers indirectly are left in the regular re-gion (such as pointers to C++ objects that contain virtualfunctions). Unlike CPI, CPS may allow certain control-flow hijack attacks, but it still offers stronger guaranteesthan CFI and incurs negligible overhead.

Our experimental evaluation shows that our proposedapproach imposes sufficiently low overhead to be de-ployable in production. For example, CPS incurs anaverage overhead of 1.2% on the C programs in SPECCPU2006 and 1.9% for all C/C++ programs. CPI incurson average 2.9% overhead for the C programs and 8.4%across all C/C++ SPEC CPU2006 programs. CPI and

CPS are effective: they prevent 100% of the attacks in theRIPE benchmark and the recent attacks [19, 15, 9] thatbypass CFI, ASLR, DEP, and all other Microsoft Win-dows protections. We compile and run with CPI/CPS acomplete FreeBSD distribution along with ≥ 100 widelyused packages, demonstrating that the approach is prac-tical. This paper makes the following contributions:

1. Definition of two new program properties that of-fer a security-benefit to enforcement-cost ratio su-perior to the state of the art: code-pointer in-tegrity (CPI) guarantees control flow cannot be hi-jacked via memory bugs, and code-pointer sepa-ration (CPS) provides stronger security guaranteesthan control-flow integrity but at negligible cost.

2. An efficient compiler-based implementation of CPIand CPS for unmodified C/C++ code.

3. The first practical and complete OS distribution(based on FreeBSD) with full protection built-inagainst control-flow hijack attacks.

In the rest of the paper we introduce our threatmodel (§2), describe CPI and CPS (§3), present our im-plementation (§4), evaluate our approach (§5), discussrelated work (§6), and conclude (§7). We formalize theCPI enforcement mechanism and provide a sketch of itscorrectness proof in Appendix A.

2 Threat Model

This paper is concerned solely with control-flow hijackattacks, namely ones that give the attacker control of theinstruction pointer. The purpose of this type of attack isto divert control flow to a location that would not oth-erwise be reachable in that same context, had the pro-gram not been compromised. Examples of such attacksinclude forcing a program to jump (i) to a location wherethe attacker injected shell code, (ii) to the start of a chainof return-oriented program fragments (“gadgets”), or (iii)to a function that performs an undesirable action in thegiven context, such as calling system() with attacker-supplied arguments. Data-only attacks, i.e., that modifyor leak unprotected non-control data, are out of scope.

We assume powerful yet realistic attacker capabilities:full control over process memory, but no ability to mod-ify the code segment. Attackers can carry out arbitrarymemory reads and writes by exploiting input-controlledmemory corruption errors in the program. They can-not modify the code segment, because the correspondingpages are marked read-executable and not writable, andthey cannot control the program loading process. Theseassumptions ensure the integrity of the original programcode instrumented at compile time, and enable the pro-gram loader to safely set up the isolation between thesafe and regular memory regions. Our assumptions areconsistent with prior work in this area.

2

Page 4: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 149

3 DesignWe now present the terminology used to describe ourdesign, then define the code-pointer integrity prop-erty (§3.1), describe the corresponding enforcementmechanism (§3.2), and define a relaxed version thattrades some security guarantees for performance (§3.3).We further formalize the CPI enforcement mechanismand sketch its correctness proof in Appendix A.

We say a pointer dereference is safe iff the memory itaccesses lies within the target object on which the deref-erenced pointer is based. A target object can either be amemory object or a control flow destination. By pointerdereference we mean accessing the memory targeted bythe pointer, either to read/write it (for data pointers) orto transfer control flow to its location (for code pointers).A memory object is a language-specific unit of memoryallocation, such as a global or local variable, a dynami-cally allocated memory block, or a sub-object of a largermemory object (e.g., a field in a struct). Memory objectscan also be program-specific, e.g., when using custommemory allocators. A control flow destination is a loca-tion in the code, such as the start of a function or a returnlocation. A target object always has a well defined life-time; for example, freeing an array and allocating a newone with the same address creates a different object.

We say a pointer is based on a target object X iff thepointer is obtained at runtime by (i) allocating X on theheap, (ii) explicitly taking the address of X , if X is allo-cated statically, such as a local or global variable, or is acontrol flow target (including return locations, whose ad-dresses are implicitly taken and stored on the stack whencalling a function), (iii) taking the address of a sub-objecty of X (e.g., a field in the X struct), or (iv) computinga pointer expression (e.g., pointer arithmetic, array in-dexing, or simply copying a pointer) involving operandsthat are either themselves based on object X or are notpointers. This is slightly stricter version of C99’s “basedon” definition: we ensure that each pointer is based on atmost one object.

The execution of a program is memory-safe iff allpointer dereferences in the execution are safe. A pro-gram is memory-safe iff all its possible executions (forall inputs) are memory-safe. This definition is consis-tent with the state of the art for C/C++, such as Soft-Bounds+CETS [34, 35]. Precise memory safety enforce-ment [34, 36, 25] tracks the based-on information foreach pointer in a program, to check the safety of eachpointer dereference according to the definition above; thedetection of an unsafe dereference aborts the program.

3.1 The Code-Pointer Integrity (CPI) Property

A program execution satisfies the code-pointer integrityproperty iff all its dereferences that either dereference oraccess sensitive pointers are safe. Sensitive pointers are

Figure 1: CPI protects code pointers 3 and 4 and pointers 1 and2 (which may access pointers 3 and 4 indirectly). Pointer 2 oftype void* may point to different objects at different times. Theint* pointer 5 and non-pointer data locations are not protected.

code pointers and pointers that may later be used to ac-cess sensitive pointers. Note that the sensitive pointerdefinition is recursive, as illustrated in Fig. 1. Accordingto case (iv) of the based-on definition above, dereferenc-ing a pointer to a pointer will correspondingly propagatethe based-on information; e.g., an expression *p = &qcopies the result of &q, which is a pointer based on q,to a location pointed to by p, and associates the based-on metadata with that location. Hence, the integrity ofthe based-on metadata associated with sensitive pointersrequires that pointers used to update sensitive pointersbe sensitive as well (we discuss implications of relaxingthis definition in §3.3). The notion of a sensitive pointeris dynamic. For example, a void* pointer 2 in Fig. 1 issensitive when it points at another sensitive pointer at runtime, but it is not sensitive when it points to an integer.

A memory-safe program execution trivially satisfiesthe CPI property, but memory-safety instrumentationtypically has high runtime overhead, e.g., ≥ 2× in state-of-the-art implementations [35]. Our observation isthat only a small subset of all pointers are responsiblefor making control-flow transfers, and so, by enforc-ing memory safety only for control-sensitive data (andthus incurring no overhead for all other data), we ob-tain important security guarantees while keeping the costof enforcement low. This is analogous to the control-plane/data-plane separation in network routers and mod-ern servers [5], with CPI ensuring the safety of data thatinfluences, directly or indirectly, the control plane.

Determining precisely the set of pointers that aresensitive can only be done at run time. However,the CPI property can still be enforced using any over-approximation of this set, and such over-approximationscan be obtained at compile time, using static analysis.

3.2 The CPI Enforcement Mechanism

We now describe a way to retrofit the CPI property intoa program P using a combination of static instrumenta-tion and runtime support. Our approach consists of astatic analysis pass that identifies all sensitive pointers inP and all instructions that operate on them (§3.2.1), aninstrumentation pass that rewrites P to “protect” all sen-sitive pointers, i.e., store them in a separate, safe memoryregion and associate, propagate, and check their based-

3

Page 5: code-pointer integrity (CPI)

150 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

on metadata (§3.2.2), and an instruction-level isolationmechanism that prevents non-protected memory opera-tions from accessing the safe region (§3.2.3). For perfor-mance reasons, we handle return addresses stored on thestack separately from the rest of the code pointers usinga safe stack mechanism (§3.2.4).

3.2.1 CPI Static Analysis

We determine the set of sensitive pointers using type-based static analysis: a pointer is sensitive if its typeis sensitive. Sensitive types are: pointers to functions,pointers to sensitive types, pointers to composite types(such as struct or array) that contains one or more mem-bers of sensitive types, or universal pointers (i.e., void*,char* and opaque pointers to forward-declared structsor classes). A programmer could additionally indicate,if desired, other types to be considered sensitive, suchas struct ucred used in the FreeBSD kernel to store pro-cess UIDs and jail information. All code pointers that acompiler or runtime creates implicitly (such as return ad-dresses, C++ virtual table pointers, and setjmp buffers)are sensitive as well.

Once the set of sensitive pointers is determined, weuse static analysis to find all program instructions thatmanipulate these pointers. These instructions includepointer dereferences, pointer arithmetic, and memory(de-)allocation operations that calls to either (i) corre-sponding standard library functions, (ii) C++ new/deleteoperators, or (iii) manually annotated custom allocators.

The derived set of sensitive pointers is over-approximate: it may include universal pointers that neverend up pointing to sensitive values at runtime. For in-stance, the C/C++ standard allows char* pointers to pointto objects of any type, but such pointers are also used forC strings. As a heuristic, we assume that char* pointersthat are passed to the standard libc string manipulationfunctions or that are assigned to point to string constantsare not universal. Neither the over-approximation nor thechar* heuristic affect the security guarantees provided byCPI: over-approximation merely introduces extra over-head, while heuristic errors may result in false violationreports (though we never observed any in practice).

Memory manipulation functions from libc, such asmemset or memcpy, could introduce a lot of overhead inCPI: they take void* arguments, so a libc compiled withCPI would instrument all accesses inside the functions,regardless of whether they are operating on sensitive dataor not. CPI’s static analysis instead detects such cases byanalyzing the real types of the arguments prior to beingcast to void*, and the subsequent instrumentation passhandles them separately using type-specific versions ofthe corresponding memory manipulation functions.

We augmented type-based static analysis with a data-flow analysis that handles most practical cases of unsafe

pointer casts and casts between pointers and integers. Ifa value v is ever cast to a sensitive pointer type withinthe function being analyzed, or is passed as an argumentor returned to another function where it is cast to a sen-sitive pointer, the analysis considers v to be sensitive aswell. This analysis may fail when the data flow betweenv and its cast to a sensitive pointer type cannot be fully re-covered statically, which might cause false violation re-ports (we have not observed any during our evaluation).Such casts are a common problem for all pointer-basedmemory safety mechanisms for C/C++ that do not re-quire source code modifications [34].

A key benefit of CPI is its selectivity: the number ofpointer operations deemed to be sensitive is a small frac-tion of all pointer operations in a program. As we showin §5, for SPEC CPU2006, the CPI type-based analy-sis identifies for instrumentation 6.5% of all pointer ac-cesses; this translates into a reduction of performanceoverhead of 16 – 44× relative to full memory safety.

Nevertheless, we still think CPI can benefit from moresophisticated analyses. CPI can leverage any kind ofpoints-to static analysis, as long as it provides an over-approximate set of sensitive pointers. For instance, whenextending CPI to also protect select non-code-pointerdata, we think DSA [27, 28] could prove more effective.

3.2.2 CPI Instrumentation

CPI instruments a program in order to (i) ensure that allsensitive pointers are stored in a safe region, (ii) createand propagate metadata for such pointers at runtime, and(iii) check the metadata on dereferences of such pointers.

In terms of memory layout, CPI introduces a safe re-gion in addition to the regular memory region (Fig. 2).Storage space for sensitive pointers is allocated in boththe safe region (the safe pointer store) and the regularregion (as usual); one of the two copies always remainsunused. This is necessary for universal pointers (e.g.,void*), which could be stored in either region depend-ing on whether they are sensitive at run time or not, andalso helps to avoid some compatibility issues that arisefrom the change in memory layout. The address in regu-lar memory is used as an offset to to look up the value ofa sensitive pointer in the safe pointer store.

The safe pointer store maps the address &p of sensi-tive pointer p, as allocated in the regular region, to thevalue of p and associated metadata. The metadata for pdescribes the target object on which p is based: lowerand upper address bounds of the object, and a temporalid (see Fig. 2). The layout of the safe pointer store issimilar to metadata storage in SoftBounds+CETS [35],except that CPI also stores the value of p in the safepointer store. Combined with the isolation of the safe re-gion (§3.2.3), this allows CPI to guarantee full memorysafety of all sensitive pointers without having to instru-

4

Page 6: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 151

Code (RX) Heap (RW)

T1'

Unsafe Stacks (RW)

Safe RegionSafe Stacks (RW)Safe Pointer Store (RW)

value | upper | lower | id

Regular Region

T2' T3' T1 T2 T3struct A

Figure 2: CPI memory layout: The safe region contains thesafe pointer store and the safe stacks. The location of a sensitivepointer on the left (shaded) remains unused, while the value ofthis pointer and its metadata are stored in the safe pointer store.The safe stacks T1,T2,T3 have corresponding stacks T ′

1,T′

2,T′

3in regular memory to allocate unsafe stack objects.

ment all pointer operations.The instrumentation step changes instructions that op-

erate on sensitive pointers, as found by CPI’s static anal-ysis, to create and propagate the metadata directly fol-lowing the based-on definition in §3.1. Instructions thatexplicitly take addresses of a statically allocated memoryobject or a function, allocate a new object on the heap, ortake an address of a sub-object are instrumented to createmetadata that describe the corresponding object. Instruc-tions that compute pointer expressions are instrumentedto propagate the metadata accordingly. Instructions thatload or store sensitive pointers to memory are replacedwith CPI intrinsic instructions (§3.2.3) that load or storeboth the pointer values and their metadata from/to thesafe pointer store. In principle, call and return instruc-tions also store and load code pointers, and so wouldneed to be instrumented, but we instead protect returnaddresses using a safe stack (§3.2.4).

Every dereference of a sensitive pointer is instru-mented to check at runtime whether it is safe, using themetadata associated with the pointer being dereferenced.Together with the restricted access to the safe region, thisresults in precise memory safety for all sensitive pointers.

Universal pointers (void* and char*) are stored in ei-ther the safe pointer store or the regular region, de-pending on whether they are sensitive at runtime or not.CPI instruments instructions that cast from non-sensitiveto universal pointer types to assign special “invalid”metadata (e.g., with lower bound greater than the upperbound) for the resulting universal pointers. These point-ers, as a result, would never be allowed to access the saferegion. CPI intrinsics for universal pointers would onlystore a pointer in the safe pointer store if it had validmetadata, and only load it from the safe pointer store if itcontained valid metadata for that pointer; otherwise, theywould store/load from the regular region.

CPI can be configured to simultaneously store pro-tected pointers in both the safe pointer store and regu-lar regions, and check whether they match when loadingthem. In this debug mode, CPI detects all attempts to hi-jack control flow using non-protected pointer errors; inthe default mode, such attempts are silently prevented.This debug mode also provides better compatibility withnon-instrumented code that may read protected pointers

(for example, callback addresses) but not write them.Modern compilers contain powerful static analysis

passes that can often prove statically that certain memoryaccesses are always safe. The CPI instrumentation passprecedes compiler optimizations, thus allowing them topotentially optimize away some of the inserted checkswhile preserving the security guarantees.

3.2.3 Isolating the Safe Region

The safe region can only be accessed via CPI intrinsicinstructions, and they properly handle pointer metadataand the safe stack (§3.2.4). The mechanism for achievingthis isolation is architecture-dependent.

On x86-32, we rely on hardware segment protection.We make the safe region accessible through a dedicatedsegment register, which is otherwise unused, and con-figure limits for all other segment registers to make theregion inaccessible through them. The CPI intrinsics arethen turned into code that uses the dedicated register andensures that no other instructions in the program use thatregister. The segment registers are configured by the pro-gram loader, whose integrity we assume in our threatmodel; we also prevent the program from reconfiguringthe segment registers via system calls. None of the pro-grams we evaluated use the segment registers.

On x86-64, CPI relies on the fact that no addressespointing into the safe region are ever stored in the regularregion. This architecture no longer enforces the segmentlimits, however it still provides two segment registerswith configurable base addresses. Similarly to x86-32,we use one of these registers to point to the safe region,however, we choose the base address of the safe regionat random and rely on preventing access to it throughinformation hiding. Unlike classic ASLR though, ourhiding is leak-proof: since the objects in the safe regionare indexed by addresses allocated for them in the regu-lar region, no addresses pointing into the safe region areever stored in regular memory at any time during execu-tion. The 48-bit address space of modern x86-64 CPUsmakes guessing the safe region address impractical, be-cause most failed guessing attempts would crash the pro-gram, and such frequent crashes can easily be detectedby other means.

Other architectures could use randomization-basedprotection as well, or rely on precise software fault isola-tion (SFI) [11]. SFI requires that all memory operationsin a program are instrumented, but the instrumentation islightweight: it could be as small as a single and opera-tion if the safe region occupies the entire upper half ofthe address space of a process. In our experiments, theadditional overhead introduced by SFI was less than 5%.

Since sensitive pointers form a small fraction of alldata stored in memory, the safe pointer store is highlysparse. To save memory, it can be organized as a hash ta-

5

Page 7: code-pointer integrity (CPI)

152 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

ble, a multi-level lookup table, or as a simple array rely-ing on the sparse address space support of the underlyingOS. We implemented and evaluated all three versions,and we discuss the fastest choice in §4.

In the future, we plan to leverage Intel MPX [24] forimplementing the safe region, as described in §4.

3.2.4 The Safe Stack

CPI treats the stack specially, in order to reduce perfor-mance overhead and complexity. This is primarily be-cause the stack hosts values that are accessed frequently,such as return addresses that are code pointers accessedon every function call, as well as spilled registers (tempo-rary values that do not fit in registers and compilers storeon the stack). Furthermore, tracking which of these val-ues will end up at run time in memory (and thus need tobe protected) vs. in registers is difficult, as the compilerdecides which registers to spill only during late stages ofcode generation, long after CPI’s instrumentation pass.

A key observation is that the safety of most accessesto stack objects can be checked statically during com-pilation, hence such accesses require no runtime checksor metadata. Most stack frames contain only memoryobjects that are accessed exclusively within the corre-sponding function and only through the stack pointerregister with a constant offset. We therefore place allsuch proven-safe objects onto a safe stack located in thesafe region. The safe stack can be accessed without anychecks. For functions that have memory objects on theirstack that do require checks (e.g., arrays or objects whoseaddress is passed to other functions), we allocate separatestack frames in the regular memory region. In our expe-rience, less than 25% of functions need such additionalstack frames (see Table 2). Furthermore, this fraction ismuch smaller among short functions, for which the over-head of setting up the extra stack frame is non-negligible.

The safe stack mechanism consists of a static analysispass, an instrumentation pass, and runtime support. Theanalysis pass identifies, for every function, which objectsin its stack frame are guaranteed to be accessed safelyand can thus be placed on the safe stack; return addressesand spilled registers always satisfy this criterion. For theobjects that do not satisfy this criterion, the instrumen-tation pass inserts code that allocates a stack frame forthese objects on the regular stack. The runtime supportallocates regular stacks for each thread and can be imple-mented either as part of the threading library, as we didon FreeBSD, or by intercepting thread create/destroy, aswe did on Linux. CPI stores the regular stack pointer in-side the thread control block, which is pointed to by oneof the segment registers and can thus be accessed with asingle memory read or write.

Our safe stack layout is similar to double stack ap-proaches in ASR [6] and XFI [18], which maintain a

separate stack for arrays and variables whose addressesare taken. However, we use the safe stack to enforcethe CPI property instead of implementing software faultisolation. The safe stack is also comparable to language-based approaches like Cyclone [25] or CCured [36] thatsimply allocate these objects on the heap, but our ap-proach has significantly lower performance overhead.

Compared to a shadow stack like in CFI [1], whichduplicates return instruction pointers outside of the at-tacker’s access, the CPI safe stack presents several ad-vantages: (i) all return instruction pointers and most localvariables are protected, whereas a shadow stack only pro-tects return instruction pointers; (ii) the safe stack is com-patible with uninstrumented code that uses just the regu-lar stack, and it directly supports exceptions, tail calls,and signal handlers; (iii) the safe stack has near-zeroperformance overhead (§5.2), because only a handfulof functions require extra stack frames, while a shadowstack allocates a shadow frame for every function call.

The safe stack can be employed independently fromCPI, and we believe it can replace stack cookies [14]in modern compilers. By providing precise protectionof all return addresses (which are the target of ROP at-tacks today), spilled registers, and some local variables,the safe stack provides substantially stronger securitythan stack cookies, while incurring equal or lower per-formance overhead and deployment complexity.

3.3 Code-Pointer Separation (CPS)

The code-pointer separation property trades some ofCPI’s security guarantees for reduced runtime overhead.This is particularly relevant to C++ programs with manyvirtual functions, where the fraction of sensitive point-ers instrumented by CPI can become high, since everypointer to an object that contains virtual functions is sen-sitive. We found that, on average, CPS reduces overheadby 4.3× (from 8.4% for CPI down to 1.9% for CPS), andin some cases by as much as an order of magnitude.

CPS further restricts the set of protected pointers tocode pointers only, leaving pointers that point to codepointers uninstrumented. We additionally restrict thedefinition of based-on by requiring that a code pointer bebased only on a control flow destination. This restrictionprevents attackers from “forging” a code pointer from avalue of another type, but still allows them to trick theprogram into reading or updating wrong code pointers.

CPS is enforced similarly to CPI, except (i) for thecriteria used to identify sensitive pointers during staticanalysis, and (ii) that CPS does not need any metadata.Control-flow destinations (pointed to by code pointers)do not have bounds, because the pointer value must al-ways match the destination exactly, hence no need forbounds metadata. Furthermore, they are typically static,hence do not need temporal metadata either (there are

6

Page 8: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 153

a few rare exceptions, like unloading a shared library,which are handled separately). This reduces the sizeof the safe region and the number of memory accesseswhen loading or storing code pointers. If the safe regionis organized as a simple array, a CPS-instrumented pro-gram performs essentially the same number of memoryaccesses when loading or storing code pointers as a non-instrumented one; the only difference is that the pointersare being loaded or stored from the safe pointer store in-stead of their original location (universal pointer load orstore instructions still introduce one extra memory accessper such instruction). As a result, CPS can be enforcedwith low performance overhead.

CPS guarantees that (i) code pointers can only bestored to or modified in memory by code pointer storeinstructions, and (ii) code pointers can only be loaded bycode pointer load instructions from memory locations towhich previously a code pointer store instruction storeda value. Combined with the safe stack, CPS preciselyprotects return addresses. CPS is stronger than most CFIimplementations [1, 54, 53], which allow any vulnerableinstruction in a program to modify any code pointer; theyonly check that the value of a code pointer (when used inan indirect control transfer) points to a function definedin a program (for function pointers) or directly followsa call instruction (for return addresses). CPS guarantee(i) above restricts the attack surface, while guarantee (ii)restricts the attacker’s flexibility by limiting the set of lo-cations to which the control can be redirected—the setincludes only entry points of functions whose addresseswere explicitly taken by the program.

To illustrate this difference, consider the case of thePerl interpreter, which implements its opcode dispatchby representing internally a Perl program as a sequenceof function pointers to opcode handlers and then callingin its main execution loop these function pointers oneby one. CFI statically approximates the set of legitimatecontrol-flow targets, which in this case would include allpossible Perl opcodes. CPS however permits only callsthrough function pointers that are actually assigned. Thismeans that a memory bug in a CFI-protected Perl in-terpreter may permit an attacker to divert control flowand execute any Perl opcode, whereas in a CPS-protectedPerl interpreter the attacker could at most execute an op-code that exists in the running Perl program.

CPS provides strong control-flow integrity guaranteesand incurs low overhead (§5). We found that it preventsall recent attacks designed to bypass CFI [19, 15, 9]. Weconsider CPS to be a solid alternative to CPI in thosecases when CPI’s (already low) overhead seems too high.

4 ImplementationWe implemented a CPI/CPS enforcement tool forC/C++, called Levee, on top of the LLVM 3.3 com-

piler infrastructure [30], with modifications to LLVM li-braries, the clang compiler, and the compiler-rt runtime.To use Levee, one just needs to pass additional flags tothe compiler to enable CPI (-fcpi), CPS (-fcps), or safe-stack protection (-fstack-protector-safe). Levee workson unmodified programs and supports Linux, FreeBSD,and Mac OS X in both 32-bit and 64-bit modes.

Levee can be downloaded from the project home-page http://levee.epfl.ch, and we plan to push ourchanges to the upstream LLVM.Analysis and instrumentation passes: We imple-mented the static analysis and instrumentation for CPIas two LLVM passes, directly following the designfrom §3.2.1 and §3.2.2. The LLVM passes operate on theLLVM intermediate representation (IR), which is a low-level strongly-typed language-independent program rep-resentation tailored for static analyses and optimizationpurposes. The LLVM IR is generated from the C/C++source code by clang, which preserves most of the typeinformation that is required by our analysis, with a fewcorner cases. For example, in certain cases, clang doesnot preserve the original types of pointers that are castto void* when passing them as an argument to memsetor similar functions, which is required for the memset-related optimizations discussed in §3.2.2. The IR alsodoes not distinguish between void* and char* (representsboth as i8*), but this information is required for our stringpointers detection heuristic. We augmented clang to al-ways preserve such type information as LLVM metadata.Safe stack instrumentation pass: The safe stack instru-mentation targets functions that contain on-stack mem-ory objects that cannot be put on the safe stack. For suchfunctions, it allocates a stack frame on the unsafe stackand relocates corresponding variables to that frame.

Given that most of the functions do not need an un-safe stack, Levee uses the usual stack pointer (rsp reg-ister on x86-64) as the safe stack pointer, and stores theunsafe stack pointer in the thread control block, which isaccessible directly through one of the segment registers.When needed, the unsafe stack pointer is loaded into anIR local value, and Levee relies on the LLVM registerallocator to pick the register for the unsafe stack pointer.Levee explicitly encodes unsafe stack operations as IRinstructions that manipulate an unsafe stack pointer; itleaves all operations that use a safe stack intact, lettingthe LLVM code generator manage them. Levee performsthese changes as a last step before code generation (di-rectly replacing LLVM’s stack-cookie protection pass),thus ensuring that it operates on the final stack layout.

Certain low-level functions modify the stack pointerdirectly. These functions include setjmp/longjmp andexception handling functions (which store/load the stackpointer), and thread create/destroy functions, which al-locate/free stacks for threads. On FreeBSD we provide

7

Page 9: code-pointer integrity (CPI)

154 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

full-system CPI, so we directly modified these functionsto support the dual stacks. On Linux, our instrumentationpass finds setjmp/longjmp and exception handling func-tions in the program and inserts required instrumentationat their call sites, while thread create/destroy functionsare intercepted and handled by the Levee runtime.

Runtime support library: Most of the instrumentationby the above passes are added as intrinsic function calls,such as cpi ptr store() or cpi memcpy(), which are im-plemented by Levee’s runtime support library (a partof compiler-rt). This design cleanly separates the safepointer store implementation from the instrumentationpass. In order to avoid the overhead associated with ex-tra function calls, we ensure that some of the runtimesupport functions are always inlined. We compile thesefunctions into LLVM bitcode and instruct clang to linkthis bitcode into every object file it compiles. Functionsthat are called rarely (e.g., cpi abort(), called when a CPIviolation is detected) are never inlined, in order to reducethe instruction cache footprint of the instrumentation.

We implemented and benchmarked several versions ofthe safe pointer store map in our runtime support library:a simple array, a two-level lookup table, and a hashtable.The array implementation relies on the sparse addressspace support of the underlying OS. Initially we found itto perform poorly on Linux, due to many page faults (es-pecially at startup) and additional TLB pressure. Switch-ing to superpages (2 MB on Linux) made this simple ta-ble the fastest implementation of the three.

Binary level functionality: Some code pointers in bina-ries are generated by the compiler and/or linker, and can-not be protected on the IR level. Such pointers includethe ones in jump tables, exception handler tables, and theglobal offset table. Bounds checks for the jump tablesand the exception handler tables are already generatedby LLVM anyway, and the tables themselves are placedin read-only memory, hence cannot be overwritten. Werely on the standard loader’s support for read-only globaloffset tables, using the existing RTLD NOW flag.

Limitations: The CPI design described in §3 includesboth spatial and temporal memory safety enforcementfor sensitive pointers, however our current prototype im-plements spatial memory safety only. It can be easilyextended to enforce temporal safety by directly applyingthe technique described in [35] for sensitive pointers.

Levee currently supports Linux, FreeBSD and MacOS user-space applications. We believe Levee can beported to protect OS kernels as well. Related technicalchallenges include integration with the kernel memorymanagement subsystem and handling of inline assembly.

CPI and CPS require instrumenting all code that ma-nipulates sensitive pointers; non-instrumented code cancause unnecessary aborts. Non-instrumented code could

come from external libraries compiled without Levee, in-line assembly, or dynamically generated code. Levee canbe configured to simultaneously store sensitive pointersin both the safe and the regular regions, in which casenon-instrumented code works fine as long as it only readssensitive pointers but doesn’t write them.

Inline assembly and dynamically generated code canstill update sensitive pointers if instrumented with appro-priate calls to the Levee runtime, either manually by aprogrammer or directly by the code generator.

Dynamically generated code (e.g., for JIT compila-tion) poses an additional problem: running the generatedcode requires making writable pages executable, whichviolates our threat model (this is a common problem formost control-flow integrity mechanisms). One solutionis to use hardware or software isolation mechanisms toisolate the code generator from the code it generates.Sensitive data protection: Even though the main focusof CPI is control-flow hijack protection, the same tech-nique can be applied to protect other types of sensitivedata. Levee can treat programmer-annotated data typesas sensitive and protect them just like code pointers. CPIcould also selectively protect individual program vari-ables (as opposed to types), however it would require re-placing the type-based static analysis described in §3.2.1with data-based points-to analysis such as DSA [27, 28].Future MPX-based implementation: Intel announceda hardware extension, Intel MPX, to be used forhardware-enforced memory safety [23]. It is proposed asa testing tool, probably due to the associated overhead;no overhead numbers are available at the time of writing.

We believe MPX (or similar) hardware can be re-purposed to enforce CPI with lower performance over-head than our existing software-only implementation.MPX provides special registers to store bounds alongwith instructions to check them, and a hardware-basedimplementation of a pointer metadata store (analogous tothe safe pointer store in our design), organized as a two-level lookup table. Our implementation can be adaptedto use these facilities once MPX-enabled hardware be-comes available. We believe that a hardware-based CPIimplementation can reduce the overhead of a software-only CPI in much the same way as HardBound [16] orWatchdog [33] reduced the overhead of SoftBound.

Adopting MPX for CPI might require implementingmetadata loading logic in software. Like CPI, MPX alsostores the pointer value together with the metadata. How-ever, being a testing tool, MPX chooses compatibilitywith non-instrumented code over security guarantees: ituses the stored pointer value to check whether the origi-nal pointer was modified by non-instrumented code and,if yes, resets the bounds to [0,∞]. In contrast, CPI’s guar-antees depend on preventing any non-instrumented codefrom ever modifying sensitive pointer values.

8

Page 10: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 155

5 EvaluationIn this section we evaluate Levee’s effectiveness, effi-ciency, and practicality. We experimentally show thatboth CPI and CPS are 100% effective on RIPE, the mostrecent attack benchmark we are aware of (§5.1). We eval-uate the efficiency of CPI, CPS, and the safe stack onSPEC CPU2006, and find average overheads of 8.4%,1.9%, and 0% respectively (§5.2). To demonstrate prac-ticality, we recompile with CPI/CPS/ safe stack the baseFreeBSD plus over 100 packages and report results onseveral benchmarks (§5.3).

We ran our experiments on an Intel Xeon E5-2697with 24 cores @ 2.7GHz in 64-bit mode with 512GBRAM. The SPEC benchmarks ran on an Ubuntu PrecisePangolin (12.04 LTS), and the FreeBSD benchmarks ina KVM-based VM on this same system.

5.1 Effectiveness on the RIPE Benchmark

We described in §3 the security guarantees provided byCPI, CPS, and the safe stack based on their design; toexperimentally evaluate their effectiveness, we use theRIPE [49] benchmark. This is a program with many dif-ferent security vulnerabilities and a set of 850 exploitsthat attempt to perform control-flow hijack attacks on theprogram using various techniques.

Levee deterministically prevents all attacks, both inCPS and CPI mode; when using only the safe stack, itprevents all stack-based attacks. On vanilla Ubuntu 6.06,which has no built-in defense mechanisms, 833–848 ex-ploits succeed when Levee is not used (some succeedprobabilistically, hence the range). On newer systems,fewer exploits succeed, due to built-in protection mech-anisms, changes in the run-time layout, and compatibil-ity issues with the RIPE benchmark. On vanilla Ubuntu13.10, with all protections (DEP, ASLR, stack cookies)disabled, 197–205 exploits succeed. With all protectionsenabled, 43–49 succeed. With CPS or CPI, none do.

The RIPE benchmark only evaluates the effectivenessof preventing existing attacks; as we argued in §3 andaccording to the proof outlined in Appendix A, CPI ren-ders all (known and unknown) memory corruption-basedcontrol-flow hijack attacks impossible.

5.2 Efficiency on SPEC CPU2006 Benchmarks

In this section we evaluate the runtime overhead of CPI,CPS, and the safe stack. We report numbers on all SPECCPU2006 benchmarks written in C and C++ (our pro-totype does not handle Fortran). The results are sum-marized in Table 1 and presented in detail in Fig. 3.We also compare Levee to two related approaches, Soft-Bound [34] and control-flow integrity [1, 54, 53].

CPI performs well for most C benchmarks, however itcan incur higher overhead for programs written in C++.This overhead is caused by abundant use of pointers to

Figure 3: Levee performance for SPEC CPU2006, under threeconfigurations: full CPI, CPS only, and safe stack only.

Safe Stack CPS CPIAverage (C/C++) 0.0% 1.9% 8.4%Median (C/C++) 0.0% 0.4% 0.4%Maximum (C/C++) 4.1% 17.2% 44.2%Average (C only) -0.4% 1.2% 2.9%Median (C only) -0.3% 0.5% 0.7%Maximum (C only) 4.1% 13.3% 16.3%

Table 1: Summary of SPEC CPU2006 performance overheads.

C++ objects that contain virtual function tables—suchpointers are sensitive for CPI, and so all operations onthem are instrumented. Same reason holds for gcc: itembeds function pointers in some of its data structuresand then uses pointers to these structures frequently.

The next-most important source of overhead are libcmemory manipulation functions, like memset and mem-cpy. When our static analysis cannot prove that a callto such a function uses as arguments only pointers tonon-sensitive data, Levee replaces the call with one to acustom version of an equivalent function that checks thesafe pointer store for each updated/copied word, whichintroduces overhead. We expect to remove some of thisoverhead using improved static analysis and heuristics.

CPS averages 1.2–1.8% overhead, and exceeds 5% ononly two benchmarks, omnetpp and perlbench. The for-mer is due to the large number of virtual function callsoccurring at run time, while the latter is caused by aspecific way in which perl implements its opcode dis-patch: it internally represents a program as a sequence offunction pointers to opcode handlers, and its main execu-tion loop calls these function pointers one after the other.Most other interpreters use a switch for opcode dispatch.

Safe stack provided a surprise: in 9 cases (out of

9

Page 11: code-pointer integrity (CPI)

156 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

19), it improves performance instead of hurting it; inone case (namd), the improvement is as high as 4.2%,more than the overhead incurred by CPI and CPS. Thisis because objects that end up being moved to the regular(unsafe) stack are usually large arrays or variables thatare used through multiple stack frames. Moving suchobjects away from the safe stack increases the localityof frequently accessed values on the stack, such as CPUregister values temporarily stored on the stack, return ad-dresses, and small local variables.

The safe stack overhead exceeds 1% in only threecases, perlbench, xalanbmk, and povray. We studiedthe disassembly of the most frequently executed func-tions that use unsafe stack frames in these programs andfound that some of the overhead is caused by inefficienthandling of the unsafe stack pointer by LLVM’s registerallocator. Instead of keeping this pointer in a single regis-ter and using it as a base for all unsafe stack accesses, theprogram keeps moving the unsafe stack pointer betweendifferent registers and often spills it to the (safe) stack.We believe this can be resolved by making the registerallocator algorithm aware of the unsafe stack pointer.

In contrast to the safe stack, stack cookies deployedtoday have an overhead of up to 5%, and offer strictlyweaker protection than our safe stack implementation.

The data structures used for the safe stack and the safememory region result in memory overhead compared toa program without protection. We measure the memoryoverhead when using either a simple array or a hash ta-ble. For SPEC CPU2006 the median memory overheadfor the safe stack is 0.1%; for CPS the overhead is 2.1%for the hash table and 5.6% for the array; and for CPI theoverhead is 13.9% for the hash table and 105% for thearray. We did not optimize the memory overhead yet andbelieve it can be improved in future prototypes.

In Table 2 we show compilation statistics for Levee.The first column shows that only a small fraction of allfunctions require an unsafe stack frame, confirming ourhypothesis from §3.2.4. The other two columns con-firm the key premises behind our approach, namely thatCPI requires much less instrumentation than full memorysafety, and CPS needs much less instrumentation thanCPI. The numbers also correlate with Fig. 3.

Comparison to SoftBound: We compare with Soft-Bound [34] on the SPEC benchmarks. We cannot fairlyreuse the numbers from [34], because they are based onan older version of SPEC. In Table 3 we report numbersfor the four C/C++ SPEC benchmarks that can compilewith the current version of SoftBound. This comparisonconfirms our hypothesis that CPI requires significantlylower overhead compared to full memory safety.

Theoretically, CPI suffers from the same compatibil-ity issues (e.g., handling unsafe pointer casts) as pointer-based memory safety. In practice, such issues arise

Benchmark FNUStack MOCPS MOCPI400 perlbench 15.0% 1.0% 13.8%401 bzip2 27.2% 1.3% 1.9%403 gcc 19.9% 0.3% 6.0%429 mcf 50.0% 0.5% 0.7%433 milc 50.9% 0.1% 0.7%444 namd 75.8% 0.6% 1.1%445 gobmk 10.3% 0.1% 0.4%447 dealII 12.3% 6.6% 13.3%450 soplex 9.5% 4.0% 2.5%453 povray 26.8% 0.8% 4.7%456 hmmer 13.6% 0.2% 2.0%458 sjeng 50.0% 0.1% 0.1%462 libquantum 28.5% 0.4% 2.3%464 h264ref 20.5% 1.5% 2.8%470 lbm 16.6% 0.6% 1.5%471 omnetpp 6.9% 10.5% 36.6%473 astar 9.0% 0.1% 3.2%482 sphinx3 19.7% 0.1% 4.6%483 xalancbmk 17.5% 17.5% 27.1%

Table 2: Compilation statistics for Levee: FNUStack lists whatfraction of functions need an unsafe stack frame; MOCPS andMOCPI show the fraction of memory operations instrumentedfor CPS and CPI, respectively.

Benchmark Safe Stack CPS CPI SoftBound401 bzip2 0.3% 1.2% 2.8% 90.2%447 dealII 0.8% -0.2% 3.7% 60.2%458 sjeng 0.3% 1.8% 2.6% 79.0%464 h264ref 0.9% 5.5% 5.8% 249.4%

Table 3: Overhead of Levee and SoftBound on SPEC programsthat compile and run errors-free with SoftBound.

much less frequently for CPI, because CPI instrumentsmuch fewer pointers. Many of the SPEC benchmarkseither don’t compile or terminate with an error when in-strumeted by SoftBound, which illustrates the practicalimpact of this difference.

Comparison to control-flow integrity (CFI): The av-erage overhead for compiler-enforced CFI is 21% fora subset of the SPEC CPU2000 benchmarks [1] and 5-6% for MCFI [39] (without stack pointer integrity). CC-FIR [53] reports an overhead of 3.6%, and binCFI [54]reports 8.54% for SPEC CPU2006 to enforce a weakCFI property with globally merged target sets. WIT [3],a source-based mechanism that enforces both CFI andwrite integrity protection, has 10% overhead1.

At less than 2%, CPS has the lowest overhead amongall existing CFI solutions, while providing stronger pro-tection guarantees. Also, CPI’s overhead is bested onlyby CCFIR. However, unlike any CFI mechanism, CPIguarantees the impossibility of any control-flow hijackattack based on memory corruptions. In contrast, there

1We were unable to find open-source implementations of compiler-based CFI, so we can only compare to published overhead numbers.

10

Page 12: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 157

Figure 4: Performance overheads on FreeBSD (Phoronix).

exist successful attacks against CFI [19, 15, 9]. Whileneither of these attacks are possible against CPI by con-struction, we found that, in practice, neither of themwould work against CPS either. We further discuss con-ceptual differences between CFI and CPI in §6.

5.3 Case Study: A Safe FreeBSD Distribution

Having shown that Levee is both effective and efficient,we now evaluate the feasibility of using Levee to protectan entire operating system distribution, namely FreeBSD10. We rebuilt the base system—base libraries, devel-opment tools, and services like bind and openssh—plusmore than 100 packages (including apache, postgresql,php, python) in four configurations: CPI, CPS, SafeStack, and vanilla. FreeBSD 10 uses LLVM/clang as itsdefault compiler, while some core components of Linux(e.g., glibc) cannot be built with clang yet. We integratedthe CPI runtime directly into the C library and the thread-ing library. We have not yet ported the runtime to kernelspace, so the OS kernel remained uninstrumented.

We evaluated the performance of the system using thePhoronix test suite [41], a widely used comprehensivebenchmarking platform for operating systems. We chosethe “server” setting and excluded benchmarks markedas unsupported or that do not compile or run on recentFreeBSD versions. All benchmarks that compiled andworked on vanilla FreeBSD also compiled and workedin the CPI, CPS and Safe Stack versions.

Fig. 4 shows the overhead of CPI, CPS and the safe-stack versions compared to the vanilla version. Theresults are consistent with the SPEC results presentedin §5.2. The Phoronix benchmarks exercise large parts ofthe system and some of them are multi-threaded, whichintroduces significant variance in the results, especiallywhen run on modern hardware. As Fig. 4 shows, formany benchmarks the overheads of CPS and the safestack are within the measurement error.

Benchmark Safe Stack CPS CPIStatic page 1.7% 8.9% 16.9%Wsgi test page 1.0% 4.0% 15.3%Dynamic page 1.4% 15.9% 138.8%

Table 4: Throughput benchmark for web server stack(FreeBSD + Apache + SQLite + mod wsgi + Python +Django).

We also evaluated a realistic usage model of theFreeBSD system as a web server. We installed Mezza-nine, a content management system based on Django,which uses Python, SQLite, Apache, and mod wsgi. Weused the Apache ab tool to benchmark the throughput ofthe web server. The results are summarized in Table 4.

The CPI overhead for a dynamic page generated byPython code is much larger then we expected, but con-sistent with suspiciously high overhead of the pybenchbenchmark in Fig. 4. We think it might be caused by theuse of some C constructs in the Python interpreter thatare not yet handled well by our optimization heuristics,e.g., emulating C++ inheritance in C. We believe the per-formance might be improved in this case by extendingthe heuristics to recognize such C constructs.

6 Related WorkA variety of defense mechanisms have been proposed to-date to answer the increasing challenge of control-flowhijack attacks. Fig. 5 compares the design of the differentprotection approaches to our approach.

Enforcing memory safety ensures that no dangling orout-of-bounds pointers can be read or written by the ap-plication, thus preventing the attack in its first step. Cy-clone [25] and CCured [36] extend C with a safe typesystem to enforce memory safety features. These ap-proaches face the problem that there is a large (unported)legacy code base. In contrast, CPI and CPS both workfor unmodified C/C++ code. SoftBound [34] with itsCETS [35] extension enforces complete memory safetyat the cost of 2× – 4× slowdown. Tools with less over-head, like BBC [4], only approximate memory safety.LBC [20] and Address Sanitizer [43] detect continu-ous buffer overflows and (probabilistically) indexing er-rors, but can be bypassed by an attacker who avoids thered zones placed around objects. Write integrity testing(WIT) [3] provides spatial memory safety by restrictingpointer writes according to points-to sets obtained by anover-approximate static analysis (and is therefore limitedby the static analysis). Other techniques [17, 2] enforcetype-safe memory reuse to mitigate attacks that exploittemporal errors (use after frees).

CPI by design enforces spatial and temporal memorysafety for a subset of data (code pointers) in Step 2 ofFig. 5. Our Levee prototype currently enforces spatialmemory safety and may be extended to enforce temporalmemory safety as well (e.g., how CETS extends Soft-

11

Page 13: code-pointer integrity (CPI)

158 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

Attack step Property Mechanism Stops all control-flow hijacks?

Memory Safety SoftBound+CETS [34, 35]BBC [4], LBC [20], ASAN [43], WIT [3]

YesNo: sub-objects, reads not protectedNo: protects red zones onlyNo: over-approximate valid sets

116%110%23%

7%

②Code-Pointer Integrity(this work)

CPI CPSSafe Stack

YesNo: valid code ptrs. interchangeableNo: precise return protection only

8.4%1.9%~0%

Randomization ASLR [40], ASLP [26]PointGuard [13]DSR [6]NOP insertion [21]

No: vulnerable to information leaksNo: vulnerable to information leaksNo: vulnerable to information leaksNo: vulnerable to information leaks

~10%10%20%

2%

Control-Flow Integrity

Stack cookiesCFI [1]WIT (CFI part) [3]DFI [10]

No: probabilistic return protection onlyNo: over-approximate valid setsNo: over-approximate valid setsNo: over-approximate valid sets

~2%20%

7%104%

⑤Non-Executable Data

HW (NX bit)SW (Exec Shield, PaX)

No: code reuse attacksNo: code reuse attacks

0%few %

High-level policies

Sandboxing (SFI)ACLsCapabilities

Isolation onlyIsolation onlyIsolation only

variesvariesvaries

Corrupt data

pointer

Modify a

code pointer …

… to address of

gadget/shellcode

Execute injected

shellcode

Exec. available

gadgets/func.-s

Control-flow

hijack

Use pointer by

indirect call/jump

Use pointer by

return instruction

Avg. overhead

Figure 5: Summary of control-flow hijack defense mechanisms aligned with individual steps that are necessary for a successfulattack. The figure on the left is a simplified version of the complete memory corruption diagram in [46].

Bound). We believe CPI is the first to stop all control-flow hijack attacks at this step.

Randomization techniques, like ASLR [40] andASLP [26], mitigate attacks by restricting the attacker’sknowledge of the memory layout of the application inStep 3. PointGuard [13] and DSR [7] (which is similar toprobabilistic WIT) randomize the data representation byencrypting pointer values, but face compatibility prob-lems. Software diversity [21] allows fine-grained, per-instance code randomization. Randomization techniquesare defeated by information leaks through, e.g., memorycorruption bugs [45] or side channel attacks [22].

Control-flow integrity [1] ensures that the targets ofall indirect control-flow transfers point to valid code lo-cations in Step 4. All CFI solutions rely on staticallypre-computed context-insensitive sets of valid control-flow target locations. Many practical CFI solutions sim-ply include every function in a program in the set ofvalid targets [53, 54, 29, 47]. Even if precise staticanalysis was be feasible, CFI could not guarantee pro-tection against all control-flow hijack attacks, but rathermerely restrict the sets of potential hijack targets. In-deed, recent results [19, 15, 9] show that many exist-ing CFI solutions can be bypassed in a principled way.CFI+SFI [52], Strato [51] and MIPS [38] enforce an evenmore relaxed, statically defined CFI property in order toenforce software-based fault isolation (SFI). CCFI [31]encrypts code pointers in memory and provides secu-rity guarantees close to CPS. Data-flow based techniqueslike data-flow integrity (DFI) [10] or dynamic taint anal-ysis (DTA) [42] can enforce that the used code pointerwas not set by an unrelated instruction or to untrusteddata, respectively. These techniques may miss some at-tacks or cause false positives, and have higher perfor-mance costs than CPI and CPS. Stack cookies, CFI, DFI,and DTA protect control-transfer instructions by detect-

ing illegal modification of the code pointer whenever itis used, while CPI protects the load and store of a codepointer, thus preventing the corruption in the first place.CPI provides precise and provable security guarantees.

In Step 5, the execution of injected code is preventedby enforcing the non-executable (NX) data policy, butcode-reuse attacks remain possible.

High level policies, e.g., restricting the allowed sys-tem calls of an application, limit the power of the at-tacker even in the presence of a successful control-flowhijack attack in Step 6. Software fault isolation (SFI)techniques [32, 18, 11, 50, 52] restrict indirect control-flow transfers and memory accesses to part of the ad-dress space, enforcing a sandbox that contains the attack.SFI prevents an attack from escaping the sandbox and al-lows the enforcement of a high-level policy, while CPIenforces the control-flow inside the application.

7 ConclusionThis paper describes code-pointer integrity (CPI), a wayto protect systems against all control-flow hijacks thatexploit memory bugs, and code-pointer separation, a re-laxed form of CPI that still provides strong guarantees.The key idea is to selectively provide full memory safetyfor just a subset of a program’s pointers, namely codepointers. We implemented our approach and showed thatit is effective, efficient, and practical. Given its advan-tageous security-to-overhead ratio, we believe our ap-proach marks a step toward deterministically secure sys-tems that are fully immune to control-flow hijack attacks.

AcknowledgmentsWe thank the anonymous reviewers and our shepherdJunfeng Yang for their valuable input. We are grate-ful to Martin Abadi, Herbert Bos, Miguel Castro, VijayD’Silva, Ulfar Erlingsson, Johannes Kinder, Per Larsen,Jim Larus, Santosh Nagarakatte, and Jonas Wagner for

12

Page 14: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 159

Atomic Types a ::= int | p∗Pointer Types p ::= a | s | f | voidStruct Types s ::= struct{ . . . ;ai : idi; . . .}

LHS Expressions lhs ::= x | ∗lhs | lhs.id | lhs−>id

RHS Expressions rhs ::= i | & f | rhs+ rhs | lhs | &lhs

| (a) rhs | sizeof(p) | malloc(rhs)

Commands c ::= c;c | lhs = rhs | f () | (∗lhs)()

Figure 6: The subset of C used in Appendix A; x denotes localstatically typed variables, id – structure fields, i – integers, andf – functions from a pre-defined set.

their valuable feedback and discussions on earlier ver-sions of the paper. This work was supported by ERCStarting Grant No. 278656, a Microsoft Research PhDfellowship, a gift from Google, DARPA award HR0011-12-2-005, NSF grants CNS-0831298 and CNS-1319137,and AFOSR FA9550-09-1-0539.

A Formal Model of CPIThis section presents a formal model and operational se-mantics of the CPI property and a sketch of its correct-ness proof. Due to the size and complexity of C/C++specifications, we focus on a small subset of C that illus-trates the most important features of CPI. Due to spacelimitations we focus on spatial memory safety. We buildupon the formalization of spatial memory safety in Soft-Bound [34], reuse the same notation, and extend it tosupport applying spatial memory safety to a subset ofmemory locations. The formalism can be easily extendedto provide temporal memory safety, directly applying theCETS [35] mechanism to the safe memory region of themodel. Fig. 6 gives the syntax rules of the C subset weconsider in this section. All valid programs must alsopass type checking as specified by the C standard.

We define the runtime environment E of a programas a triple (S,Mu,Ms), where S maps variable identifiersto their respective atomic types and addresses, a regu-lar memory Mu maps addresses to values (denoted as vand called regular values), and a safe memory Ms mapsaddresses to values with bounds information (denoted asv(b,e) and called safe values) or a special marker none.The bounds information specifies the lowest (b) and thehighest (e) address of the corresponding memory object.Mu and Ms use the same addressing, but might containdistinct values for the same address. Some locations(e.g., of void∗ type) can store either safe or regular valueand are resolved to either Ms or Mu at runtime.

The runtime provides the usual set of memory oper-ations for Mu and Ms, as summarized in Table 5. Mumodels standard memory, whereas Ms stores values withbounds and has a special marker for “absent” locations,similarly to the memory in SoftBound’s [34] formaliza-tion. We assume the memory operations follow the stan-dard behavior of read/write/malloc operations in all other

Operation Semanticsreadu Mu l return Mu[l]writeu Mu l v set Mu[l] = vreads Ms l return Ms[l], if l is allocated; return none otherwisewrites Ms l v(b,e) set Ms[l] = v(b,e), if l is allocated;

do nothing otherwisewrites Ms l none set Ms[l] = none, if l is allocated;

do nothing otherwisemalloc E i allocate a memory object of size i in both E.Mu and

E.Ms (at the same address); fail when out of memory

Table 5: Memory Operations in CPI

sensitive int ::= false

sensitive void ::= true

sensitive f ::= true

sensitive p∗ ::= sensitive p

sensitive s ::=∨

i∈fields of s

sensitive ai

Figure 7: The decision criterion for protecting types in CPI

respects, e.g., read returns the value previously written tothe same location, malloc allocates a region of memorythat is disjoint with any other allocated region, etc..

Enforcing the CPI property with low performanceoverhead requires placing most variables in Mu, whilestill ensuring that all pointers that require protection atruntime according to the CPI property are placed inMs. In this formalization, we rely on type-based staticanalysis as defined by the sensitive criterion, shownon Fig. 7. We say a type p is sensitive iff sensitive p=true. Setting sensitive to true for all types would makethe CPI operational semantics equivalent to the one pro-vided by SoftBound and would ensure full spatial mem-ory safety of all memory operations in a program.

The classification provided by the sensitive crite-rion is static and only determines which operations in aprogram to instrument. Expressions of sensitive typescould evaluate to both safe or regular values at runtime,whereas expressions of regular types always evaluate toregular values. In particular, according to Fig. 7, void∗is sensitive and, hence, in agreement with the C specifi-cation, values of that type can hold any pointer value atruntime, either safe or regular.

We extend the SoftBound definition of the result of anoperation to differentiate between safe and regular valuesand left-hand-side locations:

Results r ::= v(b,e) | v | ls | lu | OK | OutOfMem | Abort

where v(b,e) and v are the safe (with bounds informa-tion) and, respectively, regular values that result from aright hand side expression, lu and ls are locations that re-sult from a safe and regular left-hand-side expression, OKis a result of a successful command, and OutOfMem andAbort are error codes. We assume that all operational se-mantics rules of the language propagate these error codesup to the end of the program unchanged.

Using the above definitions, we now formalize the op-

13

Page 15: code-pointer integrity (CPI)

160 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

erational semantics of CPI through three classes of rules.The (E, lhs) ⇒l ls : a and (E, lhs) ⇒l lu : a rules spec-ify how left hand side expressions are evaluated to asafe or regular locations, respectively. The (E,rhs) ⇒r(v(b,e),E ′) and (E,rhs)⇒r (v,E ′) rules specify how righthand side expressions are evaluated to safe values withbounds or regular values, respectively, possibly modify-ing the environment through memory allocation (turningit from E to E ′). Finally, the (E,c)⇒c (r,E ′) rules spec-ify how commands are executed, possibly modifying theenvironment, where r can be either OK or an error code.We only present the rules that are most important for theCPI semantics, omitting rules that simply represent thestandard semantics of the C language.

Bounds information is initially assigned when allocat-ing a memory object or when taking a function’s address(both operations always return safe values):

address( f ) = l

(E,& f )⇒r (l(l,l))

(E,rhs) = imalloc E i = (l,E ′)

(E,malloc(i))⇒r (l(l,l+i),E′)

Taking the address of a variable from S if its type issensitive is analogous. Structure field access operationseither narrow bounds information accordingly, or strip itif the type of the accessed field is regular.

Type casting results in a safe value iff a safe value iscast to a sensitive type:

sensitive a′

(E,rhs)⇒l v(b,e) : a

(E,(a′)rhs)⇒r (v(b,e),E)

¬sensitive a′

(E,rhs)⇒l v(b,e) : a

(E,(a′)rhs)⇒r (v,E)

(E,rhs)⇒l v : a

(E,(a′)rhs)⇒r (v,E)

The next set of rules describes memory operations(pointer dereference and assignment) on sensitive typesand safe values:

sensitive a(E, lhs)⇒l ls : a∗reads(E.Ms)ls = some l′(b,e)l′ ∈ [b,e−sizeof(a)]

(E,∗lhs)⇒l l′s : a

sensitive a(E, lhs)⇒l ls : a∗reads(E.Ms)ls = some l′(b,e)l′ �∈ [b,e−sizeof(a)]

(E,∗lhs)⇒l Abort

sensitive a(E, lhs)⇒l ls : a(E,rhs)⇒r v(b,e) : aE ′.Ms = writes(E.Ms)ls v(b,e)

(E, lhs = rhs)⇒c (OK,E ′)

These rules are identical to the corresponding rules ofSoftBound [34] and ensure full spatial memory safety ofall memory objects in the safe memory. Only operationsmatching those rules are allowed to access safe memory

Ms. In particular, any attempts to access values of sensi-tive types through regular lvalues cause aborts:

sensitive a(E, lhs)⇒l lu : a∗

(E,∗lhs)⇒l Abort

sensitive a(E, lhs)⇒l lu : a

(E, lhs = rhs)⇒c (Abort,E)

Note that these rules can only be invoked if the value ofthe sensitive type was obtained by casting from a regu-lar type using a corresponding type casting rule. Leveerelaxes the casting rules to allow propagation of boundsinformation through certain right-hand-side expressionsof regular types. This relaxation handles most commoncases of unsafe type casting; it affects performance (in-ducing more instrumentation) but not correctness.

Some sensitive types (only void∗ in our simplifiedversion of C), can hold regular values at runtime. For ex-ample, a variable of void∗ type can first be used to storea function pointer and subsequently re-used to store anint∗ value. The following rules handle such cases:

sensitive a(E, lhs)⇒l ls : a∗reads(E.Ms)l = none

readu(E.Mu)l = l′

(E,∗lhs)⇒l l′u : a

sensitive a(E, lhs)⇒l ls : a(E,rhs)⇒r v : aE ′.Mu = writeu(E.Mu) l vE ′.Ms = writes(E.Ms) l none

(E, lhs = rhs)⇒c (OK,E ′)

Memory operations on regular types always accessregular memory, without any additional runtime checks,following the unsafe memory semantics of C.

¬sensitive a(E, lhs)⇒l l : a∗readu(E.Mu)l = l′

(E,∗lhs)⇒l l′u : a

¬sensitive a(E, lhs)⇒l l : a(E,rhs)⇒r v : aE ′.Mu = writeu(E.Mu) l v

(E, lhs = rhs)⇒c (OK,E ′)

These accesses to regular memory can go out of boundsbut, given that readu and writeu operations can onlymodify regular memory Mu, it does not violate memorysafety of the safe memory.

Finally, indirect calls abort if the function pointer be-ing called is not safe:

(E, lhs)⇒r ls : f∗

(E,(∗lhs)())⇒c (OK,E ′)

(E, lhs)⇒r lu : f∗

(E,(∗lhs)())⇒c (Abort,E)

Note that the operational rules for values that are safeat runtime are fully equivalent to the corresponding Soft-Bound rules [34] and, therefore, satisfy the SoftBoundsafety invariant which, as proven in [34], ensures mem-ory safety for these values. According to the sensitivecriterion and the safe location dereference and indirectfunction call rules above, all dereferences of pointers thatrequire protection according to the CPI property are al-ways safe at runtime, or the program aborts. Therefore,the operational semantics defined above indeed ensurethe CPI property as defined in §3.1.

14

Page 16: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 161

References[1] M. Abadi, M. Budiu, U. Erlingsson, and J. Ligatti.

Control-flow integrity. In ACM Conf. on Computerand Communication Security, 2005.

[2] P. Akritidis. Cling: A memory allocator to mitigatedangling pointers. In USENIX Security Symposium,2010.

[3] P. Akritidis, C. Cadar, C. Raiciu, M. Costa, andM. Castro. Preventing memory error exploits withWIT. In IEEE Symp. on Security and Privacy, May2008.

[4] P. Akritidis, M. Costa, M. Castro, and S. Hand.Baggy Bounds Checking: An Efficient andBackwards-compatible Defense Against Out-of-bounds Errors. In USENIX Security Symposium,2009.

[5] G. Altekar and I. Stoica. Focus replay debuggingeffort on the control plane. USENIX Workshop onHot Topics in Dependability, 2010.

[6] S. Bhatkar, E. Bhatkar, R. Sekar, and D. C. Duvar-ney. Efficient techniques for comprehensive pro-tection from memory error exploits. In USENIXSecurity Symposium, 2005.

[7] S. Bhatkar and R. Sekar. Data Space Randomiza-tion. In Intl. Conf. on Detection of Intrusions andMalware, and Vulnerability Assessment, 2008.

[8] T. Bletsch, X. Jiang, V. W. Freeh, and Z. Liang.Jump-oriented programming: a new class of code-reuse attack. In ACM Symp. on Information, Com-puter and Communications Security, 2011.

[9] N. Carlini and D. Wagner. Rop is still dangerous:Breaking modern defenses. In USENIX SecuritySymposium, 2014.

[10] M. Castro, M. Costa, and T. Harris. Securing soft-ware by enforcing data-flow integrity. In Symp.on Operating Systems Design and Implementation,2006.

[11] M. Castro, M. Costa, J.-P. Martin, M. Peinado,P. Akritidis, A. Donnelly, P. Barham, and R. Black.Fast byte-granularity software fault isolation. InACM Symp. on Operating Systems Principles,2009.

[12] S. Checkoway, L. Davi, A. Dmitrienko, A.-R.Sadeghi, H. Shacham, and M. Winandy. Return-oriented programming without returns. In ACMConf. on Computer and Communication Security,2010.

[13] C. Cowan, S. Beattie, J. Johansen, and P. Wa-gle. PointguardTM: protecting pointers from bufferoverflow vulnerabilities. In USENIX Security Sym-posium, 2003.

[14] C. Cowan, C. Pu, D. Maier, H. Hintony, J. Walpole,P. Bakke, S. Beattie, A. Grier, P. Wagle, andQ. Zhang. StackGuard: Automatic adaptive detec-tion and prevention of buffer-overflow attacks. InUSENIX Security Symposium, 1998.

[15] L. Davi, A.-R. Sadeghi, D. Lehmann, and F. Mon-rose. Stitching the gadgets: On the ineffectivenessof coarse-grained control-flow integrity protection.In USENIX Security Symposium, 2014.

[16] J. Devietti, C. Blundell, M. M. K. Martin, andS. Zdancewic. Hardbound: Architectural supportfor spatial safety of the c programming language. InIntl. Conf. on Architectural Support for Program-ming Languages and Operating Systems, 2008.

[17] D. Dhurjati, S. Kowshik, and V. Adve. SAFE-Code: enforcing alias analysis for weakly typedlanguages. SIGPLAN Notices, 41(6):144–157, June2006.

[18] U. Erlingsson, M. Abadi, M. Vrable, M. Budiu, andG. C. Necula. XFI: Software guards for system ad-dress spaces. In Symp. on Operating Systems De-sign and Implementation, 2006.

[19] E. Goktas, E. Athanasopoulosy, H. Bos, and G. Por-tokalidis. Out of control: Overcoming control-flowintegrity. In IEEE Symp. on Security and Privacy,2014.

[20] N. Hasabnis, A. Misra, and R. Sekar. Light-weightbounds checking. In IEEE/ACM Symp. on CodeGeneration and Optimization, 2012.

[21] A. Homescu, S. Neisius, P. Larsen, S. Brunthaler,and M. Franz. Profile-guided automated softwarediversity. In IEEE/ACM Symp. on Code Generationand Optimization, 2013.

[22] R. Hund, C. Willems, and T. Holz. Practical timingside channel attacks against kernel space aslr. InIEEE Symp. on Security and Privacy, 2013.

[23] Intel Architecture Instruction Set Exten-sions Programming Reference. http:

//download-software.intel.com/sites/

default/files/319433-015.pdf, 2013.

[24] Intel. Introduction to Intel memory protec-tion extensions. https://software.intel.com/en-us/articles/introduction-to-intel-memory-protection-extensions, July 2013.

15

Page 17: code-pointer integrity (CPI)

162 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) USENIX Association

[25] T. Jim, J. G. Morrisett, D. Grossman, M. W. Hicks,J. Cheney, and Y. Wang. Cyclone: A safe dialect ofC. In USENIX Annual Technical Conf., 2002.

[26] C. Kil, J. Jun, C. Bookholt, J. Xu, and P. Ning. Ad-dress space layout permutation (ASLP): Towardsfine-grained randomization of commodity softwar.In Annual Computer Security Applications Conf.,2006.

[27] C. Lattner and V. Adve. Automatic Pool Alloca-tion: Improving Performance by Controlling DataStructure Layout in the Heap. In ACM Conf. onProgramming Language Design and Implementa-tion, 2005.

[28] C. Lattner, A. Lenharth, and V. Adve. Mak-ing Context-Sensitive Points-to Analysis with HeapCloning Practical For The Real World. In ACMConf. on Programming Language Design and Im-plementation, 2007.

[29] J. Li, Z. Wang, T. K. Bletsch, D. Srinivasan, M. C.Grace, and X. Jiang. Comprehensive and ef-ficient protection of kernel control data. IEEETransactions on Information Forensics and Secu-rity, 6(4):1404–1417, Dec. 2011.

[30] The LLVM compiler infrastructure. http://

llvm.org/.

[31] A. J. Mashtizadeh, A. Bittau, D. Mazieres, andD. Boneh. Cryptographically enforced controlflow integrity. http://arxiv.org/abs/1408.1451, Aug.2014.

[32] S. McCamant and G. Morrisett. Evaluating sfi for acisc architecture. In USENIX Security Symposium,2006.

[33] S. Nagarakatte, M. M. K. Martin, andS. Zdancewic. Watchdog: Hardware for safeand secure manual memory management andfull memory safety. In Intl. Symp. on ComputerArchitecture, 2012.

[34] S. Nagarakatte, J. Zhao, M. M. Martin, andS. Zdancewic. SoftBound: Highly Compatible andComplete Spatial Safety for C. In ACM Conf. onProgramming Language Design and Implementa-tion, 2009.

[35] S. Nagarakatte, J. Zhao, M. M. Martin, andS. Zdancewic. CETS: Compiler Enforced Tempo-ral Safety for C. In Intl. Symp. on Memory Man-agement, 2010.

[36] G. Necula, J. Condit, M. Harren, S. McPeak, andW. Weimer. CCured: Type-safe retrofitting oflegacy software. ACM Trans. on ProgrammingLanguages and Systems, 27(3):477–526, 2005.

[37] Nergal. The advanced return-into-lib(c) ex-ploits. Phrack, 11(58):http://phrack.com/issues.html?issue=67&id=8, Nov. 2007.

[38] B. Niu and G. Tan. Monitor integrity protectionwith space efficiency and separate compilation. InACM Conf. on Computer and Communication Se-curity, 2013.

[39] B. Niu and G. Tan. Modular control-flow integrity.In ACM Conf. on Programming Language Designand Implementation, 2014.

[40] PaX-Team. PaX ASLR (Address Space Lay-out Randomization). http://pax.grsecurity.

net/docs/aslr.txt, 2003.

[41] Phoronix. Phoronix test suite. http://www.

phoronix-test-suite.com/.

[42] E. J. Schwartz, T. Avgerinos, and D. Brumley. Allyou ever wanted to know about dynamic taint anal-ysis and forward symbolic execution (but mighthave been afraid to ask). In IEEE Symp. on Securityand Privacy, 2010.

[43] K. Serebryany, D. Bruening, A. Potapenko, andD. Vyukov. AddressSanitizer: A Fast Address San-ity Checker. In USENIX Annual Technical Conf.,2012.

[44] H. Shacham. The geometry of innocent flesh on thebone: Return-into-libc without function calls (onthe x86). In ACM Conf. on Computer and Commu-nication Security, 2007.

[45] K. Z. Snow, F. Monrose, L. Davi, A. Dmitrienko,C. Liebchen, and A.-R. Sadeghi. Just-in-time codereuse: On the effectiveness of fine-grained addressspace layout randomization. In IEEE Symp. on Se-curity and Privacy, pages 574–588, 2013.

[46] L. Szekeres, M. Payer, T. Wei, and D. Song. SoK:Eternal war in memory. IEEE Symp. on Securityand Privacy, 2013.

[47] C. Tice, T. Roeder, P. Collingbourne, S. Check-oway, U. Erlingsson, L. Lozano, and G. Pike. En-forcing forward-edge control-flow integrity in gcc& llvm. In USENIX Security Symposium, 2014.

[48] A. van de Ven and I. Molnar. Exec Shield.https://www.redhat.com/f/pdf/rhel/

WHP0006US_Execshield.pdf, 2004.

16

Page 18: code-pointer integrity (CPI)

USENIX Association 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’14) 163

[49] J. Wilander, N. Nikiforakis, Y. Younan, M. Kamkar,and W. Joosen. RIPE: Runtime intrusion preventionevaluator. In Annual Computer Security Applica-tions Conf., 2011.

[50] B. Yee, D. Sehr, G. Dardyk, J. B. Chen, R. Muth,T. Ormandy, S. Okasaka, N. Narula, and N. Ful-lagar. Native client: A sandbox for portable, un-trusted x86 native code. In IEEE Symp. on Securityand Privacy, 2009.

[51] B. Zeng, G. Tan, and U. Erlingsson. Strato: A retar-getable framework for low-level inlined-referencemonitors. In USENIX Security Symposium, 2013.

[52] B. Zeng, G. Tan, and G. Morrisett. Combiningcontrol-flow integrity and static analysis for effi-cient and validated data sandboxing. In ACM Conf.on Computer and Communication Security, 2011.

[53] C. Zhang, T. Wei, Z. Chen, L. Duan, L. Szekeres,S. McCamant, D. Song, and W. Zou. Practical Con-trol Flow Integrity & Randomization for Binary Ex-ecutables. In IEEE Symp. on Security and Privacy,2013.

[54] M. Zhang and R. Sekar. Control flow integrity forCOTS binaries. In USENIX Security Symposium,2013.

17