Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][RISCV] Introduce command line options for RISC-V Zicfilp CFI #109784

Merged
merged 1 commit into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions clang/include/clang/Basic/CFProtectionOptions.def
Original file line number Diff line number Diff line change
@@ -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
38 changes: 38 additions & 0 deletions clang/include/clang/Basic/CFProtectionOptions.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 };
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2841,6 +2841,10 @@ def fcf_protection : Flag<["-"], "fcf-protection">, Group<f_Group>,
Visibility<[ClangOption, CLOption, CC1Option]>,
Alias<fcf_protection_EQ>, AliasArgs<["full"]>,
HelpText<"Enable cf-protection in 'full' mode">;
def mcf_branch_label_scheme_EQ : Joined<["-"], "mcf-branch-label-scheme=">,
Visibility<[ClangOption, CC1Option]>, Group<m_Group>,
HelpText<"Select label scheme for branch control-flow architecture protection">,
Values<"unlabeled,func-sig">;
def mfunction_return_EQ : Joined<["-"], "mfunction-return=">,
Group<m_Group>, Visibility<[ClangOption, CLOption, CC1Option]>,
HelpText<"Replace returns with jumps to ``__x86_return_thunk`` (x86 only, error otherwise)">,
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Basic/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/Basic/Targets/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
40 changes: 40 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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<CFBranchLabelSchemeKind>(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<llvm::FunctionReturnThunksKind>(A->getValue())
.Case("keep", llvm::FunctionReturnThunksKind::Keep)
Expand Down Expand Up @@ -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<CFBranchLabelSchemeKind>(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
Expand Down
94 changes: 94 additions & 0 deletions clang/test/CodeGen/RISCV/riscv-cf-protection.c
Original file line number Diff line number Diff line change
@@ -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; }
Loading