[CodeGen] Port CFGuard to new pass manager (#75146)

Port `CFGuard` to new pass manager, add a pass parameter to choose guard
mechanism.
This commit is contained in:
paperchalice
2023-12-13 08:50:22 +08:00
committed by GitHub
parent 19fff85893
commit 27259f17e9
7 changed files with 88 additions and 38 deletions

View File

@@ -48,6 +48,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"

View File

@@ -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))
@@ -124,8 +125,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, ())

View File

@@ -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();

View File

@@ -16,6 +16,7 @@ add_llvm_component_library(LLVMPasses
LINK_COMPONENTS
AggressiveInstCombine
Analysis
CFGuard
CodeGen
Core
Coroutines

View File

@@ -100,6 +100,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"
@@ -738,6 +739,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");
}

View File

@@ -433,6 +433,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); },

View File

@@ -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;
using Mechanism = CFGuardPass::Mechanism;
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;
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
@@ -141,21 +138,37 @@ public:
/// \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");
@@ -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");
@@ -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 =
@@ -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,
@@ -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)
@@ -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);
}
@@ -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);
}