Skip to content

Commit f788c67

Browse files
committed
[AArch64][PAC] Factor out the emission of pointer check sequence (NFC)
When pointer is authenticated or resigned, it may be required to explicitly check the authenticated value to prevent introducing signing or authentication oracles. While the check sequence is expensive in general, a more efficient sequence can be emitted under specific assumptions. This commit factors out the emission of the code sequence to check the authenticated pointer value in preparation for adding other variants of checking code, as it is currently done when emitting tail calls.
1 parent 0de0354 commit f788c67

File tree

1 file changed

+91
-77
lines changed

1 file changed

+91
-77
lines changed

llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp

+91-77
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ class AArch64AsmPrinter : public AsmPrinter {
150150
// Emit the sequence for BRA/BLRA (authenticate + branch/call).
151151
void emitPtrauthBranch(const MachineInstr *MI);
152152

153+
void emitPtrauthCheckAuthenticatedValue(Register TestedReg,
154+
Register ScratchReg,
155+
AArch64PACKey::ID Key,
156+
bool ShouldTrap,
157+
const MCSymbol *OnFailure);
158+
153159
// Emit the sequence for AUT or AUTPAC.
154160
void emitPtrauthAuthResign(const MachineInstr *MI);
155161

@@ -1719,45 +1725,37 @@ unsigned AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc,
17191725
return AArch64::X17;
17201726
}
17211727

1722-
void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
1723-
const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC;
1724-
1725-
// We can expand AUT/AUTPAC into 3 possible sequences:
1726-
// - unchecked:
1727-
// autia x16, x0
1728-
// pacib x16, x1 ; if AUTPAC
1728+
/// Emits a code sequence to check an authenticated pointer value.
1729+
///
1730+
/// If OnFailure argument is passed, jump there on check failure instead
1731+
/// of proceeding to the next instruction (only if ShouldTrap is false).
1732+
void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
1733+
Register TestedReg, Register ScratchReg, AArch64PACKey::ID Key,
1734+
bool ShouldTrap, const MCSymbol *OnFailure) {
1735+
// Insert a sequence to check if authentication of TestedReg succeeded,
1736+
// such as:
17291737
//
17301738
// - checked and clearing:
1731-
// mov x17, x0
1732-
// movk x17, #disc, lsl #48
1733-
// autia x16, x17
1739+
// ; x16 is TestedReg, x17 is ScratchReg
17341740
// mov x17, x16
17351741
// xpaci x17
17361742
// cmp x16, x17
17371743
// b.eq Lsuccess
17381744
// mov x16, x17
17391745
// b Lend
1740-
// Lsuccess:
1741-
// mov x17, x1
1742-
// movk x17, #disc, lsl #48
1743-
// pacib x16, x17
1744-
// Lend:
1745-
// Where we only emit the AUT if we started with an AUT.
1746+
// Lsuccess:
1747+
// ; skipped if authentication failed
1748+
// Lend:
1749+
// ...
17461750
//
17471751
// - checked and trapping:
1748-
// mov x17, x0
1749-
// movk x17, #disc, lsl #48
1750-
// autia x16, x0
17511752
// mov x17, x16
17521753
// xpaci x17
17531754
// cmp x16, x17
17541755
// b.eq Lsuccess
17551756
// brk #<0xc470 + aut key>
1756-
// Lsuccess:
1757-
// mov x17, x1
1758-
// movk x17, #disc, lsl #48
1759-
// pacib x16, x17 ; if AUTPAC
1760-
// Where the b.eq skips over the trap if the PAC is valid.
1757+
// Lsuccess:
1758+
// ...
17611759
//
17621760
// This sequence is expensive, but we need more information to be able to
17631761
// do better.
@@ -1770,6 +1768,71 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
17701768
// Either way, we also don't always know whether TBI is enabled or not for
17711769
// the specific target environment.
17721770

1771+
unsigned XPACOpc = getXPACOpcodeForKey(Key);
1772+
1773+
MCSymbol *SuccessSym = createTempSymbol("auth_success_");
1774+
1775+
// mov Xscratch, Xtested
1776+
emitMovXReg(ScratchReg, TestedReg);
1777+
1778+
// xpac(i|d) Xscratch
1779+
EmitToStreamer(MCInstBuilder(XPACOpc).addReg(ScratchReg).addReg(ScratchReg));
1780+
1781+
// cmp Xtested, Xscratch
1782+
EmitToStreamer(MCInstBuilder(AArch64::SUBSXrs)
1783+
.addReg(AArch64::XZR)
1784+
.addReg(TestedReg)
1785+
.addReg(ScratchReg)
1786+
.addImm(0));
1787+
1788+
// b.eq Lsuccess
1789+
EmitToStreamer(MCInstBuilder(AArch64::Bcc)
1790+
.addImm(AArch64CC::EQ)
1791+
.addExpr(MCSymbolRefExpr::create(SuccessSym, OutContext)));
1792+
1793+
if (ShouldTrap) {
1794+
assert(!OnFailure && "Cannot specify OnFailure with ShouldTrap");
1795+
// Trapping sequences do a 'brk'.
1796+
// brk #<0xc470 + aut key>
1797+
EmitToStreamer(MCInstBuilder(AArch64::BRK).addImm(0xc470 | Key));
1798+
} else {
1799+
// Non-trapping checked sequences return the stripped result in TestedReg,
1800+
// skipping over success-only code (such as re-signing the pointer) if
1801+
// there is one.
1802+
// Note that this can introduce an authentication oracle (such as based on
1803+
// the high bits of the re-signed value).
1804+
1805+
// FIXME: Can we simply return the AUT result, already in TestedReg?
1806+
// mov Xtested, Xscratch
1807+
emitMovXReg(TestedReg, ScratchReg);
1808+
1809+
if (OnFailure) {
1810+
// b Lend
1811+
EmitToStreamer(
1812+
MCInstBuilder(AArch64::B)
1813+
.addExpr(MCSymbolRefExpr::create(OnFailure, OutContext)));
1814+
}
1815+
}
1816+
1817+
// If the auth check succeeds, we can continue.
1818+
// Lsuccess:
1819+
OutStreamer->emitLabel(SuccessSym);
1820+
}
1821+
1822+
void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
1823+
const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC;
1824+
1825+
// We expand AUT/AUTPAC into a sequence of the form
1826+
//
1827+
// ; authenticate x16
1828+
// ; check pointer in x16
1829+
// Lsuccess:
1830+
// ; sign x16 (if AUTPAC)
1831+
// Lend: ; if not trapping on failure
1832+
//
1833+
// with the checking sequence chosen depending on whether we should check
1834+
// the pointer and whether we should trap on failure.
1835+
17731836
// By default, auth/resign sequences check for auth failures.
17741837
bool ShouldCheck = true;
17751838
// In the checked sequence, we only trap if explicitly requested.
@@ -1800,8 +1863,6 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
18001863
uint64_t AUTDisc = MI->getOperand(1).getImm();
18011864
unsigned AUTAddrDisc = MI->getOperand(2).getReg();
18021865

1803-
unsigned XPACOpc = getXPACOpcodeForKey(AUTKey);
1804-
18051866
// Compute aut discriminator into x17
18061867
assert(isUInt<16>(AUTDisc));
18071868
unsigned AUTDiscReg = emitPtrauthDiscriminator(AUTDisc, AUTAddrDisc);
@@ -1824,59 +1885,12 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
18241885

18251886
MCSymbol *EndSym = nullptr;
18261887

1827-
// Checked sequences do an additional strip-and-compare.
18281888
if (ShouldCheck) {
1829-
MCSymbol *SuccessSym = createTempSymbol("auth_success_");
1830-
1831-
// XPAC has tied src/dst: use x17 as a temporary copy.
1832-
// mov x17, x16
1833-
emitMovXReg(AArch64::X17, AArch64::X16);
1834-
1835-
// xpaci x17
1836-
EmitToStreamer(
1837-
*OutStreamer,
1838-
MCInstBuilder(XPACOpc).addReg(AArch64::X17).addReg(AArch64::X17));
1839-
1840-
// cmp x16, x17
1841-
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXrs)
1842-
.addReg(AArch64::XZR)
1843-
.addReg(AArch64::X16)
1844-
.addReg(AArch64::X17)
1845-
.addImm(0));
1846-
1847-
// b.eq Lsuccess
1848-
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::Bcc)
1849-
.addImm(AArch64CC::EQ)
1850-
.addExpr(MCSymbolRefExpr::create(
1851-
SuccessSym, OutContext)));
1852-
1853-
if (ShouldTrap) {
1854-
// Trapping sequences do a 'brk'.
1855-
// brk #<0xc470 + aut key>
1856-
EmitToStreamer(*OutStreamer,
1857-
MCInstBuilder(AArch64::BRK).addImm(0xc470 | AUTKey));
1858-
} else {
1859-
// Non-trapping checked sequences return the stripped result in x16,
1860-
// skipping over the PAC if there is one.
1861-
1862-
// FIXME: can we simply return the AUT result, already in x16? without..
1863-
// ..traps this is usable as an oracle anyway, based on high bits
1864-
// mov x17, x16
1865-
emitMovXReg(AArch64::X16, AArch64::X17);
1866-
1867-
if (IsAUTPAC) {
1868-
EndSym = createTempSymbol("resign_end_");
1869-
1870-
// b Lend
1871-
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::B)
1872-
.addExpr(MCSymbolRefExpr::create(
1873-
EndSym, OutContext)));
1874-
}
1875-
}
1889+
if (IsAUTPAC && !ShouldTrap)
1890+
EndSym = createTempSymbol("resign_end_");
18761891

1877-
// If the auth check succeeds, we can continue.
1878-
// Lsuccess:
1879-
OutStreamer->emitLabel(SuccessSym);
1892+
emitPtrauthCheckAuthenticatedValue(AArch64::X16, AArch64::X17, AUTKey,
1893+
ShouldTrap, EndSym);
18801894
}
18811895

18821896
// We already emitted unchecked and checked-but-non-trapping AUTs.

0 commit comments

Comments
 (0)