| 1 | .. _cfi: |
| 2 | |
| 3 | ============================ |
| 4 | Control-Flow Integrity (CFI) |
| 5 | ============================ |
| 6 | |
| 7 | This document describes the current control-flow integrity (CFI) mechanism in |
| 8 | QEMU. How it can be enabled, its benefits and deficiencies, and how it affects |
| 9 | new and existing code in QEMU |
| 10 | |
| 11 | Basics |
| 12 | ------ |
| 13 | |
| 14 | CFI is a hardening technique that focusing on guaranteeing that indirect |
| 15 | function calls have not been altered by an attacker. |
| 16 | The type used in QEMU is a forward-edge control-flow integrity that ensures |
| 17 | function calls performed through function pointers, always call a "compatible" |
| 18 | function. A compatible function is a function with the same signature of the |
| 19 | function pointer declared in the source code. |
| 20 | |
| 21 | This type of CFI is entirely compiler-based and relies on the compiler knowing |
| 22 | the signature of every function and every function pointer used in the code. |
| 23 | As of now, the only compiler that provides support for CFI is Clang. |
| 24 | |
| 25 | CFI is best used on production binaries, to protect against unknown attack |
| 26 | vectors. |
| 27 | |
| 28 | In case of a CFI violation (i.e. call to a non-compatible function) QEMU will |
| 29 | terminate abruptly, to stop the possible attack. |
| 30 | |
| 31 | Building with CFI |
| 32 | ----------------- |
| 33 | |
| 34 | NOTE: CFI requires the use of link-time optimization. Therefore, when CFI is |
| 35 | selected, LTO will be automatically enabled. |
| 36 | |
| 37 | To build with CFI, the minimum requirement is Clang 6+. If you |
| 38 | are planning to also enable fuzzing, then Clang 11+ is needed (more on this |
| 39 | later). |
| 40 | |
| 41 | Given the use of LTO, a version of AR that supports LLVM IR is required. |
| 42 | The easies way of doing this is by selecting the AR provided by LLVM:: |
| 43 | |
| 44 | AR=llvm-ar-9 CC=clang-9 CXX=clang++-9 /path/to/configure --enable-cfi |
| 45 | |
| 46 | CFI is enabled on every binary produced. |
| 47 | |
| 48 | If desired, an additional flag to increase the verbosity of the output in case |
| 49 | of a CFI violation is offered (``--enable-debug-cfi``). |
| 50 | |
| 51 | Using QEMU built with CFI |
| 52 | ------------------------- |
| 53 | |
| 54 | A binary with CFI will work exactly like a standard binary. In case of a CFI |
| 55 | violation, the binary will terminate with an illegal instruction signal. |
| 56 | |
| 57 | Incompatible code with CFI |
| 58 | -------------------------- |
| 59 | |
| 60 | As mentioned above, CFI is entirely compiler-based and therefore relies on |
| 61 | compile-time knowledge of the code. This means that, while generally supported |
| 62 | for most code, some specific use pattern can break CFI compatibility, and |
| 63 | create false-positives. The two main patterns that can cause issues are: |
| 64 | |
| 65 | * Just-in-time compiled code: since such code is created at runtime, the jump |
| 66 | to the buffer containing JIT code will fail. |
| 67 | |
| 68 | * Libraries loaded dynamically, e.g. with dlopen/dlsym, since the library was |
| 69 | not known at compile time. |
| 70 | |
| 71 | Current areas of QEMU that are not entirely compatible with CFI are: |
| 72 | |
| 73 | 1. TCG, since the idea of TCG is to pre-compile groups of instructions at |
| 74 | runtime to speed-up interpretation, quite similarly to a JIT compiler |
| 75 | |
| 76 | 2. TCI, where the interpreter has to interpret the generic *call* operation |
| 77 | |
| 78 | 3. Plugins, since a plugin is implemented as an external library |
| 79 | |
| 80 | 4. Modules, since they are implemented as an external library |
| 81 | |
| 82 | 5. Directly calling signal handlers from the QEMU source code, since the |
| 83 | signal handler may have been provided by an external library or even plugged |
| 84 | at runtime. |
| 85 | |
| 86 | Disabling CFI for a specific function |
| 87 | ------------------------------------- |
| 88 | |
| 89 | If you are working on function that is performing a call using an |
| 90 | incompatible way, as described before, you can selectively disable CFI checks |
| 91 | for such function by using the decorator ``QEMU_DISABLE_CFI`` at function |
| 92 | definition, and add an explanation on why the function is not compatible |
| 93 | with CFI. An example of the use of ``QEMU_DISABLE_CFI`` is provided here:: |
| 94 | |
| 95 | /* |
| 96 | * Disable CFI checks. |
| 97 | * TCG creates binary blobs at runtime, with the transformed code. |
| 98 | * A TB is a blob of binary code, created at runtime and called with an |
| 99 | * indirect function call. Since such function did not exist at compile time, |
| 100 | * the CFI runtime has no way to verify its signature and would fail. |
| 101 | * TCG is not considered a security-sensitive part of QEMU so this does not |
| 102 | * affect the impact of CFI in environment with high security requirements |
| 103 | */ |
| 104 | QEMU_DISABLE_CFI |
| 105 | static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) |
| 106 | |
| 107 | NOTE: CFI needs to be disabled at the **caller** function, (i.e. a compatible |
| 108 | cfi function that calls a non-compatible one), since the check is performed |
| 109 | when the function call is performed. |
| 110 | |
| 111 | CFI and fuzzing |
| 112 | --------------- |
| 113 | |
| 114 | There is generally no advantage of using CFI and fuzzing together, because |
| 115 | they target different environments (production for CFI, debug for fuzzing). |
| 116 | |
| 117 | CFI could be used in conjunction with fuzzing to identify a broader set of |
| 118 | bugs that may not end immediately in a segmentation fault or triggering |
| 119 | an assertion. However, other sanitizers such as address and ub sanitizers |
| 120 | can identify such bugs in a more precise way than CFI. |
| 121 | |
| 122 | There is, however, an interesting use case in using CFI in conjunction with |
| 123 | fuzzing, that is to make sure that CFI is not triggering any false positive |
| 124 | in remote-but-possible parts of the code. |
| 125 | |
| 126 | CFI can be enabled with fuzzing, but with some caveats: |
| 127 | 1. Fuzzing relies on the linker performing function wrapping at link-time. |
| 128 | The standard BFD linker does not support function wrapping when LTO is |
| 129 | also enabled. The workaround is to use LLVM's lld linker. |
| 130 | 2. Fuzzing also relies on a custom linker script, which is only supported by |
| 131 | lld with version 11+. |
| 132 | |
| 133 | In other words, to compile with fuzzing and CFI, clang 11+ is required, and |
| 134 | lld needs to be used as a linker:: |
| 135 | |
| 136 | AR=llvm-ar-11 CC=clang-11 CXX=clang++-11 /path/to/configure --enable-cfi \ |
| 137 | -enable-fuzzing --extra-ldflags="-fuse-ld=lld" |
| 138 | |
| 139 | and then, compile the fuzzers as usual. |