[AArch64][v8.5A] Branch Target Identification code-generation pass
The Branch Target Identification extension, introduced to AArch64 in
Armv8.5-A, adds the BTI instruction, which is used to mark valid targets
of indirect branches. When enabled, the processor will trap if an
instruction in a protected page tries to perform an indirect branch to
any instruction other than a BTI. The BTI instruction uses encodings
which were NOPs in earlier versions of the architecture, so BTI-enabled
code will still run on earlier hardware, just without the extra
protection.
There are 3 variants of the BTI instruction, which are valid targets for
different kinds or branches:
- BTI C can be targeted by call instructions, and is inteneded to be
used at function entry points. These are the BLR instruction, as well
as BR with x16 or x17. These BR instructions are allowed for use in
PLT entries, and we can also use them to allow indirect tail-calls.
- BTI J can be targeted by BR only, and is intended to be used by jump
tables.
- BTI JC acts ab both a BTI C and a BTI J instruction, and can be
targeted by any BLR or BR instruction.
Note that RET instructions are not restricted by branch target
identification, the reason for this is that return addresses can be
protected more effectively using return address signing. Direct branches
and calls are also unaffected, as it is assumed that an attacker cannot
modify executable pages (if they could, they wouldn't need to do a
ROP/JOP attack).
This patch adds a MachineFunctionPass which:
- Adds a BTI C at the start of every function which could be indirectly
called (either because it is address-taken, or externally visible so
could be address-taken in another translation unit).
- Adds a BTI J at the start of every basic block which could be
indirectly branched to. This could be either done by a jump table, or
by taking the address of the block (e.g. the using GCC label values
extension).
We only need to use BTI JC when a function is indirectly-callable, and
takes the address of the entry block. I've not been able to trigger this
from C or IR, but I've included a MIR test just in case.
Using BTI C at function entries relies on the fact that no other code in
BTI-protected pages uses indirect tail-calls, unless they use x16 or x17
to hold the address. I'll add that code-generation restriction as a
separate patch.
Differential revision: https://reviews.llvm.org/D52867
llvm-svn: 343967
2018-10-08 22:04:24 +08:00
|
|
|
//===-- AArch64BranchTargets.cpp -- Harden code using v8.5-A BTI extension -==//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
[AArch64][v8.5A] Branch Target Identification code-generation pass
The Branch Target Identification extension, introduced to AArch64 in
Armv8.5-A, adds the BTI instruction, which is used to mark valid targets
of indirect branches. When enabled, the processor will trap if an
instruction in a protected page tries to perform an indirect branch to
any instruction other than a BTI. The BTI instruction uses encodings
which were NOPs in earlier versions of the architecture, so BTI-enabled
code will still run on earlier hardware, just without the extra
protection.
There are 3 variants of the BTI instruction, which are valid targets for
different kinds or branches:
- BTI C can be targeted by call instructions, and is inteneded to be
used at function entry points. These are the BLR instruction, as well
as BR with x16 or x17. These BR instructions are allowed for use in
PLT entries, and we can also use them to allow indirect tail-calls.
- BTI J can be targeted by BR only, and is intended to be used by jump
tables.
- BTI JC acts ab both a BTI C and a BTI J instruction, and can be
targeted by any BLR or BR instruction.
Note that RET instructions are not restricted by branch target
identification, the reason for this is that return addresses can be
protected more effectively using return address signing. Direct branches
and calls are also unaffected, as it is assumed that an attacker cannot
modify executable pages (if they could, they wouldn't need to do a
ROP/JOP attack).
This patch adds a MachineFunctionPass which:
- Adds a BTI C at the start of every function which could be indirectly
called (either because it is address-taken, or externally visible so
could be address-taken in another translation unit).
- Adds a BTI J at the start of every basic block which could be
indirectly branched to. This could be either done by a jump table, or
by taking the address of the block (e.g. the using GCC label values
extension).
We only need to use BTI JC when a function is indirectly-callable, and
takes the address of the entry block. I've not been able to trigger this
from C or IR, but I've included a MIR test just in case.
Using BTI C at function entries relies on the fact that no other code in
BTI-protected pages uses indirect tail-calls, unless they use x16 or x17
to hold the address. I'll add that code-generation restriction as a
separate patch.
Differential revision: https://reviews.llvm.org/D52867
llvm-svn: 343967
2018-10-08 22:04:24 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This pass inserts BTI instructions at the start of every function and basic
|
|
|
|
// block which could be indirectly called. The hardware will (when enabled)
|
|
|
|
// trap when an indirect branch or call instruction targets an instruction
|
|
|
|
// which is not a valid BTI instruction. This is intended to guard against
|
|
|
|
// control-flow hijacking attacks. Note that this does not do anything for RET
|
|
|
|
// instructions, as they can be more precisely protected by return address
|
|
|
|
// signing.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "AArch64Subtarget.h"
|
|
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
|
|
#include "llvm/CodeGen/MachineJumpTableInfo.h"
|
|
|
|
#include "llvm/CodeGen/MachineModuleInfo.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "aarch64-branch-targets"
|
|
|
|
#define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class AArch64BranchTargets : public MachineFunctionPass {
|
|
|
|
public:
|
|
|
|
static char ID;
|
|
|
|
AArch64BranchTargets() : MachineFunctionPass(ID) {}
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
|
|
StringRef getPassName() const override { return AARCH64_BRANCH_TARGETS_NAME; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump);
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
char AArch64BranchTargets::ID = 0;
|
|
|
|
|
|
|
|
INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets",
|
|
|
|
AARCH64_BRANCH_TARGETS_NAME, false, false)
|
|
|
|
|
|
|
|
void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
|
|
AU.setPreservesCFG();
|
|
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
|
|
}
|
|
|
|
|
|
|
|
FunctionPass *llvm::createAArch64BranchTargetsPass() {
|
|
|
|
return new AArch64BranchTargets();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {
|
|
|
|
const Function &F = MF.getFunction();
|
|
|
|
if (!F.hasFnAttribute("branch-target-enforcement"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
LLVM_DEBUG(
|
|
|
|
dbgs() << "********** AArch64 Branch Targets **********\n"
|
|
|
|
<< "********** Function: " << MF.getName() << '\n');
|
|
|
|
|
|
|
|
// LLVM does not consider basic blocks which are the targets of jump tables
|
|
|
|
// to be address-taken (the address can't escape anywhere else), but they are
|
|
|
|
// used for indirect branches, so need BTI instructions.
|
|
|
|
SmallPtrSet<MachineBasicBlock *, 8> JumpTableTargets;
|
|
|
|
if (auto *JTI = MF.getJumpTableInfo())
|
|
|
|
for (auto &JTE : JTI->getJumpTables())
|
|
|
|
for (auto *MBB : JTE.MBBs)
|
|
|
|
JumpTableTargets.insert(MBB);
|
|
|
|
|
|
|
|
bool MadeChange = false;
|
|
|
|
for (MachineBasicBlock &MBB : MF) {
|
|
|
|
bool CouldCall = false, CouldJump = false;
|
|
|
|
// If the function is address-taken or externally-visible, it could be
|
|
|
|
// indirectly called. PLT entries and tail-calls use BR, but when they are
|
|
|
|
// are in guarded pages should all use x16 or x17 to hold the called
|
|
|
|
// address, so we don't need to set CouldJump here. BR instructions in
|
|
|
|
// non-guarded pages (which might be non-BTI-aware code) are allowed to
|
|
|
|
// branch to a "BTI c" using any register.
|
|
|
|
if (&MBB == &*MF.begin() && (F.hasAddressTaken() || !F.hasLocalLinkage()))
|
|
|
|
CouldCall = true;
|
|
|
|
|
|
|
|
// If the block itself is address-taken, it could be indirectly branched
|
|
|
|
// to, but not called.
|
|
|
|
if (MBB.hasAddressTaken() || JumpTableTargets.count(&MBB))
|
|
|
|
CouldJump = true;
|
|
|
|
|
|
|
|
if (CouldCall || CouldJump) {
|
|
|
|
addBTI(MBB, CouldCall, CouldJump);
|
|
|
|
MadeChange = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MadeChange;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall,
|
|
|
|
bool CouldJump) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "")
|
|
|
|
<< (CouldCall ? "c" : "") << " to " << MBB.getName()
|
|
|
|
<< "\n");
|
|
|
|
|
|
|
|
const AArch64InstrInfo *TII = static_cast<const AArch64InstrInfo *>(
|
|
|
|
MBB.getParent()->getSubtarget().getInstrInfo());
|
|
|
|
|
|
|
|
unsigned HintNum = 32;
|
|
|
|
if (CouldCall)
|
|
|
|
HintNum |= 2;
|
|
|
|
if (CouldJump)
|
|
|
|
HintNum |= 4;
|
|
|
|
assert(HintNum != 32 && "No target kinds!");
|
|
|
|
|
|
|
|
auto MBBI = MBB.begin();
|
|
|
|
|
|
|
|
// PACI[AB]SP are implicitly BTI JC, so no BTI instruction needed there.
|
|
|
|
if (MBBI != MBB.end() && (MBBI->getOpcode() == AArch64::PACIASP ||
|
|
|
|
MBBI->getOpcode() == AArch64::PACIBSP))
|
|
|
|
return;
|
|
|
|
|
|
|
|
BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()),
|
|
|
|
TII->get(AArch64::HINT))
|
|
|
|
.addImm(HintNum);
|
|
|
|
}
|