-
-
Save EKl40/f89e32acde20fca84140b14646d1cc7f to your computer and use it in GitHub Desktop.
Revisions
-
alvinhochun revised this gist
Feb 10, 2023 . 1 changed file with 7 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -189,7 +189,12 @@ https://reviews.llvm.org/D132810 adds `-mguard=cf` and `-mguard=cf-nochecks` to Clang's GNU driver. https://reviews.llvm.org/D132808 adds `--guard-cf` and `--guard-longjmp` to LLD's MinGW driver. These flags are set automatically if linking with the clang or clang++ driver with the proper `-mguard=xxx` flags. These flags are available since LLVM 16. #### Legacy info for LLVM 15 or earlier Before these, Clang does not map any of the CFGuard flags from the GCC-style driver frontend, therefore we need to use the cc1 flags by prepending them @@ -215,7 +220,7 @@ prepending them with `-Xlink`. **Note**: For the MSVC linker `/guard:cf` actually implies `/guard:cf,longjmp`. It seems that LLD not generating the long jump table for `/guard:cf` would be a bug. Addressed in https://reviews.llvm.org/D132901. When compiling and linking in a single step, the command line may look like this: -
alvinhochun revised this gist
Sep 3, 2022 . 1 changed file with 12 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -48,6 +48,9 @@ names used by MSVC and LLVM to hold the pointer to the CFGuard check functions. There needs to be a way for users to disable CFGuard checks for a specific function. MSVC and Clang supports `__declspec(guard(nocf))` for this. Since https://reviews.llvm.org/D132661 has landed, this attribute also works when compiling for MinGW target. ### 2. Collecting all valid call targets into object file CFGuard has to know what addresses are valid call targets, which means the @@ -182,9 +185,15 @@ hooking mechanism can trip off the CFGuard checks). ### Compiler and linker flags https://reviews.llvm.org/D132810 adds `-mguard=cf` and `-mguard=cf-nochecks` to Clang's GNU driver. https://reviews.llvm.org/D132808 adds `--guard-cf` and `--guard-longjmp` to LLD's MinGW driver. Before these, Clang does not map any of the CFGuard flags from the GCC-style driver frontend, therefore we need to use the cc1 flags by prepending them with `-Xclang`. | `clang-cl` option | `cc1` option | |----------------------|----------------------| -
alvinhochun revised this gist
Aug 27, 2022 . 1 changed file with 5 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -200,10 +200,14 @@ prepending them with `-Xlink`. - `-guard:longjmp`: needed to link the "Guard Long Jump Target" table into the final image. - `-guard:cf,longjmp`: this doesn't work via `-Wl` because it splits arguments by comma. Use `-Xlinker -Xlink -Xlinker -guard:cf,longjmp` instead. Also, `-guard:longjmp` already implies `-guard:cf` so it is not really necessary to specify both. **Note**: For the MSVC linker `/guard:cf` actually implies `/guard:cf,longjmp`. It seems that LLD not generating the long jump table for `/guard:cf` would be a bug. When compiling and linking in a single step, the command line may look like this: -
alvinhochun revised this gist
Aug 26, 2022 . 1 changed file with 11 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -122,7 +122,9 @@ supplied symbols as absolute VA (at least for LLD). The definition of - `__guard_fids_count` - `__guard_flags` - CFGuard flags for `_load_config_used.GuardFlags`, should be `IMAGE_GUARD_CF_INSTRUMENTED | IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT` (`0x500`) for CFGuard-enabled images. In addition, if the image includes a "Guard Long Jump Target" table, the `IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT` bit should be set. This value is 32-bit. - `__guard_iat_table` - `__guard_iat_count` - `__guard_longjmp_table` @@ -194,6 +196,14 @@ Similarly, the MinGW driver of LLD currently does not map the CFGuard flags to its COFF linker, therefore we need to use the link.exe-style flags by prepending them with `-Xlink`. - `-guard:cf` - `-guard:longjmp`: needed to link the "Guard Long Jump Target" table into the final image. - `-guard:cf,longjmp`: this doesn't work via `-Wl` because it splits arguments by comma. Use `-Wl,-Xlink,-guard:cf,-Xlink,-guard:longjmp` instead. Also, `-guard:longjmp` already implies `-guard:cf` so it is not really necessary to specify both. When compiling and linking in a single step, the command line may look like this: -
alvinhochun revised this gist
Aug 24, 2022 . 1 changed file with 5 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -65,12 +65,12 @@ uses these sections: targets. - `.gljmp$y` - "Guard Long Jump Target": These are locations which shall be considered valid long jump targets. - ~~`.gehcont$y` - "Guard EH Continuation": These are locations which shall be considered valid exception handling continuation targets. Note: Guard EH Continuation is a later addition in VS 2019 16.7 and only supported on 64-bit. It is only enabled when `/guard:ehcont` is passed to the compiler. Technically this is not part of CFGuard, but a separate feature called Control-flow Enforcement Technology (CET).~~ Let's ignore this for now. Clang uses the assembly directive `.symidx` to build these sections. Each entry is a 4-byte index of the symbol in the symbols table. @@ -91,9 +91,9 @@ Windows that does not support CFGuard. ### 5. Link the guard tables The linker shall create the tables (GuardCFFunctionTable, GuardAddressTakenIatEntryTable, GuardLongJumpTargetTable ~~and GuardEHContinuationTable~~) by collecting all symbols referenced from the guard sections (`.gfids$y`, `.giats$y`, `.gljmp$y` ~~and `.gehcont$y`~~ respectively) from all object files. The format of the tables are described in [MS docs][cfg_metadata]. -
alvinhochun revised this gist
Aug 23, 2022 . 1 changed file with 7 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -66,7 +66,11 @@ uses these sections: - `.gljmp$y` - "Guard Long Jump Target": These are locations which shall be considered valid long jump targets. - `.gehcont$y` - "Guard EH Continuation": These are locations which shall be considered valid exception handling continuation targets. Note: Guard EH Continuation is a later addition in VS 2019 16.7 and only supported on 64-bit. It is only enabled when `/guard:ehcont` is passed to the compiler. Technically this is not part of CFGuard, but a separate feature called Control-flow Enforcement Technology (CET). Clang uses the assembly directive `.symidx` to build these sections. Each entry is a 4-byte index of the symbol in the symbols table. @@ -123,8 +127,8 @@ supplied symbols as absolute VA (at least for LLD). The definition of - `__guard_iat_count` - `__guard_longjmp_table` - `__guard_longjmp_count` - `__guard_eh_cont_table`: TODO - `__guard_eh_cont_count`: TODO In addition, these symbols are also to be referenced in `_load_config_used`: -
alvinhochun created this gist
Aug 23, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,220 @@ # Control Flow Guard (CFG/CFGuard) for mingw-w64 [Control Flow Guard][cfguard] is a security mitigation that verifies the target address of indirect calls. It works by having the compiler insert a check at indirect call sites to verify the validity of the call target, and also the linker write the necessary data and flags into the PE/COFF image to enable the feature on Windows' end. [cfguard]: https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard This feature was first introduced on Windows 8.1 and came with VS 2015. Existing mingw-w64 toolchains do not support it. As far as I can tell, GCC does not know about this, neither does ld.bfd. LLVM does have it implemented for the MSVC target and it can be used with mingw-w64 through some hacks. ## Technical details These are needed to use control flow guard: ### 1. Adding checks to indirect calls The checks are basically a transformation of this: ``` leaq target_func(%rip), %rax callq *%rax ``` ...into this: ``` movl $target_func, %ecx calll *___guard_check_icall_fptr calll *%ecx ``` ...or in the case of x86_64: ``` leaq target_func(%rip), %rax callq *__guard_dispatch_icall_fptr(%rip) ``` `__guard_check_icall_fptr` and `__guard_dispatch_icall_fptr` are the symbol names used by MSVC and LLVM to hold the pointer to the CFGuard check functions. There needs to be a way for users to disable CFGuard checks for a specific function. MSVC and Clang supports `__declspec(guard(nocf))` for this. ### 2. Collecting all valid call targets into object file CFGuard has to know what addresses are valid call targets, which means the compiler needs to keep track of them and write them into the object file. Any functions that has its address taken shall be considered, because chances are they will be used in an indirect call somewhere else. MSVC (technically I'm just describing what LLVM/Clang does for the MSVC target) uses these sections: - `.gfids$y` - "Guard Function ID": These are symbols within this object file which shall be considered valid call targets. - `.giats$y` - "Guard Address-Taken IAT Entry": These are functions from the IAT which has had its address taken, which shall be considered valid call targets. - `.gljmp$y` - "Guard Long Jump Target": These are locations which shall be considered valid long jump targets. - `.gehcont$y` - "Guard EH Continuation": These are locations which shall be considered valid exception handling continuation targets. Clang uses the assembly directive `.symidx` to build these sections. Each entry is a 4-byte index of the symbol in the symbols table. ### 3. Set `@feat.00` symbol The `0x800` bit shall be set in this value to indicate that this object has been compiled with CFGuard. ### 4. Supply placeholder CFGuard check/dispatch functions and pointers VC runtime supplies `void *__guard_check_icall_fptr`, and also `void *__guard_dispatch_icall_fptr` for x86_64. They are initialized with the addresses of default placeholder CFGuard check/dispatch functions which does no checking. This makes the executables backward-compatible with older Windows that does not support CFGuard. ### 5. Link the guard tables The linker shall create the tables (GuardCFFunctionTable, GuardAddressTakenIatEntryTable, GuardLongJumpTargetTable and GuardEHContinuationTable) by collecting all symbols referenced from the guard sections (`.gfids$y`, `.giats$y`, `.gljmp$y` and `.gehcont$y` respectively) from all object files. The format of the tables are described in [MS docs][cfg_metadata]. [cfg_metadata]: https://docs.microsoft.com/en-us/windows/win32/secbp/pe-metadata ### 6. Include the Load Config directory The load config directory contains fields essential to the operation of CFG. LINK.exe takes the `_load_config_used` symbol and uses it as the load config directory. LLD does the same thing, though it considers this to be optional. Ld.bfd likely does not handle this at the moment. VC runtime appears to supply a default `_load_config_used` symbol. It apparently can be overridden from user code, though I don't see this in official docs. LINK.exe does have checks in place to attempt to verify the validity of the structure of this symbol (it needs to contain certain values). Mingw-w64 does not currently supply this symbol, but user code can supply it and LLD will take that. Specific data for the `_load_config_used` structure is provided by linker- supplied symbols as absolute VA (at least for LLD). The definition of `_load_config_used` shall include them. - `__guard_fids_table` - `__guard_fids_count` - `__guard_flags` - CFGuard flags for `_load_config_used.GuardFlags`, should be `IMAGE_GUARD_CF_INSTRUMENTED | IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT` (`0x500`) for CFGuard-enabled images. This value is 32-bit. - `__guard_iat_table` - `__guard_iat_count` - `__guard_longjmp_table` - `__guard_longjmp_count` - `__guard_eh_cont_table` - `__guard_eh_cont_count` In addition, these symbols are also to be referenced in `_load_config_used`: - `__guard_check_icall_fptr` - location that stores a pointer to the CFGuard check function. `_load_config_used.GuardCFCheckFunction` shall point to this location. - `__guard_dispatch_icall_fptr` - location that stores a pointer to the CFGuard dispatch function (only for x86_64). `_load_config_used.GuardCFCheckDispatch` shall point to this location for x86_64 images. There are also these symbols that are not applicable for mingw-w64: - `__security_cookie`: used by MSVC for `/GS` buffer security checks (it may also be reused for other hardening features); GCC and Clang supports `-fstack-protector`, but `__stack_chk_guard` is used instead (Clang uses `__security_cookie` only with the MSVC target). - `__safe_se_handler_table`, `__safe_se_handler_count`: this table is only for i686, and mingw-w64 does not support SafeSEH on i686. (TODO: needs verification) - `__enclave_config`: ??? ### 7. Set the `DLLCharacteristics` field in the image optional header The `IMAGE_DLL_CHARACTERISTICS_GUARD_CF` (`0x4000`) bit shall be set. ## Enabling with Clang See also https://github.com/mstorsjo/llvm-mingw/issues/301. ### Additions needed in mingw-w64 To support the use case of enabling CFGuard with Clang, mingw-w64 should: - Supply `__guard_check_icall_fptr` and its placeholder implementation. - Supply `__guard_dispatch_icall_fptr` and its placeholder implementation (only for x86_64). - Supply a default `_load_config_used` structure. All these can be included in `libmingwex.a` within `mingw-w64-crt`. Alternatively, the user can supply these symbols themselves as a stopgap measure. ### Toolchain distribution changes When compiling the toolchain, everything needs to be compiled and linked with the CFGuard-enabling flags, with the exception of sanitizer runtimes (the API hooking mechanism can trip off the CFGuard checks). ### Compiler and linker flags Clang currently does not map any of the CFGuard flags from the GCC-style driver frontend, therefore we need to use the cc1 flags by prepending them with `-Xclang`. | `clang-cl` option | `cc1` option | |----------------------|----------------------| | `/guard:cf` | `-cfguard` | | `/guard:cf,nochecks` | `-cfguard-no-checks` | | `/guard:ehcont` | `-ehcontguard` | Similarly, the MinGW driver of LLD currently does not map the CFGuard flags to its COFF linker, therefore we need to use the link.exe-style flags by prepending them with `-Xlink`. When compiling and linking in a single step, the command line may look like this: ``` clang++ main.cpp -o main.exe -Xclang -cfguard -Wl,-Xlink,-guard:cf ``` ### Additional attributes Clang supports the function attribute `__declspec(guard(nocf))` to disable CFGuard checks inside a specific function. This will be usable in MinGW mode without needing hacks after [D132302] has landed. [D132302]: https://reviews.llvm.org/D132302 ## For GCC and ld.bfd Here is what I think should be done: 1. Ld.bfd needs to be taught how to link the guard tables and the load config directory. An option should be added to enable this. 2. As a starting point, GCC should be taught how to generate the guard table sections, equivalent to the `-cfguard-no-checks` option of Clang. 3. After gaining support for the above, GCC should be taught to implement CFGuard checks by transforming indirect calls as required, with the support of a new function attribute `guard(nocf)` to disable these checks for the specific function.