GPT Conjecture: Understanding the Trade-offs between Granularity, Performance and Timeliness in Control-Flow Integrity

by   Zhilong Wang, et al.
Penn State University

Performance/security trade-off is widely noticed in CFI research, however, we observe that not every CFI scheme is subject to the trade-off. Motivated by the key observation, we ask three questions. Although the three questions probably cannot be directly answered, they are inspiring. We find that a deeper understanding of the nature of the trade-off will help answer the three questions. Accordingly, we proposed the GPT conjecture to pinpoint the trade-off in designing CFI schemes, which says that at most two out of three properties (fine granularity, acceptable performance, and preventive protection) could be achieved.



There are no comments yet.


page 1

page 2

page 3

page 4


Model-Agnostic Characterization of Fairness Trade-offs

There exist several inherent trade-offs in designing a fair model, such ...

Quality-Efficiency Trade-offs in Machine Learning for Text Processing

Data mining, machine learning, and natural language processing are power...

A Serious Game Design: Nudging Users' Memorability of Security Questions

Security questions are one of the techniques used to recover passwords. ...

Practical Trade-Offs for the Prefix-Sum Problem

Given an integer array A, the prefix-sum problem is to answer sum(i) que...

Online VNF Chaining and Predictive Scheduling: Optimality and Trade-offs

For NFV systems, the key design space includes the function chaining for...

SToN: A New Fundamental Trade-off for Distributed Data Storage Systems

Locating data efficiently is a key process in every distributed data sto...

Studying Politically Vulnerable Communities Online: Ethical Dilemmas, Questions, and Solutions

This short article introduces the concept of political vulnerability for...
This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

I Introduction

Along with the increased complexity of software, it becomes harder for the developers to ensure execution correctness in their software products, especially in those developed by the low-level programming languages, such as C/C++. A substantial amount of execution in-correctness is caused by the exploitation of software vulnerabilities in the real world. Softwares inevitably contain a wide variety of vulnerabilities, opening a window for attacks to compromise the system. Attackers have developed a series of attack methods, such as shellcode injection[Erickson2008Hacking], return-to-libc[wojtczuk2001advanced], ROP[shacham2007geometry] and so on, to exploit all kinds of vulnerabilities, e.g., buffer overflow, format string, use-after-free, and so on [szekeres2013sok]. Among all kinds of attacks, the control-flow hijacking attack is the most dangerous one, because it allows the attacker to control the program’s execution, execute arbitrary malicious code and attain Turing-complete operation[shacham2007geometry]. To mitigate the threats, many defense mechanisms, such as stack smashing protector (SSP)[cowan1998stackguard], address space layout randomization (ASLR)[aslr], data execution prevention (DEP)[dep] and so on, have been put forward by researchers and applied in the real world software products.

Among all the defense techniques, security schemes based on the concept of control-flow integrity (CFI) have attracted many researchers’ attention because of its simplicity to implement, effectiveness to cope with the full spectrum of control-flow hijacking attacks, and flexibility to trade between security and efficiency. CFI schemes guarantee the correctness of the program by dynamically checking the control-flow transfer and confining the target address to a legal set.

Since CFI was introduced by Abadi et al. in 2005 [Abadi], many researchers afterward were dedicated to enhance its runtime performance, security, scalability, compatibility and so on. According to mainstream taxonomy, most CFI schemes can be clarified into two categories: fine-grained CFI schemes that provide more security guarantee, and coarse-grained CFI schemes that attain higher runtime performance. However, both fine-grained and coarse-grained CFI schemes have noticeable limitations that have not been addressed yet. As shown in previous survey papers [burow2017control], lightweight CFI schemes can not fully prevent sophisticated code reuse attack. The adversary’s attacking strategy is to search large gadgets chain whose starting addresses are allowed in a rough control-flow graph that coarse-grained CFI schemes adopted [goktas2014out, passcfi]. Precise CFI schemes usually suffer from unacceptable runtime overhead. Hence, it is widely believed “performance/security trade-off” exists between runtime overhead and security in different CFI schemes [burow2017control, 236352].

However, we observe that not every CFI scheme is subject to the trade-off between performance and security. In fact, several CFI schemes are “immunized” from doing such a trade-off. For instance, CFI designed by Niu et al. achieves fine-grained security with a runtime overhead of 3.2% on average, which is fairly low and acceptable [niu2015per]. Victor et al. proposed a context-sensitive CFI scheme that achieves stronger security than conventional fine-grained ones with an overhead of less than some of the coarse-grained ones [van2015practical].

Key Observation. The trade-off between performance and security does not universally exist in meaningful CFI schemes. This intriguing observation motivates us to ask three questions: ➊ does trade-off really exist in different CFI schemes? ➋ if trade-off do exist, How do previous works comply with it? ➌ how can it inspire future research?

Although the questions probably cannot be directly answered, they are inspiring. On the other hand, we find that a deeper understanding of the nature of the trade-off will help answer these questions. Accordingly, we propose the Gpt conjecture to pinpoint general trade-offs in CFI schemes: the impossibility of guaranteeing both fine granularity and acceptable performance in a Just-In-Time CFI scheme. We analyze its rationality through empirical study—surveying a series of representative CFI schemes and showing how existing CFI schemes comply with our conjecture. Finally, we give some recommendations for future researchers. We believe that our conjecture will help researchers have a more clear understanding of internal relations among properties of CFI schemes, thereby, motivating future research in this area.

Ii Background

When compiling source code written by low-level language (such as C or C++) into machine code, the compiler emits control data [chen2005non] (data that are loaded to processor program counter at some point in program execution, e.g., return addresses and function pointers) into the binary file without any protection. The security of control data depends on checks inserted by the programmer to enforce memory safety [nagarakatte2012practical]. Along with program execution, attacker’s malicious tampering with control data through software vulnerabilities, such as buffer overflow, can transfer the program’s control-flow to any executable address in process space.

Based on this observation, researchers invented CFI to protect programs against control-flow hijacking attacks by checking programs’ control data before loading them into the program counter (EIP/RIP register in x86/x64 architecture). CFI’s strategy is to restrict the control-flow of a program to a pre-calculated CFG by checking indirect control-flow transfers at runtime [burow2017control]. Generally, most of CFI schemes follow a mainstream that consists of two phases.

In phase one, an analyzer statically computes the program’s control-flow graph (CFG). CFG is a representation in graph form of all legitimate control-flow transfers (also being called branch) in program space. It consists of sets of nodes and directed edges. Each node and edge denotes a basic block and a valid branch in the program respectively. For a comprehensive understanding, we refer the reader to the formal definition of CFG in work by Allen, et al. [Allen:1970].

In phase two, a runtime control-flow checking (validation) component validates just fetched control data before each indirect-branching according to the legitimate CFG generated in phase one 111Direct control-flow transfers do not load any control data, their target addresses/offsets are hard-coded in their instructions.. An indirect-branch can pass checking only if it can be matched to a corresponding edge in the CFG. A failed validation will result in the process to terminate its execution and report an error. In such a fashion, control-flow attacks which usually introduce out-of-range branch are extremely prohibited. Researchers need to design efficient data structures to represent the CFG and enable runtime checking.

Despite its straightforward main idea, it is pretty challenging to design a CFI scheme with strong security, acceptable performance, high compatibility and so on [236352, burow2017control]. Researchers have designed hundreds of CFI schemes to explore its potential in different perspectives. The dominant difference of these various CFI schemes can be summarized into three aspects: 1) the precision of a CFG they employed. 2) the algorithm they designed to check indirect-branches. 3) the time point checking algorithm was activated.

Ii-1 Precision of CFG Analyzer

CFG can be obtained by analyzing the program’s source code or binary code. Like pointer analysis [pointeranalysis], perfect CFG generation is can not be fully achieved yet in many situations [goktas2014out]. By now researchers have adopted several types of methods (insensitive analysis, context-sensitive analysis, and path-sensitive analysis) in their CFG analyzer and achieve different precisions. It is widely agreed that path-sensitive analysis is more precise than context-sensitive analysis, and context-sensitive analysis is more precise than insensitive analysis [khedker2017data].

Ii-2 Algorithm to Enforce Checking

The efficiency of different CFI schemes is largely dependent on their algorithms to enforce validation, which is tightly combined with their data structure that represents the CFG and enables runtime checking. Researchers have designed different types of algorithms and data structures in different CFI schemes. For example, the original CFI scheme proposed by the Abadi, et al. groups branch targets into different sets, assigns each set with a label, and inlines labels into each jump targets, i.e., the basic block’s in code. Based on this data structure, “guard instructions” are emitted before each indirect-branch instruction to compare its label with the one in target basic block [Abadi]. A mismatch indicates that the control data is corrupted, then the program’s execution will be redirect to the error handling code accordingly.

CFI [niu2015per] and MCFI [mcfi] by Niu, et al. adopts two ID tables, namely Bary and Tary, to store target program’s CFG. In essence, Bary table and Tary table are hashmaps which can efficiently map indirect-branch points and target basic blocks to their corresponding IDs. Specifically, the Tary table is an array of IDs indexed by code addresses, mapping target basic block to their corresponding IDs. The Bary table uses a similar design, mapping indirect-branch points to their corresponding IDs. Two tables enable efficient ID look-ups and a indirect-branch is checked by comparing the IDs of branch point and target.

Ii-3 Just-In-Time Checking vs. Lazy Checking

Another difference among CFI schemes is how they schedule their checking operations. Most CFI schemes check the target address before indirect-branch occurs (we define it as a Just-In-Time checking). While, to achieve better performance, some works log each indirect-branches at runtime and check them by employing another accompanying thread [huenforcing, 203656, griffin, van2015practical, 203656] (we define it as Lazy checking). For example, PITTYPAT [203656] enforces path-sensitive CFI by maintaining a “shadow” execution/analyzer, running concurrently with the protected process and checks its finished indirect-branches. Such a non-intrusive checking does not disturb the normal execution of the monitored process, hence achieves path-sensitive CFI with practical runtime overhead.

Iii Conjecture

This section aims to answer Question➊ and Question➋. We observe that some terms, such as coase-grained/fine-grained, have not been clearly defined. Before introducing the Gpt conjecture, let us give a more precise definition of the terms and concepts that will be used throughout the paper. Then we propose the Gpt conjecture which helps to answer Question➊. At last, some evidence is collected from an empirical study to answer the Question➋.

Iii-a Terminology

Property 1.(Granularity)

Suppose a program has indirect branch instructions. Let  222It is computed through mainstream insensitive control flow analysis. We admit the inaccuracy due to the difficulty of the pointer analysis. denote the set of valid successors (basic blocks) of the -th indirect branch instruction, and denote the set of all successor sets, namely,


For a CFI scheme, let denote the checking set which is defined by the scheme and assigned to the -th indirect branch instruction, then used to check the branch’s target at runtime. Only the elements in are valid successors authorized by the CFI schemes that the -th branch instruction could jump to.

Definition 1. For arbitrary two sets , from , satisfying , as long as the CFI scheme merges , when define its or , namely,


we define this scheme as a coase-grained CFI scheme. Otherwise, we define it as a fine-grained CFI scheme. This definition enables us to determinate the granularity property of CFI schemes.

Remark 1. According to Definition III-A, both context-sensitive and path-sensitive CFI schemes belong to fine-grained CFI scheme. In essence, they reduce the size of their checking set for based on context-sensitive or path-sensitive pointer analysis. Their protection is generally considered to be more powerful than that of insensitive fine-grained CFI scheme.

Remark 2. Note that CFI schemes [mashtizadeh2015ccfi, 8440029]

which adopt pointer encryption approach should be classified as

coase-grained CFI scheme. They cannot fully prevent code reuse attack because of two noticeable drawbacks. As discussed in Cryptographically Enforced Control Flow Integrity (CCFI) [mashtizadeh2015ccfi], it is still possible to replace the current encrypted pointer with another one from the program space and potentially disrupt control flow. The other drawback is that these schemes suffer from key leakage issues: the key can be infered by brute-force attack or known-plaintext attack [peng2006known], especially for schemes which adopt a linear encryption/decryption method (XOR) [8440029].

Remark 3. We remark that schemes that only provide partial protection—protecting subset of indirect branches in program space—belong to coase-grained CFI scheme. For instance, vfGuard [prakash2015vfguard], VTV [tice2014enforcing], and SafeDispatch [jang2014safedispatch] only achieve strict protection for virtual function calls in COTS binaries;

Property 2.(Performance)

Evidence 1. As discussed in many papers [szekeres2013sok, burow2017control, microsoft], runtime performance is one of the most important determinants of whether a defense technique will be adopted by industry. Generally, to get adopted by industry, a defense technique should introduce less than 5% average overhead, such as StackGurad, ASLR, and DEP. Techniques incuring an overhead larger than 10% do not tend to gain wide adoption in production environments. Accordingly, the threshold should lie between 5%-10%.

Evidence 2. Other than runtime performance, space performance is another important index to measure a scheme. Program’s runtime memory consumption consists of four aspects, i.e., code, global data, heap, and stack. Different programs have different ratios in four aspects, and a defense technique commonly increases memory consumption in one or more aspects. We observe that shadow based protections like shadow stack [shadowstack], shadow memory [newsome2005dynamic] and shadow processing [patil1995efficient], that double memory consumption in one or more aspects are unlikely to be deployed in practice.

Definition 2. Conservatively, we define a runtime overhead of less than 10% and a space overhead of less than 100% (in any of aforementioned four aspects) as an acceptable performance. Otherwise, it is an unacceptable performance. This definition enables us to determinate the performance property of CFI schemes.

Property 3.(Timeliness)

Observation 1. Whereas the term “integrity” in the context of CFI implies that it can prevent the attacks [Abadi], some of the CFI schemes do not hit the mark. To achieve higher efficiency, some CFI schemes as mentioned in Section II-3 adopted a lazy checking mechanism, which checks programs’ control-flow following the program‘s execution rather than before each indirect branching. Generally, they log the program’s runtime control-flow transfer along with its execution, then check the control-flow offline or through an accompanying thread. In these designs, a sliding window exists between the program’s control-flow transfer and checking. The attacker can compromise the system without being perceived in the sliding window, which means this kind of CFI cannot protect software against such attacks.

Definition 3. We regard that the aforementioned design of CFI schemes provides less protection than CFI schemes that perform Just-In-Time checking. We define protection capability powered by lazy checking schemes as detective protection, the others that powered by Just-In-Time checking as preventive protection. This definition enables us to determinate the property timeliness of CFI schemes.

Iii-B The Proposed Conjecture

Gpt Conjecture: A control-flow integrity scheme can have at most two out of three properties: P1. Fine granularity P2. Acceptable performance P3. Preventive protection
Schemes   P11 P2 P3
CFIXX [burow2018cfixx]   4.98%
Reins [wartell2012securing]   2.40%
vfGuard [prakash2015vfguard]   18.30%
VTV [tice2014enforcing]   9.60%
LLVM-CFI [llvmcfi]   1.10%
VTI [bounov2016protecting]   0.50%
CFGuard [cfguard]   2.30%
IFCC [tice2014enforcing]   -0.30%
ROPecker [ropecker]   2.60%
bin-CFI [zhang2013control]   8.50%
ROPGuard [fratric2012ropguard]   0.48%
SafeDispatch [jang2014safedispatch]   2.00%
CCFIR [zhang2013practical]   2.08%
kBouncer [pappas2013transparent]   4.00%
OCFI [mohan2015opaque]   4.70%
CFIMon [xia2012cfimon]   6.10%
CFI [muntean2018tau]   2.89%
HCIC [8440029]   0.95%
RAGuard [Zhang:2017]   1.86%
HyperSafe [5504800]   5.00%
BinCC [5504800]   4.00%
CCFI [mashtizadeh2015ccfi]   52.00%
KCoFI [6956571]   13.00%
Original CFI [abadi2009control]   16.00%
Lockdown [payer2015fine]   20.00%
MCFI [mcfi]   5.00% & 4GB
CFI [niu2015per]   3.20% & 4GB
Griffin [griffin]   H2 11.90%
PittyPat [203656]   P, H 12.73%
ECFI [Abbasi:2017]   1.50%
CFI [huenforcing]   C, H 10.00%
PathArmor [van2015practical]   C 3.00%
  • If a CFI scheme supports different security levels, e.g. having both coase-grained and fine-grained versions, we focus on its most secure version.

  • ‘H’, ‘P’ and ‘C’ denote hardware-assisted CFI scheme, path sensitive CFI scheme, and context sensitive CFI scheme, respectively.

TABLE I: Reflection of Gpt conjecture in 32 control-flow integrity schemes.

Iii-C Some Evidence of the Gpt Conjecture

In this section, we will reflect on our conjecture through several pieces of evidence. To verify the rationality of our conjecture, we conduct an empirical study on 32 representative works, and show the results in Table I. Three columns (P1, P2 and P3) in the table display three properties respectively as we define in Section III-A. P1 column denotes the granularity—check-mark indicates a fine-grained scheme whileas cross-mark represents a coase-grained scheme. P2 column shows the performance overheads which are reported in corresponding papers. Note that we prefer evaluation results which are based on 2006 benchmarks [spec]. P3 column labels whether a CFI scheme provides preventive protection. We label the data in each column with red color when it fails to meet the requirement defined in the conjecture.

Evidence i. It can be clearly seen in Table I that all CFI schemes we surveyed comply with our conjecture—no CFI schemes can achieve all three properties. Also, some of unsophisticated schemes, such as PittyPat [203656] and Griffin [griffin], only achieve one properity, i.e., fine granularity.

Evidence ii. MCFI [mcfi] and CFI developed by Niu, et al. achieve fine granularity with acceptable runtime overheads, i.e., 3.2% and 5.0%, respectively. However, researchers did not realize that their better runtime overhead is achieved through sacrificing their space performance. Even though they did not report their space overhead in their paper explicitly, we can infer it in a reasonable manner.

As discussed in Section II-2, both of two schemes adopt two tables, namely Bary and Tary, to support their runtime checking. Accordingly, 1GB/4GB memory space on x86-32 and x86-64 operating system, respectively, need to be reserved in each process for the tables. As stated by the author, “On x86-32, memory segmentation is used, as in NaCl [5207638]. A 1GB segment is reserved for running the application code and another 1GB segment is reserved for the table region. x86-64, however, does not support memory segmentation. Instead, memory writes are instrumented so that they are restricted to the [0, 4GB) memory region. Another 4GB memory region is reserved for tables.” In view of the size of memory consumption of typical programs (mostly less than 1GB [spec]), their space overhead has already reached 100% except for code bloat caused by extra no-op instructions inserted to enforce four-byte alignment on indirect-branch targets.

Evidence iii. Griffin [griffin] is a hardware-assisted CFI, which leverages Intel PT to record control-flow of a monitored program. It supports multiple types of CFI policies to enable flexible trade-offs between security and performance. The fine-grained scheme incures an average of 11.9% overhead. It leverages idle cores on a multi-core system for security checking by having multiple worker threads to check runtime control-flow simultaneously. In most of the time, it performs non-blocking checking which analyzes trace buffer of Intel PT whenever it becomes full; In a few cases when security-sensitive system calls are invoked, it performs blocking checking which stops the target thread until all the control transfers in the buffer have been checked. It can only provide the detective protection for software according to Definition III-A. This case indicates that Gpt conjecture is applicable to hardware-assisted CFI schemes.

Evidence iv. PittyPat [203656], CFI [huenforcing] and PathArmor [van2015practical] are path/context sensitive CFI schemes which adopt path-sensitive or context-sensitive analysis to generate their CFG. However, path-sensitive and context-sensitive analysis is generally considered to be more time-consuming and space-consuming than insensitive analysis [khedker2017data]. We find that all three CFI schemes adopt two common features: hard-assisted branch recording and lazy checking. Specifically, PittyPat and CFI employ Intel PT—a brand new hardware feature in Intel CPUs—to efficiently record conditional and indirect branches taken by a program at runtime while PathArmor adopts Last Branch Record (LBR) registers available in Intel processors to monitor recently exercised control-flow transfers in an efficient way. Their control-flow checking is achieved through accompanying threads. This case indicates that both path-sensitive and context-sensitive CFI schemes conform to the claim of Gpt conjecture.

Remark 4. Our observations indicate that the Gpt conjecture is universally applicable in all kinds of scenarios. Further, four pieces of evidence are not meant to be exhaustive and more evidence are easy to find.

Iv Implications of the Gpt Conjecture

In this section, we will focus on answering Question ➌: how can Gpt conjecture inspire future research?

First of all, Gpt conjecture illustrates the inherent trade-offs of three important properties (fine granularity, acceptable performance, and preventive protection) in CFI schemes. It helps researchers to have a deeper understanding of the nature of CFI based protection. Accordingly, future researchers should make a necessary sacrifice before designing new CFI schemes. In the broader context, Gpt conjecture provides insights into the feasible design space for CFI schemes, shedding some light on the manner in which algorithm designers and software engineers have circumvented the conjecture.

Second, for decades, security researchers have been focused on CFI scheme’s runtime performance and made their best effort to improve it. Evidence III-C shows that in some cases, better runtime performance is achieved by sacrificing its space performance. Just as Gerhard states, “For some problems, we can reach an improved time complexity, but it seems that we have to pay for this with an exponential space complexity” [timeandspace]. Therefore, performance evaluation in future research should not merely be limited to runtime performance and researchers should have a more comprehensive evaluation of their schemes.

Third, Evidence III-C that even powerful hardware support cannot eliminate the runtime overhead of Just-In-Time CFI schemes to an acceptable level, which implies that the challenge in the implementation of CFI cannot be solved only through engineering efforts, instead, it may relate to computational complexity theory [comcomplexity]. In a broader sense, we observe that indirect branching poses not only challenge in the security field, but also challenges to many others: precise pointer analysis is an NP-complete problem [pointernp]; indirect branch prediction is a performance-limiting factor for current computer systems [branchpredict]. Hence, Gpt conjecture implies the complexity of the CFI problem, which deserves to be investigated through theoretical methods.

At last, despite the inspiring implications that Gpt conjecture gives to us, we admit that we still cannot prove the conjecture at this time.

V Conclusion

Control-flow integrity is a popular defence technique for detecting and defeating control-flow hijacking attacks. Since its inception in the decade, researchers have put great efforts to explore its potential regarding security, performance, compatibility and so on. Even though performance/security trade-off is widely noticed in CFI research, we observe that not every CFI scheme is subject to it. In this paper, we propose the Gpt conjecture to illustrate the general trade-offs in CFI schemes. The conjecture points out the impossibility of guaranteeing both fine granularity and acceptable performance in a Just-In-Time CFI schemes. We have verified the rationality of our conjecture based on an empirical study on existing works. Even though we cannot prove the conjecture at this time, we believe that Gpt conjecture will help researcher to have a deeper understanding of the nature of CFI problem and it will direct future research in this area.