On Thu, 22 Sep 2022, Martin Storsjö wrote:
On Thu, 22 Sep 2022, Alvin Wong via Mingw-w64-public wrote:
This adds support to enable building mingw-w64-crt and user code with
Control Flow Guard using Clang, with the option `--enable-cfguard`. With
this option enabled, the Clang 16 compiler option `-mguard=cf` will be
added to CFLAGS when building mingw-w64-crt.
Two new objects are also added to `libmingwex.a` to support CFGuard:
- `mingw_cfguard_support.c` contains the guard check and dispatch
function pointers and their backward compatible no-op.
* This object will be built regardless of the `--enable-cfguard`
option, to allow linking CFGuard-enabled objects to a mingw-w64-crt
built without CFGuard (the guard checks will effectively become
no-ops.)
- `mingw_cfguard_loadcfg.S` contains the definition of a load config
directory structure with the symbol name `_load_config_used`.
* This object is only built when `--enable-cfguard` is enabled,
because mingw-w64 doesn't use the load config directory for
anything else yet. Checks have been added to LLD 16 such that, if
CFGuard has been requested but the needed load config data is
missing, it will emit a warning.
This change alone is not enough to fully enable CFGuard in user-built
binaries. All other libraries shipped with the toolchain will also need
to be built with `-mguard=cf` to include CFGuard checks. Same goes for
building user code. If the user builds and links an executable without
`-mguard=cf`, it will still run normally but without any of the CFGuard
checks being operational.
(An exception is the sanitizer libraries -- their API hooking mechanisms
may result in indirect calls that fails CFGuard checks.)
User code may use the function attribute `__declspec(guard(nocf))`
(supported by Clang 16) to stop CFGuard checks from being added to
indirect calls within one function.
===
CFGuard works by having the compiler insert a check before performing
any indirect calls (which terminates the process if the call target is
invalid). The compiler also has to keep track of all the address-taken
functions, which the linker will then use to assemble a
`GuardCFFunctionTable` to list all valid indirect call target addresses.
To maintain backward compatibile, the guard check function is referenced
via a function pointer which points to a no-op by default. On systems
supporting CFGuard, the image loader replaces the function pointer with
the address of the actual guard check function.
For this to work, the metadata is stored in the load config directory
struct. MSVC supplies this struct in vcruntime as a symbol with the name
`_load_config_used`, which the linker takes as the load config
directory. In addition, the linker generates synthetic symbols to be
included in the load config struct. This also works with LLD since it
has implemented this feature.
The load config directory can be inspected using `llvm-readobj
--coff-load-config file.exe` or `dumpbin.exe /loadconfig file.exe`
(dumpbin.exe is included in MSVC build tools.)
Further reading:
- Control Flow Guard:
https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard
- Load config structure:
https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-load-configuration-structure-image-only
- Control Flow Guard metadata:
https://docs.microsoft.com/en-us/windows/win32/secbp/pe-metadata
- My personal notes on CFGuard:
https://gist.github.com/alvinhochun/a65e4177e2b34d551d7ecb02b55a4b0a
Signed-off-by: Alvin Wong <al...@alvinhc.com>
---
Reworded the commit message to be a bit clearer on what's included when
the flag is not specified.
Great stuff! If the only thing changed is the commit message, then that does
seem good to me. I can go ahead and push this after a day or two, to let
others still chime in, if they would happen to have concerns.
I pushed this now - thanks for your contribution!
// Martin
_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public