brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.1 KiB · 4645670 Raw
323 lines · cpp
1//===-- CFGuard.cpp - Control Flow Guard checks -----------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file contains the IR transform to add Microsoft's Control Flow Guard11/// checks on Windows targets.12///13//===----------------------------------------------------------------------===//14 15#include "llvm/Transforms/CFGuard.h"16#include "llvm/ADT/SmallVector.h"17#include "llvm/ADT/Statistic.h"18#include "llvm/IR/CallingConv.h"19#include "llvm/IR/IRBuilder.h"20#include "llvm/IR/Instruction.h"21#include "llvm/IR/Module.h"22#include "llvm/InitializePasses.h"23#include "llvm/Pass.h"24#include "llvm/TargetParser/Triple.h"25 26using namespace llvm;27 28using OperandBundleDef = OperandBundleDefT<Value *>;29 30#define DEBUG_TYPE "cfguard"31 32STATISTIC(CFGuardCounter, "Number of Control Flow Guard checks added");33 34constexpr StringRef GuardCheckFunctionName = "__guard_check_icall_fptr";35constexpr StringRef GuardDispatchFunctionName = "__guard_dispatch_icall_fptr";36 37namespace {38 39/// Adds Control Flow Guard (CFG) checks on indirect function calls/invokes.40/// These checks ensure that the target address corresponds to the start of an41/// address-taken function. X86_64 targets use the Mechanism::Dispatch42/// mechanism. X86, ARM, and AArch64 targets use the Mechanism::Check machanism.43class CFGuardImpl {44public:45  using Mechanism = CFGuardPass::Mechanism;46 47  CFGuardImpl(Mechanism M) : GuardMechanism(M) {48    // Get or insert the guard check or dispatch global symbols.49    switch (GuardMechanism) {50    case Mechanism::Check:51      GuardFnName = GuardCheckFunctionName;52      break;53    case Mechanism::Dispatch:54      GuardFnName = GuardDispatchFunctionName;55      break;56    }57  }58 59  /// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG60  /// check mechanism. When the image is loaded, the loader puts the appropriate61  /// guard check function pointer in the __guard_check_icall_fptr global62  /// symbol. This checks that the target address is a valid address-taken63  /// function. The address of the target function is passed to the guard check64  /// function in an architecture-specific register (e.g. ECX on 32-bit X86,65  /// X15 on Aarch64, and R0 on ARM). The guard check function has no return66  /// value (if the target is invalid, the guard check funtion will raise an67  /// error).68  ///69  /// For example, the following LLVM IR:70  /// \code71  ///   %func_ptr = alloca i32 ()*, align 872  ///   store i32 ()* @target_func, i32 ()** %func_ptr, align 873  ///   %0 = load i32 ()*, i32 ()** %func_ptr, align 874  ///   %1 = call i32 %0()75  /// \endcode76  ///77  /// is transformed to:78  /// \code79  ///   %func_ptr = alloca i32 ()*, align 880  ///   store i32 ()* @target_func, i32 ()** %func_ptr, align 881  ///   %0 = load i32 ()*, i32 ()** %func_ptr, align 882  ///   %1 = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr83  ///   %2 = bitcast i32 ()* %0 to i8*84  ///   call cfguard_checkcc void %1(i8* %2)85  ///   %3 = call i32 %0()86  /// \endcode87  ///88  /// For example, the following X86 assembly code:89  /// \code90  ///   movl  $_target_func, %eax91  ///   calll *%eax92  /// \endcode93  ///94  /// is transformed to:95  /// \code96  /// 	movl	$_target_func, %ecx97  /// 	calll	*___guard_check_icall_fptr98  /// 	calll	*%ecx99  /// \endcode100  ///101  /// \param CB indirect call to instrument.102  void insertCFGuardCheck(CallBase *CB);103 104  /// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG105  /// dispatch mechanism. When the image is loaded, the loader puts the106  /// appropriate guard check function pointer in the107  /// __guard_dispatch_icall_fptr global symbol. This checks that the target108  /// address is a valid address-taken function and, if so, tail calls the109  /// target. The target address is passed in an architecture-specific register110  /// (e.g. RAX on X86_64), with all other arguments for the target function111  /// passed as usual.112  ///113  /// For example, the following LLVM IR:114  /// \code115  ///   %func_ptr = alloca i32 ()*, align 8116  ///   store i32 ()* @target_func, i32 ()** %func_ptr, align 8117  ///   %0 = load i32 ()*, i32 ()** %func_ptr, align 8118  ///   %1 = call i32 %0()119  /// \endcode120  ///121  /// is transformed to:122  /// \code123  ///   %func_ptr = alloca i32 ()*, align 8124  ///   store i32 ()* @target_func, i32 ()** %func_ptr, align 8125  ///   %0 = load i32 ()*, i32 ()** %func_ptr, align 8126  ///   %1 = load i32 ()*, i32 ()** @__guard_dispatch_icall_fptr127  ///   %2 = call i32 %1() [ "cfguardtarget"(i32 ()* %0) ]128  /// \endcode129  ///130  /// For example, the following X86_64 assembly code:131  /// \code132  ///   leaq   target_func(%rip), %rax133  ///	  callq  *%rax134  /// \endcode135  ///136  /// is transformed to:137  /// \code138  ///   leaq   target_func(%rip), %rax139  ///   callq  *__guard_dispatch_icall_fptr(%rip)140  /// \endcode141  ///142  /// \param CB indirect call to instrument.143  void insertCFGuardDispatch(CallBase *CB);144 145  bool doInitialization(Module &M);146  bool runOnFunction(Function &F);147 148private:149  // Only add checks if the module has the cfguard=2 flag.150  int CFGuardModuleFlag = 0;151  StringRef GuardFnName;152  Mechanism GuardMechanism = Mechanism::Check;153  FunctionType *GuardFnType = nullptr;154  PointerType *GuardFnPtrType = nullptr;155  Constant *GuardFnGlobal = nullptr;156};157 158class CFGuard : public FunctionPass {159  CFGuardImpl Impl;160 161public:162  static char ID;163 164  // Default constructor required for the INITIALIZE_PASS macro.165  CFGuard(CFGuardImpl::Mechanism M) : FunctionPass(ID), Impl(M) {}166 167  bool doInitialization(Module &M) override { return Impl.doInitialization(M); }168  bool runOnFunction(Function &F) override { return Impl.runOnFunction(F); }169};170 171} // end anonymous namespace172 173void CFGuardImpl::insertCFGuardCheck(CallBase *CB) {174  assert(CB->getModule()->getTargetTriple().isOSWindows() &&175         "Only applicable for Windows targets");176  assert(CB->isIndirectCall() &&177         "Control Flow Guard checks can only be added to indirect calls");178 179  IRBuilder<> B(CB);180  Value *CalledOperand = CB->getCalledOperand();181 182  // If the indirect call is called within catchpad or cleanuppad,183  // we need to copy "funclet" bundle of the call.184  SmallVector<llvm::OperandBundleDef, 1> Bundles;185  if (auto Bundle = CB->getOperandBundle(LLVMContext::OB_funclet))186    Bundles.push_back(OperandBundleDef(*Bundle));187 188  // Load the global symbol as a pointer to the check function.189  LoadInst *GuardCheckLoad = B.CreateLoad(GuardFnPtrType, GuardFnGlobal);190 191  // Create new call instruction. The CFGuard check should always be a call,192  // even if the original CallBase is an Invoke or CallBr instruction.193  CallInst *GuardCheck =194      B.CreateCall(GuardFnType, GuardCheckLoad, {CalledOperand}, Bundles);195 196  // Ensure that the first argument is passed in the correct register197  // (e.g. ECX on 32-bit X86 targets).198  GuardCheck->setCallingConv(CallingConv::CFGuard_Check);199}200 201void CFGuardImpl::insertCFGuardDispatch(CallBase *CB) {202  assert(CB->getModule()->getTargetTriple().isOSWindows() &&203         "Only applicable for Windows targets");204  assert(CB->isIndirectCall() &&205         "Control Flow Guard checks can only be added to indirect calls");206 207  IRBuilder<> B(CB);208  Value *CalledOperand = CB->getCalledOperand();209  Type *CalledOperandType = CalledOperand->getType();210 211  // Load the global as a pointer to a function of the same type.212  LoadInst *GuardDispatchLoad = B.CreateLoad(CalledOperandType, GuardFnGlobal);213 214  // Add the original call target as a cfguardtarget operand bundle.215  SmallVector<llvm::OperandBundleDef, 1> Bundles;216  CB->getOperandBundlesAsDefs(Bundles);217  Bundles.emplace_back("cfguardtarget", CalledOperand);218 219  // Create a copy of the call/invoke instruction and add the new bundle.220  assert((isa<CallInst>(CB) || isa<InvokeInst>(CB)) &&221         "Unknown indirect call type");222  CallBase *NewCB = CallBase::Create(CB, Bundles, CB->getIterator());223 224  // Change the target of the call to be the guard dispatch function.225  NewCB->setCalledOperand(GuardDispatchLoad);226 227  // Replace the original call/invoke with the new instruction.228  CB->replaceAllUsesWith(NewCB);229 230  // Delete the original call/invoke.231  CB->eraseFromParent();232}233 234bool CFGuardImpl::doInitialization(Module &M) {235  // Check if this module has the cfguard flag and read its value.236  if (auto *MD =237          mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("cfguard")))238    CFGuardModuleFlag = MD->getZExtValue();239 240  // Skip modules for which CFGuard checks have been disabled.241  if (CFGuardModuleFlag != 2)242    return false;243 244  // Set up prototypes for the guard check and dispatch functions.245  GuardFnType =246      FunctionType::get(Type::getVoidTy(M.getContext()),247                        {PointerType::getUnqual(M.getContext())}, false);248  GuardFnPtrType = PointerType::get(M.getContext(), 0);249 250  GuardFnGlobal = M.getOrInsertGlobal(GuardFnName, GuardFnPtrType, [&] {251    auto *Var = new GlobalVariable(M, GuardFnPtrType, false,252                                   GlobalVariable::ExternalLinkage, nullptr,253                                   GuardFnName);254    Var->setDSOLocal(true);255    return Var;256  });257 258  return true;259}260 261bool CFGuardImpl::runOnFunction(Function &F) {262  // Skip modules for which CFGuard checks have been disabled.263  if (CFGuardModuleFlag != 2)264    return false;265 266  SmallVector<CallBase *, 8> IndirectCalls;267 268  // Iterate over the instructions to find all indirect call/invoke/callbr269  // instructions. Make a separate list of pointers to indirect270  // call/invoke/callbr instructions because the original instructions will be271  // deleted as the checks are added.272  for (BasicBlock &BB : F) {273    for (Instruction &I : BB) {274      auto *CB = dyn_cast<CallBase>(&I);275      if (CB && CB->isIndirectCall() && !CB->hasFnAttr("guard_nocf")) {276        IndirectCalls.push_back(CB);277        CFGuardCounter++;278      }279    }280  }281 282  // If no checks are needed, return early.283  if (IndirectCalls.empty())284    return false;285 286  // For each indirect call/invoke, add the appropriate dispatch or check.287  if (GuardMechanism == Mechanism::Dispatch) {288    for (CallBase *CB : IndirectCalls)289      insertCFGuardDispatch(CB);290  } else {291    for (CallBase *CB : IndirectCalls)292      insertCFGuardCheck(CB);293  }294 295  return true;296}297 298PreservedAnalyses CFGuardPass::run(Function &F, FunctionAnalysisManager &FAM) {299  CFGuardImpl Impl(GuardMechanism);300  bool Changed = Impl.doInitialization(*F.getParent());301  Changed |= Impl.runOnFunction(F);302  return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();303}304 305char CFGuard::ID = 0;306INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false)307 308FunctionPass *llvm::createCFGuardCheckPass() {309  return new CFGuard(CFGuardPass::Mechanism::Check);310}311 312FunctionPass *llvm::createCFGuardDispatchPass() {313  return new CFGuard(CFGuardPass::Mechanism::Dispatch);314}315 316bool llvm::isCFGuardFunction(const GlobalValue *GV) {317  if (GV->getLinkage() != GlobalValue::ExternalLinkage)318    return false;319 320  StringRef Name = GV->getName();321  return Name == GuardCheckFunctionName || Name == GuardDispatchFunctionName;322}323