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

Add -fcf-protection. #4437

Merged
merged 7 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417)
- Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing).
- Less pedantic checks for conflicting C(++) function declarations when compiling multiple modules to a single object file ('Error: Function type does not match previously declared function with the same mangled name'). The error now only appears if an object file actually references multiple conflicting functions. (#4420)
- New command-line option `--fcf-protection`, which enables Intel's Control-Flow Enforcement Technology (CET). (#4437)

#### Platform support

Expand Down
18 changes: 18 additions & 0 deletions driver/cl_options_instrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ llvm::StringRef getXRayInstructionThresholdString() {
return thresholdString;
}

cl::opt<CFProtectionType> fCFProtection(
"fcf-protection",
cl::desc("Instrument control-flow architecture protection"), cl::ZeroOrMore,
cl::ValueOptional,
cl::values(clEnumValN(CFProtectionType::None, "none", ""),
clEnumValN(CFProtectionType::Branch, "branch", ""),
clEnumValN(CFProtectionType::Return, "return", ""),
clEnumValN(CFProtectionType::Full, "full", ""),
clEnumValN(CFProtectionType::Full, "",
"") // default to "full" if no argument specified
),
cl::init(CFProtectionType::None));

void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) {
if (ASTPGOInstrGenFile.getNumOccurrences() > 0) {
pgoMode = PGO_ASTBasedInstr;
Expand All @@ -110,6 +123,11 @@ void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) {

if (dmdFunctionTrace)
global.params.trace = true;

if (fCFProtection != CFProtectionType::None && !triple.isX86()) {
error(Loc(), "option '--fcf-protection' cannot be specified on this target "
"architecture");
}
}

} // namespace opts
3 changes: 3 additions & 0 deletions driver/cl_options_instrumentation.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ extern cl::opt<bool> instrumentFunctions;
extern cl::opt<bool> fXRayInstrument;
llvm::StringRef getXRayInstructionThresholdString();

enum class CFProtectionType { None = 0, Branch = 1, Return = 2, Full = 3 };
extern cl::opt<CFProtectionType> fCFProtection;

/// This initializes the instrumentation options, and checks the validity of the
/// commandline flags. targetTriple should be initialized before calling this.
/// It should be called only once.
Expand Down
21 changes: 21 additions & 0 deletions gen/modules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,27 @@ void registerModuleInfo(Module *m) {
emitModuleRefToSection(mangle, moduleInfoSym);
}
}

void addModuleFlags(llvm::Module &m) {
#if LDC_LLVM_VER >= 1500
const auto ModuleMinFlag = llvm::Module::Min;
#else
const auto ModuleMinFlag = llvm::Module::Warning; // Fallback value
#endif

if (opts::fCFProtection == opts::CFProtectionType::Return ||
opts::fCFProtection == opts::CFProtectionType::Full) {
m.addModuleFlag(ModuleMinFlag, "cf-protection-return", 1);
}

if (opts::fCFProtection == opts::CFProtectionType::Branch ||
opts::fCFProtection == opts::CFProtectionType::Full) {
m.addModuleFlag(ModuleMinFlag, "cf-protection-branch", 1);
}
}

} // anonymous namespace

void codegenModule(IRState *irs, Module *m) {
TimeTraceScope timeScope("Generate IR", m->toChars(), m->loc);

Expand Down Expand Up @@ -444,6 +463,8 @@ void codegenModule(IRState *irs, Module *m) {
addCoverageAnalysisInitializer(m);
}

addModuleFlags(irs->module);

gIR = nullptr;
irs->dmodule = nullptr;
}
6 changes: 6 additions & 0 deletions gen/target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "dmd/mtype.h"
#include "dmd/target.h"
#include "driver/cl_options.h"
#include "driver/cl_options_instrumentation.h"
#include "driver/linker.h"
#include "gen/abi/abi.h"
#include "gen/irstate.h"
Expand Down Expand Up @@ -309,6 +310,11 @@ Expression *Target::getTargetInfo(const char *name_, const Loc &loc) {
Loc(), static_cast<unsigned>(global.params.cplusplus), Type::tint32);
}

if (name == "CET") {
auto cet = opts::fCFProtection.getValue();
return IntegerExp::create(loc, static_cast<unsigned>(cet), Type::tint32);
}

#if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX
if (name == "dcomputeTargets") {
Expressions* exps = createExpressions();
Expand Down
31 changes: 31 additions & 0 deletions tests/codegen/fcf_protection.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Test -fcf-protection

// REQUIRES: target_X86

// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t.ll %s -d-version=NOTHING && FileCheck %s --check-prefix=NOTHING < %t.ll

// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_branch.ll %s --fcf-protection=branch -d-version=BRANCH && FileCheck %s --check-prefix=BRANCH < %t_branch.ll
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_return.ll %s --fcf-protection=return -d-version=RETURN && FileCheck %s --check-prefix=RETURN < %t_return.ll
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_full.ll %s --fcf-protection=full -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_full.ll
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_noarg.ll %s --fcf-protection -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_noarg.ll

// NOTHING-NOT: cf-prot
// BRANCH-DAG: "cf-protection-branch", i32 1
// RETURN-DAG: "cf-protection-return", i32 1
// FULL-DAG: "cf-protection-branch", i32 1
// FULL-DAG: "cf-protection-return", i32 1

void foo() {}

version(NOTHING) {
static assert(__traits(getTargetInfo, "CET") == 0);
}
version(BRANCH) {
static assert(__traits(getTargetInfo, "CET") == 1);
}
version(RETURN) {
static assert(__traits(getTargetInfo, "CET") == 2);
}
version(FULL) {
static assert(__traits(getTargetInfo, "CET") == 3);
}
Loading