diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -96,7 +96,8 @@ void initializeCFGOnlyViewerLegacyPassPass(PassRegistry&); void initializeCFGPrinterLegacyPassPass(PassRegistry&); void initializeCFGSimplifyPassPass(PassRegistry&); -void initializeCFGuardPass(PassRegistry&); +void initializeCFGuardCheckPass(PassRegistry &); +void initializeCFGuardDispatchPass(PassRegistry &); void initializeCFGuardLongjmpPass(PassRegistry&); void initializeCFGViewerLegacyPassPass(PassRegistry&); void initializeCFIInstrInserterPass(PassRegistry&); diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp --- a/llvm/lib/Transforms/CFGuard/CFGuard.cpp +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -32,27 +32,33 @@ 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 { +/// Base class for adding 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. +template class CFGuardBase : public FunctionPass { public: - static char ID; + CFGuardBase() : FunctionPass(T::ID) { static_cast(this)->initialize(); } - enum Mechanism { CF_Check, CF_Dispatch }; + bool doInitialization(Module &M) override; + bool runOnFunction(Function &F) override; - // Default constructor required for the INITIALIZE_PASS macro. - CFGuard() : FunctionPass(ID) { - initializeCFGuardPass(*PassRegistry::getPassRegistry()); - // By default, use the guard check mechanism. - GuardMechanism = CF_Check; - } +protected: + // Only add checks if the module has the cfguard=2 flag. + int cfguard_module_flag = 0; + FunctionType *GuardFnType = nullptr; + PointerType *GuardFnPtrType = nullptr; + Constant *GuardFnGlobal = nullptr; +}; - // Recommended constructor used to specify the type of guard mechanism. - CFGuard(Mechanism Var) : FunctionPass(ID) { - initializeCFGuardPass(*PassRegistry::getPassRegistry()); - GuardMechanism = Var; +/// Adds Control Flow Guard (CFG) checks on indirect function calls/invokes +/// using the check mchanism. This is used on X86, ARM, and AArch64 targets. +class CFGuardCheck : public CFGuardBase { +public: + static char ID; + const StringRef GuardFnGlobalName = "__guard_check_icall_fptr"; + + void initialize() { + initializeCFGuardCheckPass(*PassRegistry::getPassRegistry()); } /// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG @@ -98,7 +104,19 @@ /// \endcode /// /// \param CB indirect call to instrument. - void insertCFGuardCheck(CallBase *CB); + void insertCFGuard(CallBase *CB); +}; + +/// Adds Control Flow Guard (CFG) checks on indirect function calls/invokes +/// using the dispatch mchanism. This is used on X86_64 targets. +class CFGuardDispatch : public CFGuardBase { +public: + static char ID; + const StringRef GuardFnGlobalName = "__guard_dispatch_icall_fptr"; + + void initialize() { + initializeCFGuardDispatchPass(*PassRegistry::getPassRegistry()); + } /// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG /// dispatch mechanism. When the image is loaded, the loader puts the @@ -139,26 +157,13 @@ /// \endcode /// /// \param CB indirect call to instrument. - void insertCFGuardDispatch(CallBase *CB); - - bool doInitialization(Module &M) override; - bool runOnFunction(Function &F) override; - -private: - // Only add checks if the module has the cfguard=2 flag. - int cfguard_module_flag = 0; - Mechanism GuardMechanism = CF_Check; - FunctionType *GuardFnType = nullptr; - PointerType *GuardFnPtrType = nullptr; - Constant *GuardFnGlobal = nullptr; + void insertCFGuard(CallBase *CB); }; } // end anonymous namespace -void CFGuard::insertCFGuardCheck(CallBase *CB) { +void CFGuardCheck::insertCFGuard(CallBase *CB) { - assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() && - "Only applicable for Windows targets"); assert(CB->isIndirectCall() && "Control Flow Guard checks can only be added to indirect calls"); @@ -179,10 +184,8 @@ GuardCheck->setCallingConv(CallingConv::CFGuard_Check); } -void CFGuard::insertCFGuardDispatch(CallBase *CB) { +void CFGuardDispatch::insertCFGuard(CallBase *CB) { - assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() && - "Only applicable for Windows targets"); assert(CB->isIndirectCall() && "Control Flow Guard checks can only be added to indirect calls"); @@ -223,7 +226,7 @@ CB->eraseFromParent(); } -bool CFGuard::doInitialization(Module &M) { +template bool CFGuardBase::doInitialization(Module &M) { // Check if this module has the cfguard flag and read its value. if (auto *MD = @@ -240,19 +243,13 @@ GuardFnPtrType = PointerType::get(GuardFnType, 0); // Get or insert the guard check or dispatch global symbols. - if (GuardMechanism == CF_Check) { - GuardFnGlobal = - M.getOrInsertGlobal("__guard_check_icall_fptr", GuardFnPtrType); - } else { - assert(GuardMechanism == CF_Dispatch && "Invalid CFGuard mechanism"); - GuardFnGlobal = - M.getOrInsertGlobal("__guard_dispatch_icall_fptr", GuardFnPtrType); - } + GuardFnGlobal = M.getOrInsertGlobal(static_cast(this)->GuardFnGlobalName, + GuardFnPtrType); return true; } -bool CFGuard::runOnFunction(Function &F) { +template bool CFGuardBase::runOnFunction(Function &F) { // Skip modules for which CFGuard checks have been disabled. if (cfguard_module_flag != 2) @@ -280,26 +277,27 @@ } // For each indirect call/invoke, add the appropriate dispatch or check. - if (GuardMechanism == CF_Dispatch) { - for (CallBase *CB : IndirectCalls) { - insertCFGuardDispatch(CB); - } - } else { - for (CallBase *CB : IndirectCalls) { - insertCFGuardCheck(CB); - } + for (CallBase *CB : IndirectCalls) { + static_cast(this)->insertCFGuard(CB); } return true; } -char CFGuard::ID = 0; -INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false) +char CFGuardCheck::ID = 0; -FunctionPass *llvm::createCFGuardCheckPass() { - return new CFGuard(CFGuard::CF_Check); -} +INITIALIZE_PASS(CFGuardCheck, "cfguard-check", + "Control Flow Guard: inserts checks before indirect branches", + false, false) + +FunctionPass *llvm::createCFGuardCheckPass() { return new CFGuardCheck(); } + +char CFGuardDispatch::ID = 0; + +INITIALIZE_PASS(CFGuardDispatch, "cfguard-dispatch", + "Control Flow Guard: inserts dispatches on indirect branches", + false, false) FunctionPass *llvm::createCFGuardDispatchPass() { - return new CFGuard(CFGuard::CF_Dispatch); + return new CFGuardDispatch(); } diff --git a/llvm/test/Transforms/CFGuard/cfguard-check-dispatch.ll b/llvm/test/Transforms/CFGuard/cfguard-check-dispatch.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/CFGuard/cfguard-check-dispatch.ll @@ -0,0 +1,184 @@ +; RUN: opt < %s -cfguard-check -S | FileCheck %s -check-prefix=CHK +; RUN: opt < %s -cfguard-dispatch -S | FileCheck %s -check-prefix=DIS + +; Test the IR transforms for adding Control Flow Guard checks. + + +declare i32 @target_func() + +; Test that Control Flow Guard checks are not added on calls with the "guard_nocf" attribute. +define i32 @func_guard_nocf() { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() #0 + ret i32 %1 + + ; CHK-LABEL: func_guard_nocf + ; CHK-NOT: __guard_check_icall_fptr + + ; DIS-LABEL: func_guard_nocf + ; DIS-NOT: __guard_dispatch_icall_fptr +} +attributes #0 = { "guard_nocf" } + +; Test that Control Flow Guard checks are added even with optnone. +define i32 @func_optnone_cf() #1 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; CHK-LABEL: func_optnone_cf + ; CHK: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; CHK: [[GUARD:%.*]] = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr, align 8 + ; CHK: [[PTR:%.*]] = bitcast i32 ()* [[TARGET]] to i8* + ; CHK: call cfguard_checkcc void [[GUARD]](i8* [[PTR]]) + ; CHK: call i32 [[TARGET]]() + + ; DIS-LABEL: func_optnone_cf + ; DIS: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; DIS: [[GUARD:%.*]] = load i32 ()*, i32 ()** bitcast (void (i8*)** @__guard_dispatch_icall_fptr to i32 ()**), align 8 + ; DIS: call i32 [[GUARD]]() [ "cfguardtarget"(i32 ()* [[TARGET]]) ] +} +attributes #1 = { noinline optnone } + +; Test that Control Flow Guard checks are correctly added in optimized code (common case). +define i32 @func_cf() { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; CHK-LABEL: func_cf + ; CHK: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; CHK: [[GUARD:%.*]] = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr, align 8 + ; CHK: [[PTR:%.*]] = bitcast i32 ()* [[TARGET]] to i8* + ; CHK: call cfguard_checkcc void [[GUARD]](i8* [[PTR]]) + ; CHK: call i32 [[TARGET]]() + + ; DIS-LABEL: func_cf + ; DIS: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; DIS: [[GUARD:%.*]] = load i32 ()*, i32 ()** bitcast (void (i8*)** @__guard_dispatch_icall_fptr to i32 ()**), align 8 + ; DIS: call i32 [[GUARD]]() [ "cfguardtarget"(i32 ()* [[TARGET]]) ] +} + +; Test that Control Flow Guard checks are correctly added on invoke instructions. +define i32 @func_cf_invoke() personality i8* bitcast (void ()* @h to i8*) { +entry: + %0 = alloca i32, align 4 + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %1 = load i32 ()*, i32 ()** %func_ptr, align 8 + %2 = invoke i32 %1() + to label %invoke.cont unwind label %lpad +invoke.cont: ; preds = %entry + ret i32 %2 + +lpad: ; preds = %entry + %tmp = landingpad { i8*, i32 } + catch i8* null + ret i32 -1 + + ; CHK-LABEL: func_cf_invoke + ; CHK: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; CHK: [[GUARD:%.*]] = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr, align 8 + ; CHK: [[PTR:%.*]] = bitcast i32 ()* [[TARGET]] to i8* + ; CHK: call cfguard_checkcc void [[GUARD]](i8* [[PTR]]) + ; CHK: invoke i32 [[TARGET]]() + + ; DIS-LABEL: func_cf_invoke + ; DIS: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; DIS: [[GUARD:%.*]] = load i32 ()*, i32 ()** bitcast (void (i8*)** @__guard_dispatch_icall_fptr to i32 ()**), align 8 + ; DIS: invoke i32 [[GUARD]]() [ "cfguardtarget"(i32 ()* [[TARGET]]) ] +} + +declare void @h() + +; Test that Control Flow Guard preserves floating point arguments. +declare double @target_func_doubles(double, double, double, double) + +define double @func_cf_doubles() { +entry: + %func_ptr = alloca double (double, double, double, double)*, align 8 + store double (double, double, double, double)* @target_func_doubles, double (double, double, double, double)** %func_ptr, align 8 + %0 = load double (double, double, double, double)*, double (double, double, double, double)** %func_ptr, align 8 + %1 = call double %0(double 1.000000e+00, double 2.000000e+00, double 3.000000e+00, double 4.000000e+00) + ret double %1 + + ; CHK-LABEL: func_cf_doubles + ; CHK: [[TARGET:%.*]] = load double (double, double, double, double)*, double (double, double, double, double)** %func_ptr, align 8 + ; CHK: [[GUARD:%.*]] = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr, align 8 + ; CHK: [[PTR:%.*]] = bitcast double (double, double, double, double)* [[TARGET]] to i8* + ; CHK: call cfguard_checkcc void [[GUARD]](i8* [[PTR]]) + ; CHK: call double [[TARGET]](double 1.000000e+00, double 2.000000e+00, double 3.000000e+00, double 4.000000e+00) + + ; DIS-LABEL: func_cf_doubles + ; DIS: [[TARGET:%.*]] = load double (double, double, double, double)*, double (double, double, double, double)** %func_ptr, align 8 + ; DIS: [[GUARD:%.*]] = load double (double, double, double, double)*, double (double, double, double, double)** bitcast (void (i8*)** @__guard_dispatch_icall_fptr to double (double, double, double, double)**), align 8 + ; DIS: call double [[GUARD]](double 1.000000e+00, double 2.000000e+00, double 3.000000e+00, double 4.000000e+00) [ "cfguardtarget"(double (double, double, double, double)* [[TARGET]]) ] +} + +; Test that Control Flow Guard checks are correctly added for tail calls. +define i32 @func_cf_tail() { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = musttail call i32 %0() + ret i32 %1 + + ; CHK-LABEL: func_cf_tail + ; CHK: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; CHK: [[GUARD:%.*]] = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr, align 8 + ; CHK: [[PTR:%.*]] = bitcast i32 ()* [[TARGET]] to i8* + ; CHK: call cfguard_checkcc void [[GUARD]](i8* [[PTR]]) + ; CHK: musttail call i32 [[TARGET]]() + + ; DIS-LABEL: func_cf_tail + ; DIS: [[TARGET:%.*]] = load i32 ()*, i32 ()** %func_ptr, align 8 + ; DIS: [[GUARD:%.*]] = load i32 ()*, i32 ()** bitcast (void (i8*)** @__guard_dispatch_icall_fptr to i32 ()**), align 8 + ; DIS: musttail call i32 [[GUARD]]() [ "cfguardtarget"(i32 ()* [[TARGET]]) ] +} + +%struct.Foo = type { i32 (%struct.Foo*)** } + +; Test that Control Flow Guard checks are correctly added for variadic musttail +; calls. These are used for MS C++ ABI virtual member pointer thunks. +; PR44049 +define i32 @vmptr_thunk(%struct.Foo* inreg %p) { +entry: + %vptr.addr = getelementptr inbounds %struct.Foo, %struct.Foo* %p, i32 0, i32 0 + %vptr = load i32 (%struct.Foo*)**, i32 (%struct.Foo*)*** %vptr.addr + %slot = getelementptr inbounds i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %vptr, i32 1 + %vmethod = load i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %slot + %rv = musttail call i32 %vmethod(%struct.Foo* inreg %p) + ret i32 %rv + + ; CHK-LABEL: vmptr_thunk + ; CHK: %vptr.addr = getelementptr inbounds %struct.Foo, %struct.Foo* %p, i32 0, i32 0 + ; CHK: %vptr = load i32 (%struct.Foo*)**, i32 (%struct.Foo*)*** %vptr.addr, align 8 + ; CHK: %slot = getelementptr inbounds i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %vptr, i32 1 + ; CHK: %vmethod = load i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %slot, align 8 + ; CHK: [[GUARD:%.*]] = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr, align 8 + ; CHK: [[PTR:%.*]] = bitcast i32 (%struct.Foo*)* %vmethod to i8* + ; CHK: call cfguard_checkcc void [[GUARD]](i8* [[PTR]]) + ; CHK: %rv = musttail call i32 %vmethod(%struct.Foo* inreg %p) + + ; DIS-LABEL: vmptr_thunk + ; DIS: %vptr.addr = getelementptr inbounds %struct.Foo, %struct.Foo* %p, i32 0, i32 0 + ; DIS: %vptr = load i32 (%struct.Foo*)**, i32 (%struct.Foo*)*** %vptr.addr, align 8 + ; DIS: %slot = getelementptr inbounds i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %vptr, i32 1 + ; DIS: %vmethod = load i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %slot, align 8 + ; DIS: [[GUARD:%.*]] = load i32 (%struct.Foo*)*, i32 (%struct.Foo*)** bitcast (void (i8*)** @__guard_dispatch_icall_fptr to i32 (%struct.Foo*)**), align 8 + ; DIS: %rv1 = musttail call i32 [[GUARD]](%struct.Foo* inreg %p) [ "cfguardtarget"(i32 (%struct.Foo*)* %vmethod) ] +} + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} diff --git a/llvm/tools/opt/CMakeLists.txt b/llvm/tools/opt/CMakeLists.txt --- a/llvm/tools/opt/CMakeLists.txt +++ b/llvm/tools/opt/CMakeLists.txt @@ -7,6 +7,7 @@ Analysis AsmParser BitWriter + CFGuard CodeGen Core Coroutines diff --git a/llvm/tools/opt/LLVMBuild.txt b/llvm/tools/opt/LLVMBuild.txt --- a/llvm/tools/opt/LLVMBuild.txt +++ b/llvm/tools/opt/LLVMBuild.txt @@ -22,6 +22,7 @@ AsmParser BitReader BitWriter + CFGuard CodeGen IRReader IPO diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -554,6 +554,8 @@ // Initialize passes PassRegistry &Registry = *PassRegistry::getPassRegistry(); initializeCore(Registry); + initializeCFGuardCheckPass(Registry); + initializeCFGuardDispatchPass(Registry); initializeCoroutines(Registry); initializeScalarOpts(Registry); initializeObjCARCOpts(Registry);