diff --git a/CMakeLists.txt b/CMakeLists.txt
index c8ecb5295..a3bba73ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -411,6 +411,38 @@ endif()
 ################################################################################
 include(${CMAKE_SOURCE_DIR}/cmake/compiler_lto.cmake)
 
+################################################################################
+# Control flow integrity
+################################################################################
+option(ENABLE_CFI "Enable control flow integrity checking" OFF)
+if (ENABLE_CFI)
+  set(build_types_with_cfi "RELEASE" "RELWITHDEBINFO")
+  if (NOT LINK_TIME_OPTIMIZATION)
+    message(FATAL_ERROR "Cannot enable control flow integrity checking without link-time optimization."
+      "You should set LINK_TIME_OPTIMIZATION to ON or ENABLE_CFI to OFF.")
+  endif()
+  if (DEFINED CMAKE_CONFIGURATION_TYPES)
+    # Multi configuration generator
+    message(STATUS "Note CFI is only enabled for the following configurations: ${build_types_with_cfi}")
+    # No need for else because this is the same as the set that LTO requires.
+  endif()
+  if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+    z3_add_cxx_flag("-fsanitize=cfi" REQUIRED)
+    z3_add_cxx_flag("-fsanitize-cfi-cross-dso" REQUIRED)
+  elseif ("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
+    z3_add_cxx_flag("/guard:cf" REQUIRED)
+    message(STATUS "Enabling CFI for MSVC")
+    foreach (_build_type ${build_types_with_cfi})
+      message(STATUS "Enabling CFI for MSVC")
+      string(APPEND CMAKE_EXE_LINKER_FLAGS_${_build_type} " /GUARD:CF")
+      string(APPEND CMAKE_SHARED_LINKER_FLAGS_${_build_type} " /GUARD:CF")
+    endforeach()
+  else()
+    message(FATAL_ERROR "Can't enable control flow integrity for compiler \"${CMAKE_CXX_COMPILER_ID}\"."
+      "You should set ENABLE_CFI to OFF or use Clang or MSVC to compile.")
+  endif()
+endif()
+
 ################################################################################
 # MSVC specific flags inherited from old build system
 ################################################################################
diff --git a/README-CMake.md b/README-CMake.md
index 715cacad2..605c14818 100644
--- a/README-CMake.md
+++ b/README-CMake.md
@@ -265,6 +265,8 @@ The following useful options can be passed to CMake whilst configuring.
 * ``ALWAYS_BUILD_DOCS`` - BOOL. If set to ``TRUE`` and ``BUILD_DOCUMENTATION`` is ``TRUE`` then documentation for API bindings will always be built.
     Disabling this is useful for faster incremental builds. The documentation can be manually built by invoking the ``api_docs`` target.
 * ``LINK_TIME_OPTIMIZATION`` - BOOL. If set to ``TRUE`` link time optimization will be enabled.
+* ``ENABLE_CFI`` - BOOL. If set to ``TRUE`` will enable Control Flow Integrity security checks. This is only supported by MSVC and Clang and will
+    fail on other compilers. This requires LINK_TIME_OPTIMIZATION to also be enabled.
 * ``API_LOG_SYNC`` - BOOL. If set to ``TRUE`` will enable experimental API log sync feature.
 * ``WARNINGS_AS_ERRORS`` - STRING. If set to ``TRUE`` compiler warnings will be treated as errors. If set to ``False`` compiler warnings will not be treated as errors.
     If set to ``SERIOUS_ONLY`` a subset of compiler warnings will be treated as errors.
diff --git a/cmake/msvc_legacy_quirks.cmake b/cmake/msvc_legacy_quirks.cmake
index 2ca20277c..36fe82bb3 100644
--- a/cmake/msvc_legacy_quirks.cmake
+++ b/cmake/msvc_legacy_quirks.cmake
@@ -184,7 +184,12 @@ foreach (_build_type ${_build_types_as_upper})
     # Address space layout randomization
     # See https://msdn.microsoft.com/en-us/library/bb384887.aspx
     string(APPEND CMAKE_EXE_LINKER_FLAGS_${_build_type} " /DYNAMICBASE")
-    string(APPEND CMAKE_SHARED_LINKER_FLAGS_${_build_type} " /DYNAMICBASE:NO")
+    if(ENABLE_CFI)
+      # CFI requires /DYNAMICBASE to be enabled.
+      string(APPEND CMAKE_SHARED_LINKER_FLAGS_${_build_type} " /DYNAMICBASE")
+    else()
+      string(APPEND CMAKE_SHARED_LINKER_FLAGS_${_build_type} " /DYNAMICBASE:NO")
+    endif()
 
     # FIXME: This is not necessary. This is MSVC's default.
     # Indicate that the executable is compatible with DEP