control-flow-integrity.rst 5.6 KB

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