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