Skip to content

Support Control Flow Guard (CFG/CFGuard) on Windows #301

Closed
@alvinhochun

Description

@alvinhochun

Control Flow Guard is a security mitigation that verifies the
target address of indirect calls. It works by having the compiler insert
instrumentation code at indirect call sites, and also the linker write the
necessary data and flags into the PE/COFF image to enable the feature on
Windows' end.

Existing Support in LLVM

In LLVM, Clang already knows how to insert the CFGuard instrumentation code
and LLD is able to produce CFGuard data, like how MSVC does it. 1 2
Conveniently, these work the same when targeting mingw-w64 given the proper
compiler and linker flags. They however assume certain symbols to be available
in order to work, and mingw-w64 does not provide them yet. For example, if
you try to compile a source file into an x86_64 EXE with -Xclang -cfguard,
you may get this:

ld.lld: error: undefined symbol: __guard_dispatch_icall_fptr

To be compatible with MSVC, Clang expects the symbol __guard_dispatch_icall_fptr
on x86_64, and __guard_check_icall_fptr on x86, arm and aarch64. In addition,
LLD uses the contents of the _load_config_used symbol as the load config
directory, which contains CFGuard support flags and data pointers.

Using with mingw-w64

If we are fine with reusing these existing support in LLVM as-is, here is
what we need to do:

  • Add the symbols __guard_check_icall_fptr and __guard_dispatch_icall_fptr
    along with the corresponding placeholder functions (to be used when running
    with CFGuard disabled or unavailable).
  • Provide a default _load_config_used data structure which LLD will use
    as the load config directory, and fill its fields with the LLD-provided
    symbols needed for CFGuard.
  • Compile all included libraries (e.g. mingw-w64-*, compiler-rt, libc++)
    with CFGuard enabled. This means adding -Xclang -cfguard to compile flags
    and -Wl,-Xlink,-guard:cf to link flags.

Users will still need to opt into enabling CFGuard by passing the same compile
flags and link flags. For users who does not enable CFGuard, this setup does
add a small amount of bloat:

  • Every indirect calls in the included libraries get an overhead of several
    instructions and a far call.
  • All images linked with LLD will include the default load config directory,
    which may add a couple hundred bytes to the image.

What About GCC and Binutils?

As far as I can tell, there is zero support for CFGuard in GCC and Binutils.
These changes should not affect a GCC-based mingw-w64 toolchain.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions