Vlad Tsyrklevich | bbb3280 | 2017-09-20 20:38:14 +0000 | [diff] [blame] | 1 | ============================================== |
| 2 | Control Flow Verification Tool Design Document |
| 3 | ============================================== |
| 4 | |
| 5 | .. contents:: |
| 6 | :local: |
| 7 | |
| 8 | Objective |
| 9 | ========= |
| 10 | |
| 11 | This document provides an overview of an external tool to verify the protection |
| 12 | mechanisms implemented by Clang's *Control Flow Integrity* (CFI) schemes |
| 13 | (``-fsanitize=cfi``). This tool, provided a binary or DSO, should infer whether |
| 14 | indirect control flow operations are protected by CFI, and should output these |
| 15 | results in a human-readable form. |
| 16 | |
| 17 | This tool should also be added as part of Clang's continuous integration testing |
| 18 | framework, where modifications to the compiler ensure that CFI protection |
| 19 | schemes are still present in the final binary. |
| 20 | |
| 21 | Location |
| 22 | ======== |
| 23 | |
| 24 | This tool will be present as a part of the LLVM toolchain, and will reside in |
| 25 | the "/llvm/tools/llvm-cfi-verify" directory, relative to the LLVM trunk. It will |
| 26 | be tested in two methods: |
| 27 | |
Nico Weber | 86a524c | 2018-05-09 01:15:06 +0000 | [diff] [blame] | 28 | - Unit tests to validate code sections, present in |
| 29 | "/llvm/unittests/tools/llvm-cfi-verify". |
Vlad Tsyrklevich | bbb3280 | 2017-09-20 20:38:14 +0000 | [diff] [blame] | 30 | - Integration tests, present in "/llvm/tools/clang/test/LLVMCFIVerify". These |
| 31 | integration tests are part of clang as part of a continuous integration |
| 32 | framework, ensuring updates to the compiler that reduce CFI coverage on |
| 33 | indirect control flow instructions are identified. |
| 34 | |
| 35 | Background |
| 36 | ========== |
| 37 | |
| 38 | This tool will continuously validate that CFI directives are properly |
| 39 | implemented around all indirect control flows by analysing the output machine |
| 40 | code. The analysis of machine code is important as it ensures that any bugs |
| 41 | present in linker or compiler do not subvert CFI protections in the final |
| 42 | shipped binary. |
| 43 | |
| 44 | Unprotected indirect control flow instructions will be flagged for manual |
| 45 | review. These unexpected control flows may simply have not been accounted for in |
| 46 | the compiler implementation of CFI (e.g. indirect jumps to facilitate switch |
| 47 | statements may not be fully protected). |
| 48 | |
| 49 | It may be possible in the future to extend this tool to flag unnecessary CFI |
| 50 | directives (e.g. CFI directives around a static call to a non-polymorphic base |
| 51 | type). This type of directive has no security implications, but may present |
| 52 | performance impacts. |
| 53 | |
| 54 | Design Ideas |
| 55 | ============ |
| 56 | |
| 57 | This tool will disassemble binaries and DSO's from their machine code format and |
| 58 | analyse the disassembled machine code. The tool will inspect virtual calls and |
| 59 | indirect function calls. This tool will also inspect indirect jumps, as inlined |
| 60 | functions and jump tables should also be subject to CFI protections. Non-virtual |
| 61 | calls (``-fsanitize=cfi-nvcall``) and cast checks (``-fsanitize=cfi-*cast*``) |
| 62 | are not implemented due to a lack of information provided by the bytecode. |
| 63 | |
| 64 | The tool would operate by searching for indirect control flow instructions in |
| 65 | the disassembly. A control flow graph would be generated from a small buffer of |
| 66 | the instructions surrounding the 'target' control flow instruction. If the |
| 67 | target instruction is branched-to, the fallthrough of the branch should be the |
| 68 | CFI trap (on x86, this is a ``ud2`` instruction). If the target instruction is |
| 69 | the fallthrough (i.e. immediately succeeds) of a conditional jump, the |
| 70 | conditional jump target should be the CFI trap. If an indirect control flow |
| 71 | instruction does not conform to one of these formats, the target will be noted |
| 72 | as being CFI-unprotected. |
| 73 | |
| 74 | Note that in the second case outlined above (where the target instruction is the |
| 75 | fallthrough of a conditional jump), if the target represents a vcall that takes |
| 76 | arguments, these arguments may be pushed to the stack after the branch but |
| 77 | before the target instruction. In these cases, a secondary 'spill graph' in |
| 78 | constructed, to ensure the register argument used by the indirect jump/call is |
| 79 | not spilled from the stack at any point in the interim period. If there are no |
| 80 | spills that affect the target register, the target is marked as CFI-protected. |
| 81 | |
| 82 | Other Design Notes |
| 83 | ~~~~~~~~~~~~~~~~~~ |
| 84 | |
| 85 | Only machine code sections that are marked as executable will be subject to this |
| 86 | analysis. Non-executable sections do not require analysis as any execution |
| 87 | present in these sections has already violated the control flow integrity. |
| 88 | |
Joel Galenson | e27b1d0 | 2018-07-16 15:26:44 +0000 | [diff] [blame] | 89 | Suitable extensions may be made at a later date to include analysis for indirect |
Vlad Tsyrklevich | bbb3280 | 2017-09-20 20:38:14 +0000 | [diff] [blame] | 90 | control flow operations across DSO boundaries. Currently, these CFI features are |
| 91 | only experimental with an unstable ABI, making them unsuitable for analysis. |
Joel Galenson | e27b1d0 | 2018-07-16 15:26:44 +0000 | [diff] [blame] | 92 | |
| 93 | The tool currently only supports the x86, x86_64, and AArch64 architectures. |