Add Windows Control Flow Guard checks (/guard:cf).
Summary:
A new function pass (Transforms/CFGuard/CFGuard.cpp) inserts CFGuard checks on
indirect function calls, using either the check mechanism (X86, ARM, AArch64) or
or the dispatch mechanism (X86-64). The check mechanism requires a new calling
convention for the supported targets. The dispatch mechanism adds the target as
an operand bundle, which is processed by SelectionDAG. Another pass
(CodeGen/CFGuardLongjmp.cpp) identifies and emits valid longjmp targets, as
required by /guard:cf. This feature is enabled using the `cfguard` CC1 option.
Reviewers: thakis, rnk, theraven, pcc
Subscribers: ychen, hans, metalcanine, dmajor, tomrittervg, alex, mehdi_amini, mgorny, javed.absar, kristof.beyls, hiraditya, steven_wu, dexonsmith, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D65761
diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst
index 5f6bb98..e8d561f 100644
--- a/clang/docs/ClangCommandLineReference.rst
+++ b/clang/docs/ClangCommandLineReference.rst
@@ -124,7 +124,11 @@
.. option:: -cfguard
-Emit tables required for Windows Control Flow Guard.
+Emit tables and checks for Windows Control Flow Guard.
+
+.. option:: -cfguard-no-checks
+
+Emit tables required for Windows Control Flow Guard without checks.
.. option:: -client\_name<arg>
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index d2266cc..f8d94e3 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -37,6 +37,7 @@
CODEGENOPT(Autolink , 1, 1) ///< -fno-autolink
CODEGENOPT(ObjCAutoRefCountExceptions , 1, 0) ///< Whether ARC should be EH-safe.
CODEGENOPT(Backchain , 1, 0) ///< -mbackchain
+CODEGENOPT(ControlFlowGuardNoChecks , 1, 0) ///< -cfguard-no-checks
CODEGENOPT(ControlFlowGuard , 1, 0) ///< -cfguard
CODEGENOPT(CoverageExtraChecksum, 1, 0) ///< Whether we need a second checksum for functions in GCNO files.
CODEGENOPT(CoverageNoFunctionNamesInData, 1, 0) ///< Do not include function names in GCDA files.
diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td
index 72019aa..1d550eb 100644
--- a/clang/include/clang/Driver/CC1Options.td
+++ b/clang/include/clang/Driver/CC1Options.td
@@ -400,6 +400,10 @@
Values<"a_key,b_key">;
def mbranch_target_enforce : Flag<["-"], "mbranch-target-enforce">;
def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">;
+def cfguard_no_checks : Flag<["-"], "cfguard-no-checks">,
+ HelpText<"Emit Windows Control Flow Guard tables only (no checks)">;
+def cfguard : Flag<["-"], "cfguard">,
+ HelpText<"Emit Windows Control Flow Guard tables and checks">;
//===----------------------------------------------------------------------===//
// Dependency Output Options
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 1c3d7f7..2401a31 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -503,8 +503,6 @@
def bundle__loader : Separate<["-"], "bundle_loader">;
def bundle : Flag<["-"], "bundle">;
def b : JoinedOrSeparate<["-"], "b">, Flags<[Unsupported]>;
-def cfguard : Flag<["-"], "cfguard">, Flags<[CC1Option]>,
- HelpText<"Emit tables required for Windows Control Flow Guard.">;
def cl_opt_disable : Flag<["-"], "cl-opt-disable">, Group<opencl_Group>, Flags<[CC1Option]>,
HelpText<"OpenCL only. This option disables all optimizations. By default optimizations are enabled.">;
def cl_strict_aliasing : Flag<["-"], "cl-strict-aliasing">, Group<opencl_Group>, Flags<[CC1Option]>,
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 75708d6..480a33f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -482,8 +482,11 @@
getModule().addModuleFlag(llvm::Module::Warning, "CodeViewGHash", 1);
}
if (CodeGenOpts.ControlFlowGuard) {
- // We want function ID tables for Control Flow Guard.
- getModule().addModuleFlag(llvm::Module::Warning, "cfguardtable", 1);
+ // Function ID tables and checks for Control Flow Guard (cfguard=2).
+ getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 2);
+ } else if (CodeGenOpts.ControlFlowGuardNoChecks) {
+ // Function ID tables for Control Flow Guard (cfguard=1).
+ getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 1);
}
if (CodeGenOpts.OptimizationLevel > 0 && CodeGenOpts.StrictVTablePointers) {
// We don't support LTO with 2 with different StrictVTablePointers
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index ae12465..c60dc76 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5975,26 +5975,19 @@
}
if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) {
- SmallVector<StringRef, 1> SplitArgs;
- StringRef(A->getValue()).split(SplitArgs, ",");
- bool Instrument = false;
- bool NoChecks = false;
- for (StringRef Arg : SplitArgs) {
- if (Arg.equals_lower("cf"))
- Instrument = true;
- else if (Arg.equals_lower("cf-"))
- Instrument = false;
- else if (Arg.equals_lower("nochecks"))
- NoChecks = true;
- else if (Arg.equals_lower("nochecks-"))
- NoChecks = false;
- else
- D.Diag(diag::err_drv_invalid_value) << A->getSpelling() << Arg;
- }
- // Currently there's no support emitting CFG instrumentation; the flag only
- // emits the table of address-taken functions.
- if (Instrument || NoChecks)
+ StringRef GuardArgs = A->getValue();
+ // The only valid options are "cf", "cf,nochecks", and "cf-".
+ if (GuardArgs.equals_lower("cf")) {
+ // Emit CFG instrumentation and the table of address-taken functions.
CmdArgs.push_back("-cfguard");
+ } else if (GuardArgs.equals_lower("cf,nochecks")) {
+ // Emit only the table of address-taken functions.
+ CmdArgs.push_back("-cfguard-no-checks");
+ } else if (GuardArgs.equals_lower("cf-")) {
+ // Do nothing, but we might want to emit a security warning in future.
+ } else {
+ D.Diag(diag::err_drv_invalid_value) << A->getSpelling() << GuardArgs;
+ }
}
}
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 1d31844..4e143f6 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -422,6 +422,17 @@
Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link);
+ // Control Flow Guard checks
+ if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) {
+ StringRef GuardArgs = A->getValue();
+ if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) {
+ // MSVC doesn't yet support the "nochecks" modifier.
+ CmdArgs.push_back("-guard:cf");
+ } else if (GuardArgs.equals_lower("cf-")) {
+ CmdArgs.push_back("-guard:cf-");
+ }
+ }
+
if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,
options::OPT_fno_openmp, false)) {
CmdArgs.push_back("-nodefaultlib:vcomp.lib");
@@ -679,6 +690,17 @@
: "/Zc:threadSafeInit-");
}
+ // Control Flow Guard checks
+ if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) {
+ StringRef GuardArgs = A->getValue();
+ if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) {
+ // MSVC doesn't yet support the "nochecks" modifier.
+ CmdArgs.push_back("/guard:cf");
+ } else if (GuardArgs.equals_lower("cf-")) {
+ CmdArgs.push_back("/guard:cf-");
+ }
+ }
+
// Pass through all unknown arguments so that the fallback command can see
// them too.
Args.AddAllArgs(CmdArgs, options::OPT_UNKNOWN);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index f6e6f71..f197a67 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1003,6 +1003,7 @@
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier);
+ Opts.ControlFlowGuardNoChecks = Args.hasArg(OPT_cfguard_no_checks);
Opts.ControlFlowGuard = Args.hasArg(OPT_cfguard);
Opts.DisableGCov = Args.hasArg(OPT_test_coverage);
diff --git a/clang/test/CodeGen/cfguardtable.c b/clang/test/CodeGen/cfguardtable.c
index 9649292..05284f6 100644
--- a/clang/test/CodeGen/cfguardtable.c
+++ b/clang/test/CodeGen/cfguardtable.c
@@ -1,6 +1,8 @@
-// RUN: %clang_cc1 -cfguard -emit-llvm %s -o - | FileCheck %s
-
-void f() {}
-
-// Check that the cfguardtable metadata flag gets set on the module.
-// CHECK: !"cfguardtable", i32 1}
+// RUN: %clang_cc1 -cfguard-no-checks -emit-llvm %s -o - | FileCheck %s -check-prefix=CFGUARDNOCHECKS
+// RUN: %clang_cc1 -cfguard -emit-llvm %s -o - | FileCheck %s -check-prefix=CFGUARD
+
+void f() {}
+
+// Check that the cfguard metadata flag gets correctly set on the module.
+// CFGUARDNOCHECKS: !"cfguard", i32 1}
+// CFGUARD: !"cfguard", i32 2}
diff --git a/clang/test/Driver/cl-fallback.c b/clang/test/Driver/cl-fallback.c
index 2e5698c..a840883 100644
--- a/clang/test/Driver/cl-fallback.c
+++ b/clang/test/Driver/cl-fallback.c
@@ -2,7 +2,7 @@
// command-line option, e.g. on Mac where %s is commonly under /Users.
// RUN: %clang_cl --target=i686-pc-win32 /fallback /Dfoo=bar /Ubaz /Ifoo /O0 /Ox /GR /GR- /GS /GS- /Gy /Gy- \
-// RUN: /Gw /Gw- /LD /LDd /EHs /EHs- /Zl /MD /MDd /MTd /MT /FImyheader.h /Zi \
+// RUN: /Gw /Gw- /LD /LDd /EHs /EHs- /Zl /MD /MDd /MTd /MT /guard:cf /guard:cf- /FImyheader.h /Zi \
// RUN: -garbage -moregarbage \
// RUN: -### -- %s 2>&1 \
// RUN: | FileCheck %s
@@ -33,6 +33,7 @@
// CHECK: "/EHs-"
// CHECK: "/Zl"
// CHECK: "/MT"
+// CHECK: "/guard:cf-"
// CHECK: "-garbage"
// CHECK: "-moregarbage"
// CHECK: "/Tc" "{{.*cl-fallback.c}}"
diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c
index 354ed99..b82d69b 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -597,9 +597,14 @@
// NOCFGUARD-NOT: -cfguard
// RUN: %clang_cl /guard:cf -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s
-// RUN: %clang_cl /guard:cf,nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s
-// RUN: %clang_cl /guard:nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s
// CFGUARD: -cfguard
+// CFGUARD-NOT: -cfguard-no-checks
+
+// RUN: %clang_cl /guard:cf,nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDNOCHECKS %s
+// CFGUARDNOCHECKS: -cfguard-no-checks
+
+// RUN: %clang_cl /guard:nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDNOCHECKSINVALID %s
+// CFGUARDNOCHECKSINVALID: invalid value 'nochecks' in '/guard:'
// RUN: %clang_cl /guard:foo -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDINVALID %s
// CFGUARDINVALID: invalid value 'foo' in '/guard:'