When emitting the assembly .set directive, KCFI needs to use getZExtValue(). However, this means that FileCheck pattern matching can't match between the .set directive and the IR when the high bit of a 32-bit value is set. We had gotten lucky with the existing tests happening to just not have had the high bit set. The coming hash change will expose this, though. LLVM IR's default printing behavior uses APInt::operator<<, which calls APInt::print(OS, /*isSigned=*/true). This means KCFI operand bundles in call instructions print as signed (e.g. [ "kcfi"(i32 -1208803271) ]), and KCFI type metadata prints as signed (e.g. !3 = !{i32 -1208803271}). Changing the IR to print unsigned i32 values would impact hundreds of existing tests, so it is best to just leave it be. Update the KCFI .set direct to use getSExtValue() in a comment so that we can both build correctly and use FileCheck with pattern matching in tests. KCFI generates hashes in two places. Instead of exposing the hash implementation in both places, introduce a helper that wraps the specific hash implementation in a single place, llvm::getKCFITypeID. In order to transition between KCFI hash, we need to be able to specify them. Add the Clang option -fsanitize-kcfi-hash= and a IR module option "kcfi-hash" that can choose between xxHash64 and FNV-1a. Default to xxHash64 to stay backward compatible, as we'll need to also update rustc to take a new option to change the hash to FNV-1a for interop with the coming GCC KCFI.
119 lines
4.3 KiB
C++
119 lines
4.3 KiB
C++
//===-- KCFI.cpp - Generic KCFI operand bundle lowering ---------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This pass emits generic KCFI indirect call checks for targets that don't
|
|
// support lowering KCFI operand bundles in the back-end.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Transforms/Instrumentation/KCFI.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
|
#include "llvm/IR/DiagnosticPrinter.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/InstIterator.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/MDBuilder.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/xxhash.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "kcfi"
|
|
|
|
STATISTIC(NumKCFIChecks, "Number of kcfi operands transformed into checks");
|
|
|
|
namespace {
|
|
class DiagnosticInfoKCFI : public DiagnosticInfo {
|
|
const Twine &Msg;
|
|
|
|
public:
|
|
DiagnosticInfoKCFI(const Twine &DiagMsg LLVM_LIFETIME_BOUND,
|
|
DiagnosticSeverity Severity = DS_Error)
|
|
: DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {}
|
|
void print(DiagnosticPrinter &DP) const override { DP << Msg; }
|
|
};
|
|
} // namespace
|
|
|
|
PreservedAnalyses KCFIPass::run(Function &F, FunctionAnalysisManager &AM) {
|
|
Module &M = *F.getParent();
|
|
if (!M.getModuleFlag("kcfi"))
|
|
return PreservedAnalyses::all();
|
|
|
|
// Find call instructions with KCFI operand bundles.
|
|
SmallVector<CallInst *> KCFICalls;
|
|
for (Instruction &I : instructions(F)) {
|
|
if (auto *CI = dyn_cast<CallInst>(&I))
|
|
if (CI->getOperandBundle(LLVMContext::OB_kcfi))
|
|
KCFICalls.push_back(CI);
|
|
}
|
|
|
|
if (KCFICalls.empty())
|
|
return PreservedAnalyses::all();
|
|
|
|
LLVMContext &Ctx = M.getContext();
|
|
// patchable-function-prefix emits nops between the KCFI type identifier
|
|
// and the function start. As we don't know the size of the emitted nops,
|
|
// don't allow this attribute with generic lowering.
|
|
if (F.hasFnAttribute("patchable-function-prefix"))
|
|
Ctx.diagnose(
|
|
DiagnosticInfoKCFI("-fpatchable-function-entry=N,M, where M>0 is not "
|
|
"compatible with -fsanitize=kcfi on this target"));
|
|
|
|
IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
|
|
MDNode *VeryUnlikelyWeights = MDBuilder(Ctx).createUnlikelyBranchWeights();
|
|
Triple T(M.getTargetTriple());
|
|
|
|
for (CallInst *CI : KCFICalls) {
|
|
// Get the expected hash value.
|
|
const uint32_t ExpectedHash =
|
|
cast<ConstantInt>(CI->getOperandBundle(LLVMContext::OB_kcfi)->Inputs[0])
|
|
->getZExtValue();
|
|
|
|
// Drop the KCFI operand bundle.
|
|
CallBase *Call = CallBase::removeOperandBundle(CI, LLVMContext::OB_kcfi,
|
|
CI->getIterator());
|
|
assert(Call != CI);
|
|
Call->copyMetadata(*CI);
|
|
CI->replaceAllUsesWith(Call);
|
|
CI->eraseFromParent();
|
|
|
|
if (!Call->isIndirectCall())
|
|
continue;
|
|
|
|
// Emit a check and trap if the target hash doesn't match.
|
|
IRBuilder<> Builder(Call);
|
|
Value *FuncPtr = Call->getCalledOperand();
|
|
// ARM uses the least significant bit of the function pointer to select
|
|
// between ARM and Thumb modes for the callee. Instructions are always
|
|
// at least 16-bit aligned, so clear the LSB before we compute the hash
|
|
// location.
|
|
if (T.isARM() || T.isThumb()) {
|
|
FuncPtr = Builder.CreateIntToPtr(
|
|
Builder.CreateAnd(Builder.CreatePtrToInt(FuncPtr, Int32Ty),
|
|
ConstantInt::get(Int32Ty, -2)),
|
|
FuncPtr->getType());
|
|
}
|
|
Value *HashPtr = Builder.CreateConstInBoundsGEP1_32(Int32Ty, FuncPtr, -1);
|
|
Value *Test = Builder.CreateICmpNE(Builder.CreateLoad(Int32Ty, HashPtr),
|
|
ConstantInt::get(Int32Ty, ExpectedHash));
|
|
Instruction *ThenTerm =
|
|
SplitBlockAndInsertIfThen(Test, Call, false, VeryUnlikelyWeights);
|
|
Builder.SetInsertPoint(ThenTerm);
|
|
Builder.CreateIntrinsic(Intrinsic::debugtrap, {});
|
|
++NumKCFIChecks;
|
|
}
|
|
|
|
return PreservedAnalyses::none();
|
|
}
|