Skip to content

[CodeGen] Port CFGuard to new pass manager #75146

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

Merged
merged 1 commit into from
Dec 13, 2023
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
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/CodeGenPassBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/CGPassBuilderOption.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/CFGuard.h"
#include "llvm/Transforms/Scalar/ConstantHoisting.h"
#include "llvm/Transforms/Scalar/LoopPassManager.h"
#include "llvm/Transforms/Scalar/LoopStrengthReduce.h"
Expand Down
3 changes: 1 addition & 2 deletions llvm/include/llvm/CodeGen/MachinePassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ FUNCTION_ANALYSIS("targetir", TargetIRAnalysis,
#define FUNCTION_PASS(NAME, PASS_NAME, CONSTRUCTOR)
#endif
FUNCTION_PASS("callbrprepare", CallBrPreparePass, ())
FUNCTION_PASS("cfguard", CFGuardPass, ())
FUNCTION_PASS("consthoist", ConstantHoistingPass, ())
FUNCTION_PASS("dwarf-eh-prepare", DwarfEHPreparePass, (TM))
FUNCTION_PASS("ee-instrument", EntryExitInstrumenterPass, (false))
Expand Down Expand Up @@ -123,8 +124,6 @@ MACHINE_FUNCTION_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis,
#define DUMMY_FUNCTION_PASS(NAME, PASS_NAME, CONSTRUCTOR)
#endif
DUMMY_FUNCTION_PASS("atomic-expand", AtomicExpandPass, ())
DUMMY_FUNCTION_PASS("cfguard-check", CFGuardCheckPass, ())
DUMMY_FUNCTION_PASS("cfguard-dispatch", CFGuardDispatchPass, ())
DUMMY_FUNCTION_PASS("codegenprepare", CodeGenPreparePass, ())
DUMMY_FUNCTION_PASS("expandmemcmp", ExpandMemCmpPass, ())
DUMMY_FUNCTION_PASS("gc-lowering", GCLoweringPass, ())
Expand Down
13 changes: 13 additions & 0 deletions llvm/include/llvm/Transforms/CFGuard.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,23 @@
#ifndef LLVM_TRANSFORMS_CFGUARD_H
#define LLVM_TRANSFORMS_CFGUARD_H

#include "llvm/IR/PassManager.h"

namespace llvm {

class FunctionPass;

class CFGuardPass : public PassInfoMixin<CFGuardPass> {
public:
enum class Mechanism { Check, Dispatch };

CFGuardPass(Mechanism M = Mechanism::Check) : GuardMechanism(M) {}
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);

private:
Mechanism GuardMechanism;
};

/// Insert Control FLow Guard checks on indirect function calls.
FunctionPass *createCFGuardCheckPass();

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Passes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_llvm_component_library(LLVMPasses
LINK_COMPONENTS
AggressiveInstCombine
Analysis
CFGuard
CodeGen
Core
Coroutines
Expand Down
21 changes: 21 additions & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
#include "llvm/Support/Regex.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h"
#include "llvm/Transforms/CFGuard.h"
#include "llvm/Transforms/Coroutines/CoroCleanup.h"
#include "llvm/Transforms/Coroutines/CoroConditionalWrapper.h"
#include "llvm/Transforms/Coroutines/CoroEarly.h"
Expand Down Expand Up @@ -737,6 +738,26 @@ Expected<bool> parsePostOrderFunctionAttrsPassOptions(StringRef Params) {
"PostOrderFunctionAttrs");
}

Expected<CFGuardPass::Mechanism> parseCFGuardPassOptions(StringRef Params) {
if (Params.empty())
return CFGuardPass::Mechanism::Check;

auto [Param, RHS] = Params.split(';');
if (!RHS.empty())
return make_error<StringError>(
formatv("too many CFGuardPass parameters '{0}' ", Params).str(),
inconvertibleErrorCode());

if (Param == "check")
return CFGuardPass::Mechanism::Check;
if (Param == "dispatch")
return CFGuardPass::Mechanism::Dispatch;

return make_error<StringError>(
formatv("invalid CFGuardPass mechanism: '{0}' ", Param).str(),
inconvertibleErrorCode());
}

Expected<bool> parseEarlyCSEPassOptions(StringRef Params) {
return parseSinglePassOption(Params, "memssa", "EarlyCSE");
}
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,10 @@ FUNCTION_PASS("wasm-eh-prepare", WasmEHPreparePass())
#ifndef FUNCTION_PASS_WITH_PARAMS
#define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS)
#endif
FUNCTION_PASS_WITH_PARAMS(
"cfguard", "CFGuardPass",
[](CFGuardPass::Mechanism M) { return CFGuardPass(M); },
parseCFGuardPassOptions, "check;dispatch")
FUNCTION_PASS_WITH_PARAMS(
"early-cse", "EarlyCSEPass",
[](bool UseMemorySSA) { return EarlyCSEPass(UseMemorySSA); },
Expand Down
85 changes: 48 additions & 37 deletions llvm/lib/Transforms/CFGuard/CFGuard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,22 @@ namespace {

/// Adds Control Flow Guard (CFG) checks on indirect function calls/invokes.
/// These checks ensure that the target address corresponds to the start of an
/// address-taken function. X86_64 targets use the CF_Dispatch mechanism. X86,
/// ARM, and AArch64 targets use the CF_Check machanism.
class CFGuard : public FunctionPass {
/// address-taken function. X86_64 targets use the Mechanism::Dispatch
/// mechanism. X86, ARM, and AArch64 targets use the Mechanism::Check machanism.
class CFGuardImpl {
public:
static char ID;

enum Mechanism { CF_Check, CF_Dispatch };

// Default constructor required for the INITIALIZE_PASS macro.
CFGuard() : FunctionPass(ID) {
initializeCFGuardPass(*PassRegistry::getPassRegistry());
// By default, use the guard check mechanism.
GuardMechanism = CF_Check;
}

// Recommended constructor used to specify the type of guard mechanism.
CFGuard(Mechanism Var) : FunctionPass(ID) {
initializeCFGuardPass(*PassRegistry::getPassRegistry());
GuardMechanism = Var;
using Mechanism = CFGuardPass::Mechanism;

CFGuardImpl(Mechanism M) : GuardMechanism(M) {
// Get or insert the guard check or dispatch global symbols.
switch (GuardMechanism) {
case Mechanism::Check:
GuardFnName = "__guard_check_icall_fptr";
break;
case Mechanism::Dispatch:
GuardFnName = "__guard_dispatch_icall_fptr";
break;
}
}

/// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG
Expand Down Expand Up @@ -141,21 +138,37 @@ class CFGuard : public FunctionPass {
/// \param CB indirect call to instrument.
void insertCFGuardDispatch(CallBase *CB);

bool doInitialization(Module &M) override;
bool runOnFunction(Function &F) override;
bool doInitialization(Module &M);
bool runOnFunction(Function &F);

private:
// Only add checks if the module has the cfguard=2 flag.
int cfguard_module_flag = 0;
Mechanism GuardMechanism = CF_Check;
StringRef GuardFnName;
Mechanism GuardMechanism = Mechanism::Check;
FunctionType *GuardFnType = nullptr;
PointerType *GuardFnPtrType = nullptr;
Constant *GuardFnGlobal = nullptr;
};

class CFGuard : public FunctionPass {
CFGuardImpl Impl;

public:
static char ID;

// Default constructor required for the INITIALIZE_PASS macro.
CFGuard(CFGuardImpl::Mechanism M) : FunctionPass(ID), Impl(M) {
initializeCFGuardPass(*PassRegistry::getPassRegistry());
}

bool doInitialization(Module &M) override { return Impl.doInitialization(M); }
bool runOnFunction(Function &F) override { return Impl.runOnFunction(F); }
};

} // end anonymous namespace

void CFGuard::insertCFGuardCheck(CallBase *CB) {
void CFGuardImpl::insertCFGuardCheck(CallBase *CB) {

assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() &&
"Only applicable for Windows targets");
Expand Down Expand Up @@ -184,7 +197,7 @@ void CFGuard::insertCFGuardCheck(CallBase *CB) {
GuardCheck->setCallingConv(CallingConv::CFGuard_Check);
}

void CFGuard::insertCFGuardDispatch(CallBase *CB) {
void CFGuardImpl::insertCFGuardDispatch(CallBase *CB) {

assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() &&
"Only applicable for Windows targets");
Expand Down Expand Up @@ -218,7 +231,7 @@ void CFGuard::insertCFGuardDispatch(CallBase *CB) {
CB->eraseFromParent();
}

bool CFGuard::doInitialization(Module &M) {
bool CFGuardImpl::doInitialization(Module &M) {

// Check if this module has the cfguard flag and read its value.
if (auto *MD =
Expand All @@ -235,15 +248,6 @@ bool CFGuard::doInitialization(Module &M) {
{PointerType::getUnqual(M.getContext())}, false);
GuardFnPtrType = PointerType::get(GuardFnType, 0);

// Get or insert the guard check or dispatch global symbols.
llvm::StringRef GuardFnName;
if (GuardMechanism == CF_Check) {
GuardFnName = "__guard_check_icall_fptr";
} else if (GuardMechanism == CF_Dispatch) {
GuardFnName = "__guard_dispatch_icall_fptr";
} else {
assert(false && "Invalid CFGuard mechanism");
}
GuardFnGlobal = M.getOrInsertGlobal(GuardFnName, GuardFnPtrType, [&] {
auto *Var = new GlobalVariable(M, GuardFnPtrType, false,
GlobalVariable::ExternalLinkage, nullptr,
Expand All @@ -255,7 +259,7 @@ bool CFGuard::doInitialization(Module &M) {
return true;
}

bool CFGuard::runOnFunction(Function &F) {
bool CFGuardImpl::runOnFunction(Function &F) {

// Skip modules for which CFGuard checks have been disabled.
if (cfguard_module_flag != 2)
Expand Down Expand Up @@ -283,7 +287,7 @@ bool CFGuard::runOnFunction(Function &F) {
}

// For each indirect call/invoke, add the appropriate dispatch or check.
if (GuardMechanism == CF_Dispatch) {
if (GuardMechanism == Mechanism::Dispatch) {
for (CallBase *CB : IndirectCalls) {
insertCFGuardDispatch(CB);
}
Expand All @@ -296,13 +300,20 @@ bool CFGuard::runOnFunction(Function &F) {
return true;
}

PreservedAnalyses CFGuardPass::run(Function &F, FunctionAnalysisManager &FAM) {
CFGuardImpl Impl(GuardMechanism);
bool Changed = Impl.doInitialization(*F.getParent());
Changed |= Impl.runOnFunction(F);
return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
}

char CFGuard::ID = 0;
INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false)

FunctionPass *llvm::createCFGuardCheckPass() {
return new CFGuard(CFGuard::CF_Check);
return new CFGuard(CFGuardPass::Mechanism::Check);
}

FunctionPass *llvm::createCFGuardDispatchPass() {
return new CFGuard(CFGuard::CF_Dispatch);
return new CFGuard(CFGuardPass::Mechanism::Dispatch);
}