====== Control-flow Integrity (CFI) ====== Control-flow Integrity (CFI) attempts to validate the control-flow of a process. This is achieved by analyzing the application and building up a Control-flow Graph (CFG) representing the intended program behavior((Tice, Caroline, et al. "Enforcing Forward-Edge Control-Flow Integrity in GCC & LLVM." USENIX Security Symposium. 2014.))((Conti, Mauro, et al. "Losing control: On the effectiveness of control-flow integrity under stack attacks." Proceedings of the 22nd ACM SIGSAC Conference on Computer and Communications Security. ACM, 2015.)). One important addition of CFI is the so-called "shadow stack". Next to the existing stack of an application, another one is introduced for verification purposes. Return addresses are added to both stacks upon function calls. When executing a return instruction, the top elements of both stacks is compared. In case they are equal, execution continues. But if they differ, a manipulation of the control-flow is detected. ===== Compiler-based Implementations ===== Major compilers such as GCC and LLVM support CFI. Naturally, applications need to be recompiled if CFI is added later on. Also note that the performance impact of a purely software-based solution should not be neglected((Dang, Thurston HY, Petros Maniatis, and David Wagner. "The performance cost of shadow stacks and stack canaries." Proceedings of the 10th ACM Symposium on Information, Computer and Communications Security. ACM, 2015.)). Following example demonstrates the usage of CFI in combination with the LLVM-powered Clang compiler(([[https://blog.trailofbits.com/2016/10/17/lets-talk-about-cfi-clang-edition/|Let’s talk about CFI: clang edition | Trail of Bits Blog]])). // clang -fvisibility=hidden -flto -fno-sanitize-trap=all -fsanitize=cfi-icall clang.c #include void admin_stuff(); void user_stuff(); int main() { void (*fp[])() = {&admin_stuff, &user_stuff}; int choice; printf("Enter choice:\n0) Admin\n1) User\n"); scanf("%d", &choice); (*fp[choice])(); return 0; } void admin_stuff() { printf("admin stuff\n"); } void user_stuff() { printf("user stuff\n"); } Entering valid values for the array of function pointers causes the program to behave just as expected. $ ./a.out Enter choice: 0) Admin 1) User 0 admin stuff $ ./a.out Enter choice: 0) Admin 1) User 1 user stuff However, entering and invalid value and trying to execute an arbitrary memory region causes the execution to abort. $ ./a.out Enter choice: 0) Admin 1) User 123 clang.c:13:5: runtime error: control flow integrity check for type 'void ()' failed during indirect function call 0x0000000003e8: note: (unknown) defined here ===== Hardware-based Implementations ===== Control-flow Enforcement Technology (CET)(([[https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf|Control-flow Enforcement Technology Preview]])) is a hardware-supported implementation of CFI suggested by Intel. With CET, a shadow stack tracks function calls. Upon a ''call'' instruction, the return address is also pushed to the shadow stack. When returning from the function call, the return addresses are popped from both stacks and compared. In case of different values, an exception is raised. Additionally, targets of ''jmp'' and indirect ''call'' instructions are marked. An error occurs if these instructions are executed for a target that is not marked. As of January 2018 the CET specification is only available as a preview and there are no processors supporting it on a hardware level. CET support was added to version 8 of GCC(([[https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=3c0f15b4cebccdbb6388a8df5933e69c9b773149|gcc commit: Add generic part for Intel CET enabling]])). \\ ----
[[.nx|← Back to NX bit]] [[..start|Overview]] [[.static-analysis|Continue with static code analysis →]]