Skip to content

Commit 1d2b558

Browse files
committed
[AArch64][PAC] Check authenticated LR value during tail call
When performing a tail call, check the value of LR register after authentication to prevent the callee from signing and spilling an untrusted value. This commit implements a few variants of check, more can be added later. If it is safe to assume that executable pages are always readable, LR can be checked just by dereferencing the LR value via LDR. As an alternative, LR can be checked as follows: ; lowered AUT* instruction ; <some variant of check that LR contains a valid address> b.cond break_block ret_block: ; lowered TCRETURN break_block: brk 0xc471 As the existing methods either break the compatibility with execute-only memory mappings or can degrade the performance, they are disabled by default and can be explicitly enabled with a command line option. Individual subtargets can opt-in to use one of the available methods by updating AArch64FrameLowering::getAuthenticatedLRCheckMethod(). Reviewed By: kristof.beyls Differential Revision: https://reviews.llvm.org/D156716
1 parent df5c278 commit 1d2b558

10 files changed

+534
-31
lines changed

llvm/lib/Target/AArch64/AArch64FrameLowering.cpp

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -269,14 +269,10 @@ STATISTIC(NumRedZoneFunctions, "Number of functions using red zone");
269269
static int64_t getArgumentStackToRestore(MachineFunction &MF,
270270
MachineBasicBlock &MBB) {
271271
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
272-
bool IsTailCallReturn = false;
273-
if (MBB.end() != MBBI) {
274-
unsigned RetOpcode = MBBI->getOpcode();
275-
IsTailCallReturn = RetOpcode == AArch64::TCRETURNdi ||
276-
RetOpcode == AArch64::TCRETURNri ||
277-
RetOpcode == AArch64::TCRETURNriBTI;
278-
}
279272
AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>();
273+
bool IsTailCallReturn = (MBB.end() != MBBI)
274+
? AArch64InstrInfo::isTailCallReturnInst(*MBBI)
275+
: false;
280276

281277
int64_t ArgumentPopSize = 0;
282278
if (IsTailCallReturn) {
@@ -300,7 +296,6 @@ static int64_t getArgumentStackToRestore(MachineFunction &MF,
300296
static bool produceCompactUnwindFrame(MachineFunction &MF);
301297
static bool needsWinCFI(const MachineFunction &MF);
302298
static StackOffset getSVEStackSize(const MachineFunction &MF);
303-
static bool needsShadowCallStackPrologueEpilogue(MachineFunction &MF);
304299

305300
/// Returns true if a homogeneous prolog or epilog code can be emitted
306301
/// for the size optimization. If possible, a frame helper call is injected.
@@ -617,7 +612,7 @@ void AArch64FrameLowering::resetCFIToInitialState(
617612
}
618613

619614
// Shadow call stack uses X18, reset it.
620-
if (needsShadowCallStackPrologueEpilogue(MF))
615+
if (MFI.needsShadowCallStackPrologueEpilogue(MF))
621616
insertCFISameValue(CFIDesc, MF, MBB, InsertPt,
622617
TRI.getDwarfRegNum(AArch64::X18, true));
623618

@@ -1290,19 +1285,6 @@ static bool IsSVECalleeSave(MachineBasicBlock::iterator I) {
12901285
}
12911286
}
12921287

1293-
static bool needsShadowCallStackPrologueEpilogue(MachineFunction &MF) {
1294-
if (!(llvm::any_of(
1295-
MF.getFrameInfo().getCalleeSavedInfo(),
1296-
[](const auto &Info) { return Info.getReg() == AArch64::LR; }) &&
1297-
MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)))
1298-
return false;
1299-
1300-
if (!MF.getSubtarget<AArch64Subtarget>().isXRegisterReserved(18))
1301-
report_fatal_error("Must reserve x18 to use shadow call stack");
1302-
1303-
return true;
1304-
}
1305-
13061288
static void emitShadowCallStackPrologue(const TargetInstrInfo &TII,
13071289
MachineFunction &MF,
13081290
MachineBasicBlock &MBB,
@@ -1414,7 +1396,7 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
14141396
DebugLoc DL;
14151397

14161398
const auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
1417-
if (needsShadowCallStackPrologueEpilogue(MF))
1399+
if (MFnI.needsShadowCallStackPrologueEpilogue(MF))
14181400
emitShadowCallStackPrologue(*TII, MF, MBB, MBBI, DL, NeedsWinCFI,
14191401
MFnI.needsDwarfUnwindInfo(MF));
14201402

@@ -1945,7 +1927,7 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF,
19451927
if (NeedsWinCFI)
19461928
HasWinCFI = true; // AArch64PointerAuth pass will insert SEH_PACSignLR
19471929
}
1948-
if (needsShadowCallStackPrologueEpilogue(MF))
1930+
if (AFI->needsShadowCallStackPrologueEpilogue(MF))
19491931
emitShadowCallStackEpilogue(*TII, MF, MBB, MBB.getFirstTerminator(), DL);
19501932
if (EmitCFI)
19511933
emitCalleeSavedGPRRestores(MBB, MBB.getFirstTerminator());

llvm/lib/Target/AArch64/AArch64InstrInfo.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "AArch64InstrInfo.h"
1515
#include "AArch64FrameLowering.h"
1616
#include "AArch64MachineFunctionInfo.h"
17+
#include "AArch64PointerAuth.h"
1718
#include "AArch64Subtarget.h"
1819
#include "MCTargetDesc/AArch64AddressingModes.h"
1920
#include "Utils/AArch64BaseInfo.h"
@@ -2490,6 +2491,20 @@ bool AArch64InstrInfo::isPairableLdStInst(const MachineInstr &MI) {
24902491
}
24912492
}
24922493

2494+
bool AArch64InstrInfo::isTailCallReturnInst(const MachineInstr &MI) {
2495+
switch (MI.getOpcode()) {
2496+
default:
2497+
assert((!MI.isCall() || !MI.isReturn()) &&
2498+
"Unexpected instruction - was a new tail call opcode introduced?");
2499+
return false;
2500+
case AArch64::TCRETURNdi:
2501+
case AArch64::TCRETURNri:
2502+
case AArch64::TCRETURNriBTI:
2503+
case AArch64::TCRETURNriALL:
2504+
return true;
2505+
}
2506+
}
2507+
24932508
unsigned AArch64InstrInfo::convertToFlagSettingOpc(unsigned Opc) {
24942509
switch (Opc) {
24952510
default:
@@ -8217,12 +8232,24 @@ AArch64InstrInfo::getOutliningCandidateInfo(
82178232
// necessary. However, at this point we don't know if the outlined function
82188233
// will have a RET instruction so we assume the worst.
82198234
const TargetRegisterInfo &TRI = getRegisterInfo();
8235+
// Performing a tail call may require extra checks when PAuth is enabled.
8236+
// If PAuth is disabled, set it to zero for uniformity.
8237+
unsigned NumBytesToCheckLRInTCEpilogue = 0;
82208238
if (FirstCand.getMF()
82218239
->getInfo<AArch64FunctionInfo>()
82228240
->shouldSignReturnAddress(true)) {
82238241
// One PAC and one AUT instructions
82248242
NumBytesToCreateFrame += 8;
82258243

8244+
// PAuth is enabled - set extra tail call cost, if any.
8245+
auto LRCheckMethod = Subtarget.getAuthenticatedLRCheckMethod();
8246+
NumBytesToCheckLRInTCEpilogue =
8247+
AArch64PAuth::getCheckerSizeInBytes(LRCheckMethod);
8248+
// Checking the authenticated LR value may significantly impact
8249+
// SequenceSize, so account for it for more precise results.
8250+
if (isTailCallReturnInst(*RepeatedSequenceLocs[0].back()))
8251+
SequenceSize += NumBytesToCheckLRInTCEpilogue;
8252+
82268253
// We have to check if sp modifying instructions would get outlined.
82278254
// If so we only allow outlining if sp is unchanged overall, so matching
82288255
// sub and add instructions are okay to outline, all other sp modifications
@@ -8393,7 +8420,8 @@ AArch64InstrInfo::getOutliningCandidateInfo(
83938420
if (RepeatedSequenceLocs[0].back()->isTerminator()) {
83948421
FrameID = MachineOutlinerTailCall;
83958422
NumBytesToCreateFrame = 0;
8396-
SetCandidateCallInfo(MachineOutlinerTailCall, 4);
8423+
unsigned NumBytesForCall = 4 + NumBytesToCheckLRInTCEpilogue;
8424+
SetCandidateCallInfo(MachineOutlinerTailCall, NumBytesForCall);
83978425
}
83988426

83998427
else if (LastInstrOpcode == AArch64::BL ||
@@ -8402,7 +8430,7 @@ AArch64InstrInfo::getOutliningCandidateInfo(
84028430
!HasBTI)) {
84038431
// FIXME: Do we need to check if the code after this uses the value of LR?
84048432
FrameID = MachineOutlinerThunk;
8405-
NumBytesToCreateFrame = 0;
8433+
NumBytesToCreateFrame = NumBytesToCheckLRInTCEpilogue;
84068434
SetCandidateCallInfo(MachineOutlinerThunk, 4);
84078435
}
84088436

llvm/lib/Target/AArch64/AArch64InstrInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ class AArch64InstrInfo final : public AArch64GenInstrInfo {
129129
/// Return true if pairing the given load or store may be paired with another.
130130
static bool isPairableLdStInst(const MachineInstr &MI);
131131

132+
/// Returns true if MI is one of the TCRETURN* instructions.
133+
static bool isTailCallReturnInst(const MachineInstr &MI);
134+
132135
/// Return the opcode that set flags when possible. The caller is
133136
/// responsible for ensuring the opc has a flag setting equivalent.
134137
static unsigned convertToFlagSettingOpc(unsigned Opc);

llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,27 @@ bool AArch64FunctionInfo::shouldSignReturnAddress(bool SpillsLR) const {
122122
return SpillsLR;
123123
}
124124

125+
static bool isLRSpilled(const MachineFunction &MF) {
126+
return llvm::any_of(
127+
MF.getFrameInfo().getCalleeSavedInfo(),
128+
[](const auto &Info) { return Info.getReg() == AArch64::LR; });
129+
}
130+
125131
bool AArch64FunctionInfo::shouldSignReturnAddress(
126132
const MachineFunction &MF) const {
127-
return shouldSignReturnAddress(llvm::any_of(
128-
MF.getFrameInfo().getCalleeSavedInfo(),
129-
[](const auto &Info) { return Info.getReg() == AArch64::LR; }));
133+
return shouldSignReturnAddress(isLRSpilled(MF));
134+
}
135+
136+
bool AArch64FunctionInfo::needsShadowCallStackPrologueEpilogue(
137+
MachineFunction &MF) const {
138+
if (!(isLRSpilled(MF) &&
139+
MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)))
140+
return false;
141+
142+
if (!MF.getSubtarget<AArch64Subtarget>().isXRegisterReserved(18))
143+
report_fatal_error("Must reserve x18 to use shadow call stack");
144+
145+
return true;
130146
}
131147

132148
bool AArch64FunctionInfo::needsDwarfUnwindInfo(

llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,8 @@ class AArch64FunctionInfo final : public MachineFunctionInfo {
431431
bool shouldSignReturnAddress(const MachineFunction &MF) const;
432432
bool shouldSignReturnAddress(bool SpillsLR) const;
433433

434+
bool needsShadowCallStackPrologueEpilogue(MachineFunction &MF) const;
435+
434436
bool shouldSignWithBKey() const { return SignWithBKey; }
435437
bool isMTETagged() const { return IsMTETagged; }
436438

0 commit comments

Comments
 (0)