diff --git a/clang/include/clang/Basic/CFProtectionOptions.def b/clang/include/clang/Basic/CFProtectionOptions.def new file mode 100644 index 00000000000000..b9df2de7f7eba0 --- /dev/null +++ b/clang/include/clang/Basic/CFProtectionOptions.def @@ -0,0 +1,15 @@ +//===-- CFProtectionOptions.def - cf-protection options ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +#ifdef CF_BRANCH_LABEL_SCHEME +CF_BRANCH_LABEL_SCHEME(Unlabeled, unlabeled) +CF_BRANCH_LABEL_SCHEME(FuncSig, func-sig) + +#undef CF_BRANCH_LABEL_SCHEME +#endif // #ifdef CF_BRANCH_LABEL_SCHEME diff --git a/clang/include/clang/Basic/CFProtectionOptions.h b/clang/include/clang/Basic/CFProtectionOptions.h new file mode 100644 index 00000000000000..13f46d4c13e7e7 --- /dev/null +++ b/clang/include/clang/Basic/CFProtectionOptions.h @@ -0,0 +1,38 @@ +//===--- CFProtectionOptions.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines constants for -fcf-protection and other related flags. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_CFPROTECTIONOPTIONS_H +#define LLVM_CLANG_BASIC_CFPROTECTIONOPTIONS_H + +#include "llvm/Support/ErrorHandling.h" + +namespace clang { + +enum class CFBranchLabelSchemeKind { + Default, +#define CF_BRANCH_LABEL_SCHEME(Kind, FlagVal) Kind, +#include "clang/Basic/CFProtectionOptions.def" +}; + +static inline const char * +getCFBranchLabelSchemeFlagVal(const CFBranchLabelSchemeKind Scheme) { +#define CF_BRANCH_LABEL_SCHEME(Kind, FlagVal) \ + if (Scheme == CFBranchLabelSchemeKind::Kind) \ + return #FlagVal; +#include "clang/Basic/CFProtectionOptions.def" + + llvm::report_fatal_error("invalid scheme"); +} + +} // namespace clang + +#endif // #ifndef LLVM_CLANG_BASIC_CFPROTECTIONOPTIONS_H diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index b600198998d85b..de7ae73f8a603b 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -110,6 +110,8 @@ CODEGENOPT(CFProtectionReturn , 1, 0) ///< if -fcf-protection is ///< set to full or return. CODEGENOPT(CFProtectionBranch , 1, 0) ///< if -fcf-protection is ///< set to full or branch. +ENUM_CODEGENOPT(CFBranchLabelScheme, CFBranchLabelSchemeKind, 2, + CFBranchLabelSchemeKind::Default) ///< if -mcf-branch-label-scheme is set. CODEGENOPT(FunctionReturnThunks, 1, 0) ///< -mfunction-return={keep|thunk-extern} CODEGENOPT(IndirectBranchCSPrefix, 1, 0) ///< if -mindirect-branch-cs-prefix ///< is set. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index f2a707a8ba8d76..a6953c17a447ef 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H #define LLVM_CLANG_BASIC_CODEGENOPTIONS_H +#include "clang/Basic/CFProtectionOptions.h" #include "clang/Basic/PointerAuthOptions.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/XRayInstr.h" diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index fd3346d29f26a3..68db400c22e6c1 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -364,6 +364,8 @@ BENIGN_LANGOPT(CompatibilityQualifiedIdBlockParamTypeChecking, 1, 0, LANGOPT(ObjCDisableDirectMethodsForTesting, 1, 0, "Disable recognition of objc_direct methods") LANGOPT(CFProtectionBranch , 1, 0, "Control-Flow Branch Protection enabled") +ENUM_LANGOPT(CFBranchLabelScheme, CFBranchLabelSchemeKind, 2, CFBranchLabelSchemeKind::Default, + "Control-Flow Branch Protection Label Scheme") LANGOPT(FakeAddressSpaceMap , 1, 0, "OpenCL fake address space map") ENUM_LANGOPT(AddressSpaceMapMangling , AddrSpaceMapMangling, 2, ASMM_Target, "OpenCL address space map mangling mode") LANGOPT(IncludeDefaultHeader, 1, 0, "Include default header file for OpenCL") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 6c186c410e158d..c3d53ca92d450c 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_BASIC_LANGOPTIONS_H #define LLVM_CLANG_BASIC_LANGOPTIONS_H +#include "clang/Basic/CFProtectionOptions.h" #include "clang/Basic/CommentOptions.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangStandard.h" @@ -73,6 +74,7 @@ class LangOptionsBase { public: using Visibility = clang::Visibility; using RoundingMode = llvm::RoundingMode; + using CFBranchLabelSchemeKind = clang::CFBranchLabelSchemeKind; enum GCMode { NonGC, GCOnly, HybridGC }; enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq }; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index f31d88a354ea28..57783850606290 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -16,6 +16,7 @@ #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/BitmaskEnum.h" +#include "clang/Basic/CFProtectionOptions.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -1727,6 +1728,13 @@ class TargetInfo : public TransferrableTargetInfo, virtual bool checkCFProtectionBranchSupported(DiagnosticsEngine &Diags) const; + /// Get the target default CFBranchLabelScheme scheme + virtual CFBranchLabelSchemeKind getDefaultCFBranchLabelScheme() const; + + virtual bool + checkCFBranchLabelSchemeSupported(const CFBranchLabelSchemeKind Scheme, + DiagnosticsEngine &Diags) const; + /// Check if the target supports CFProtection return. virtual bool checkCFProtectionReturnSupported(DiagnosticsEngine &Diags) const; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 002f60350543d9..ccbe302e4937e1 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2841,6 +2841,10 @@ def fcf_protection : Flag<["-"], "fcf-protection">, Group, Visibility<[ClangOption, CLOption, CC1Option]>, Alias, AliasArgs<["full"]>, HelpText<"Enable cf-protection in 'full' mode">; +def mcf_branch_label_scheme_EQ : Joined<["-"], "mcf-branch-label-scheme=">, + Visibility<[ClangOption, CC1Option]>, Group, + HelpText<"Select label scheme for branch control-flow architecture protection">, + Values<"unlabeled,func-sig">; def mfunction_return_EQ : Joined<["-"], "mfunction-return=">, Group, Visibility<[ClangOption, CLOption, CC1Option]>, HelpText<"Replace returns with jumps to ``__x86_return_thunk`` (x86 only, error otherwise)">, diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 29f5cd14e46e11..92195333821097 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -198,6 +198,22 @@ TargetInfo::checkCFProtectionBranchSupported(DiagnosticsEngine &Diags) const { return false; } +CFBranchLabelSchemeKind TargetInfo::getDefaultCFBranchLabelScheme() const { + // if this hook is called, the target should override it to return a + // non-default scheme + llvm::report_fatal_error("not implemented"); +} + +bool TargetInfo::checkCFBranchLabelSchemeSupported( + const CFBranchLabelSchemeKind Scheme, DiagnosticsEngine &Diags) const { + if (Scheme != CFBranchLabelSchemeKind::Default) + Diags.Report(diag::err_opt_not_valid_on_target) + << (Twine("mcf-branch-label-scheme=") + + getCFBranchLabelSchemeFlagVal(Scheme)) + .str(); + return false; +} + bool TargetInfo::checkCFProtectionReturnSupported(DiagnosticsEngine &Diags) const { Diags.Report(diag::err_opt_not_valid_on_target) << "cf-protection=return"; diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h index 351ef21e197c4d..bf40edb8683b3e 100644 --- a/clang/lib/Basic/Targets/RISCV.h +++ b/clang/lib/Basic/Targets/RISCV.h @@ -134,6 +134,28 @@ class RISCVTargetInfo : public TargetInfo { bool validateGlobalRegisterVariable(StringRef RegName, unsigned RegSize, bool &HasSizeMismatch) const override; + + bool checkCFProtectionBranchSupported(DiagnosticsEngine &) const override { + // Always generate Zicfilp lpad insns + // Non-zicfilp CPUs would read them as NOP + return true; + } + + CFBranchLabelSchemeKind getDefaultCFBranchLabelScheme() const override { + return CFBranchLabelSchemeKind::FuncSig; + } + + bool + checkCFBranchLabelSchemeSupported(const CFBranchLabelSchemeKind Scheme, + DiagnosticsEngine &Diags) const override { + switch (Scheme) { + case CFBranchLabelSchemeKind::Default: + case CFBranchLabelSchemeKind::Unlabeled: + case CFBranchLabelSchemeKind::FuncSig: + return true; + } + return TargetInfo::checkCFBranchLabelSchemeSupported(Scheme, Diags); + } }; class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo { public: diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d53d47979f29fb..2381fa93e23fea 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1164,6 +1164,16 @@ void CodeGenModule::Release() { // Indicate that we want to instrument branch control flow protection. getModule().addModuleFlag(llvm::Module::Min, "cf-protection-branch", 1); + + auto Scheme = CodeGenOpts.getCFBranchLabelScheme(); + if (Target.checkCFBranchLabelSchemeSupported(Scheme, getDiags())) { + if (Scheme == CFBranchLabelSchemeKind::Default) + Scheme = Target.getDefaultCFBranchLabelScheme(); + getModule().addModuleFlag( + llvm::Module::Error, "cf-branch-label-scheme", + llvm::MDString::get(getLLVMContext(), + getCFBranchLabelSchemeFlagVal(Scheme))); + } } if (CodeGenOpts.FunctionReturnThunks) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 0bab48caf1a5e2..d3c5c3d9872ee4 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7012,6 +7012,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Arg *A = Args.getLastArg(options::OPT_fcf_protection_EQ)) { CmdArgs.push_back( Args.MakeArgString(Twine("-fcf-protection=") + A->getValue())); + + if (Arg *SA = Args.getLastArg(options::OPT_mcf_branch_label_scheme_EQ)) + CmdArgs.push_back(Args.MakeArgString(Twine("-mcf-branch-label-scheme=") + + SA->getValue())); } if (Arg *A = Args.getLastArg(options::OPT_mfunction_return_EQ)) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index efd852593468aa..6c09843a7146f4 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1688,6 +1688,18 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts, else if (Opts.CFProtectionBranch) GenerateArg(Consumer, OPT_fcf_protection_EQ, "branch"); + if (Opts.CFProtectionBranch) { + switch (Opts.getCFBranchLabelScheme()) { + case CFBranchLabelSchemeKind::Default: + break; +#define CF_BRANCH_LABEL_SCHEME(Kind, FlagVal) \ + case CFBranchLabelSchemeKind::Kind: \ + GenerateArg(Consumer, OPT_mcf_branch_label_scheme_EQ, #FlagVal); \ + break; +#include "clang/Basic/CFProtectionOptions.def" + } + } + if (Opts.FunctionReturnThunks) GenerateArg(Consumer, OPT_mfunction_return_EQ, "thunk-extern"); @@ -2022,6 +2034,22 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; } + if (Opts.CFProtectionBranch && T.isRISCV()) { + if (const Arg *A = Args.getLastArg(OPT_mcf_branch_label_scheme_EQ)) { + const auto Scheme = + llvm::StringSwitch(A->getValue()) +#define CF_BRANCH_LABEL_SCHEME(Kind, FlagVal) \ + .Case(#FlagVal, CFBranchLabelSchemeKind::Kind) +#include "clang/Basic/CFProtectionOptions.def" + .Default(CFBranchLabelSchemeKind::Default); + if (Scheme != CFBranchLabelSchemeKind::Default) + Opts.setCFBranchLabelScheme(Scheme); + else + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + } + if (const Arg *A = Args.getLastArg(OPT_mfunction_return_EQ)) { auto Val = llvm::StringSwitch(A->getValue()) .Case("keep", llvm::FunctionReturnThunksKind::Keep) @@ -3952,6 +3980,18 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, } } + if (Opts.CFProtectionBranch) { + if (const Arg *A = Args.getLastArg(OPT_mcf_branch_label_scheme_EQ)) { + const auto Scheme = + llvm::StringSwitch(A->getValue()) +#define CF_BRANCH_LABEL_SCHEME(Kind, FlagVal) \ + .Case(#FlagVal, CFBranchLabelSchemeKind::Kind) +#include "clang/Basic/CFProtectionOptions.def" + .Default(CFBranchLabelSchemeKind::Default); + Opts.setCFBranchLabelScheme(Scheme); + } + } + if ((Args.hasArg(OPT_fsycl_is_device) || Args.hasArg(OPT_fsycl_is_host)) && !Args.hasArg(OPT_sycl_std_EQ)) { // If the user supplied -fsycl-is-device or -fsycl-is-host, but failed to diff --git a/clang/test/CodeGen/RISCV/riscv-cf-protection.c b/clang/test/CodeGen/RISCV/riscv-cf-protection.c new file mode 100644 index 00000000000000..3a9855a3d2f011 --- /dev/null +++ b/clang/test/CodeGen/RISCV/riscv-cf-protection.c @@ -0,0 +1,94 @@ +// RUN: %clang --target=riscv32 -menable-experimental-extensions \ +// RUN: -march=rv32i_zicfilp1p0 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=unlabeled -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,UNLABELED-FLAG %s + +// RUN: %clang --target=riscv32 -menable-experimental-extensions \ +// RUN: -march=rv32i_zicfilp1p0 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=func-sig -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,FUNC-SIG-FLAG %s + +// RUN: %clang --target=riscv32 -menable-experimental-extensions \ +// RUN: -march=rv32i_zicfilp1p0 -mcf-branch-label-scheme=unlabeled -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,UNLABELED-SCHEME-UNUSED %s + +// RUN: %clang --target=riscv32 -menable-experimental-extensions \ +// RUN: -march=rv32i_zicfilp1p0 -mcf-branch-label-scheme=func-sig -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,FUNC-SIG-SCHEME-UNUSED %s + +// RUN: %clang --target=riscv32 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=unlabeled -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,UNLABELED-FLAG %s + +// RUN: %clang --target=riscv32 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=func-sig -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,FUNC-SIG-FLAG %s + +// RUN: %clang --target=riscv32 -mcf-branch-label-scheme=unlabeled -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,UNLABELED-SCHEME-UNUSED %s + +// RUN: %clang --target=riscv32 -mcf-branch-label-scheme=func-sig -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,FUNC-SIG-SCHEME-UNUSED %s + +// RUN: %clang --target=riscv64 -menable-experimental-extensions \ +// RUN: -march=rv64i_zicfilp1p0 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=unlabeled -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,UNLABELED-FLAG %s + +// RUN: %clang --target=riscv64 -menable-experimental-extensions \ +// RUN: -march=rv64i_zicfilp1p0 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=func-sig -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,FUNC-SIG-FLAG %s + +// RUN: %clang --target=riscv64 -menable-experimental-extensions \ +// RUN: -march=rv64i_zicfilp1p0 -mcf-branch-label-scheme=unlabeled -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,UNLABELED-SCHEME-UNUSED %s + +// RUN: %clang --target=riscv64 -menable-experimental-extensions \ +// RUN: -march=rv64i_zicfilp1p0 -mcf-branch-label-scheme=func-sig -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,FUNC-SIG-SCHEME-UNUSED %s + +// RUN: %clang --target=riscv64 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=unlabeled -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,UNLABELED-FLAG %s + +// RUN: %clang --target=riscv64 -fcf-protection=branch \ +// RUN: -mcf-branch-label-scheme=func-sig -S -emit-llvm %s -o - | FileCheck \ +// RUN: --check-prefixes=BRANCH-PROT-FLAG,FUNC-SIG-FLAG %s + +// RUN: %clang --target=riscv64 -mcf-branch-label-scheme=unlabeled -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,UNLABELED-SCHEME-UNUSED %s + +// RUN: %clang --target=riscv64 -mcf-branch-label-scheme=func-sig -S \ +// RUN: -emit-llvm %s -o - 2>&1 | FileCheck \ +// RUN: --check-prefixes=NO-FLAG,FUNC-SIG-SCHEME-UNUSED %s + +// Default -mcf-branch-label-scheme is func-sig +// RUN: %clang --target=riscv32 -fcf-protection=branch -S -emit-llvm %s -o - \ +// RUN: | FileCheck --check-prefixes=BRANCH-PROT-FLAG,FUNC-SIG-FLAG %s + +// Default -mcf-branch-label-scheme is func-sig +// RUN: %clang --target=riscv64 -fcf-protection=branch -S -emit-llvm %s -o - \ +// RUN: | FileCheck --check-prefixes=BRANCH-PROT-FLAG,FUNC-SIG-FLAG %s + +// UNLABELED-SCHEME-UNUSED: warning: argument unused during compilation: +// UNLABELED-SCHEME-UNUSED-SAME: '-mcf-branch-label-scheme=unlabeled' +// FUNC-SIG-SCHEME-UNUSED: warning: argument unused during compilation: +// FUNC-SIG-SCHEME-UNUSED-SAME: '-mcf-branch-label-scheme=func-sig' + +// BRANCH-PROT-FLAG-DAG: [[P_FLAG:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1} +// UNLABELED-FLAG-DAG: [[S_FLAG:![0-9]+]] = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"} +// FUNC-SIG-FLAG-DAG: [[S_FLAG:![0-9]+]] = !{i32 1, !"cf-branch-label-scheme", !"func-sig"} +// BRANCH-PROT-FLAG-DAG: !llvm.module.flags = !{{[{].*}}[[P_FLAG]]{{.*, }}[[S_FLAG]]{{(,.+)?[}]}} +// NO-FLAG-NOT: !{i32 8, !"cf-protection-branch", i32 1} +// NO-FLAG-NOT: !{i32 8, !"cf-branch-label-scheme", !"unlabeled"} +// NO-FLAG-NOT: !{i32 8, !"cf-branch-label-scheme", !"func-sig"} + +int main() { return 0; }