@@ -153,6 +153,7 @@ class AArch64AsmPrinter : public AsmPrinter {
153
153
void emitPtrauthCheckAuthenticatedValue (Register TestedReg,
154
154
Register ScratchReg,
155
155
AArch64PACKey::ID Key,
156
+ AArch64PAuth::AuthCheckMethod Method,
156
157
bool ShouldTrap,
157
158
const MCSymbol *OnFailure);
158
159
@@ -1731,7 +1732,8 @@ unsigned AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc,
1731
1732
// / of proceeding to the next instruction (only if ShouldTrap is false).
1732
1733
void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue (
1733
1734
Register TestedReg, Register ScratchReg, AArch64PACKey::ID Key,
1734
- bool ShouldTrap, const MCSymbol *OnFailure) {
1735
+ AArch64PAuth::AuthCheckMethod Method, bool ShouldTrap,
1736
+ const MCSymbol *OnFailure) {
1735
1737
// Insert a sequence to check if authentication of TestedReg succeeded,
1736
1738
// such as:
1737
1739
//
@@ -1757,38 +1759,70 @@ void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
1757
1759
// Lsuccess:
1758
1760
// ...
1759
1761
//
1760
- // This sequence is expensive, but we need more information to be able to
1761
- // do better.
1762
- //
1763
- // We can't TBZ the poison bit because EnhancedPAC2 XORs the PAC bits
1764
- // on failure.
1765
- // We can't TST the PAC bits because we don't always know how the address
1766
- // space is setup for the target environment (and the bottom PAC bit is
1767
- // based on that).
1768
- // Either way, we also don't always know whether TBI is enabled or not for
1769
- // the specific target environment.
1762
+ // See the documentation on AuthCheckMethod enumeration constants for
1763
+ // the specific code sequences that can be used to perform the check.
1764
+ using AArch64PAuth::AuthCheckMethod;
1770
1765
1771
- unsigned XPACOpc = getXPACOpcodeForKey (Key);
1766
+ if (Method == AuthCheckMethod::None)
1767
+ return ;
1768
+ if (Method == AuthCheckMethod::DummyLoad) {
1769
+ EmitToStreamer (MCInstBuilder (AArch64::LDRWui)
1770
+ .addReg (getWRegFromXReg (ScratchReg))
1771
+ .addReg (TestedReg)
1772
+ .addImm (0 ));
1773
+ assert (ShouldTrap && !OnFailure && " DummyLoad always traps on error" );
1774
+ return ;
1775
+ }
1772
1776
1773
1777
MCSymbol *SuccessSym = createTempSymbol (" auth_success_" );
1778
+ if (Method == AuthCheckMethod::XPAC || Method == AuthCheckMethod::XPACHint) {
1779
+ // mov Xscratch, Xtested
1780
+ emitMovXReg (ScratchReg, TestedReg);
1774
1781
1775
- // mov Xscratch, Xtested
1776
- emitMovXReg (ScratchReg, TestedReg);
1777
-
1778
- // xpac(i|d) Xscratch
1779
- EmitToStreamer (MCInstBuilder (XPACOpc).addReg (ScratchReg).addReg (ScratchReg));
1782
+ if (Method == AuthCheckMethod::XPAC) {
1783
+ // xpac(i|d) Xscratch
1784
+ unsigned XPACOpc = getXPACOpcodeForKey (Key);
1785
+ EmitToStreamer (
1786
+ MCInstBuilder (XPACOpc).addReg (ScratchReg).addReg (ScratchReg));
1787
+ } else {
1788
+ // xpaclri
1789
+
1790
+ // Note that this method applies XPAC to TestedReg instead of ScratchReg.
1791
+ assert (TestedReg == AArch64::LR &&
1792
+ " XPACHint mode is only compatible with checking the LR register" );
1793
+ assert ((Key == AArch64PACKey::IA || Key == AArch64PACKey::IB) &&
1794
+ " XPACHint mode is only compatible with I-keys" );
1795
+ EmitToStreamer (MCInstBuilder (AArch64::XPACLRI));
1796
+ }
1780
1797
1781
- // cmp Xtested, Xscratch
1782
- EmitToStreamer (MCInstBuilder (AArch64::SUBSXrs)
1783
- .addReg (AArch64::XZR)
1784
- .addReg (TestedReg)
1785
- .addReg (ScratchReg)
1786
- .addImm (0 ));
1798
+ // cmp Xtested, Xscratch
1799
+ EmitToStreamer (MCInstBuilder (AArch64::SUBSXrs)
1800
+ .addReg (AArch64::XZR)
1801
+ .addReg (TestedReg)
1802
+ .addReg (ScratchReg)
1803
+ .addImm (0 ));
1787
1804
1788
- // b.eq Lsuccess
1789
- EmitToStreamer (MCInstBuilder (AArch64::Bcc)
1790
- .addImm (AArch64CC::EQ)
1791
- .addExpr (MCSymbolRefExpr::create (SuccessSym, OutContext)));
1805
+ // b.eq Lsuccess
1806
+ EmitToStreamer (
1807
+ MCInstBuilder (AArch64::Bcc)
1808
+ .addImm (AArch64CC::EQ)
1809
+ .addExpr (MCSymbolRefExpr::create (SuccessSym, OutContext)));
1810
+ } else if (Method == AuthCheckMethod::HighBitsNoTBI) {
1811
+ // eor Xscratch, Xtested, Xtested, lsl #1
1812
+ EmitToStreamer (MCInstBuilder (AArch64::EORXrs)
1813
+ .addReg (ScratchReg)
1814
+ .addReg (TestedReg)
1815
+ .addReg (TestedReg)
1816
+ .addImm (1 ));
1817
+ // tbz Xscratch, #62, Lsuccess
1818
+ EmitToStreamer (
1819
+ MCInstBuilder (AArch64::TBZX)
1820
+ .addReg (ScratchReg)
1821
+ .addImm (62 )
1822
+ .addExpr (MCSymbolRefExpr::create (SuccessSym, OutContext)));
1823
+ } else {
1824
+ llvm_unreachable (" Unsupported check method" );
1825
+ }
1792
1826
1793
1827
if (ShouldTrap) {
1794
1828
assert (!OnFailure && " Cannot specify OnFailure with ShouldTrap" );
@@ -1802,9 +1836,26 @@ void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
1802
1836
// Note that this can introduce an authentication oracle (such as based on
1803
1837
// the high bits of the re-signed value).
1804
1838
1805
- // FIXME: Can we simply return the AUT result, already in TestedReg?
1806
- // mov Xtested, Xscratch
1807
- emitMovXReg (TestedReg, ScratchReg);
1839
+ // FIXME: The XPAC method can be optimized by applying XPAC to TestedReg
1840
+ // instead of ScratchReg, thus eliminating one `mov` instruction.
1841
+ // Both XPAC and XPACHint can be further optimized by not using a
1842
+ // conditional branch jumping over an unconditional one.
1843
+
1844
+ switch (Method) {
1845
+ case AuthCheckMethod::XPACHint:
1846
+ // LR is already XPAC-ed at this point.
1847
+ break ;
1848
+ case AuthCheckMethod::XPAC:
1849
+ // mov Xtested, Xscratch
1850
+ emitMovXReg (TestedReg, ScratchReg);
1851
+ break ;
1852
+ default :
1853
+ // If Xtested was not XPAC-ed so far, emit XPAC here.
1854
+ // xpac(i|d) Xtested
1855
+ unsigned XPACOpc = getXPACOpcodeForKey (Key);
1856
+ EmitToStreamer (
1857
+ MCInstBuilder (XPACOpc).addReg (TestedReg).addReg (TestedReg));
1858
+ }
1808
1859
1809
1860
if (OnFailure) {
1810
1861
// b Lend
@@ -1830,7 +1881,7 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
1830
1881
// ; sign x16 (if AUTPAC)
1831
1882
// Lend: ; if not trapping on failure
1832
1883
//
1833
- // with the checking sequence chosen depending on whether we should check
1884
+ // with the checking sequence chosen depending on whether/how we should check
1834
1885
// the pointer and whether we should trap on failure.
1835
1886
1836
1887
// By default, auth/resign sequences check for auth failures.
@@ -1890,6 +1941,7 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
1890
1941
EndSym = createTempSymbol (" resign_end_" );
1891
1942
1892
1943
emitPtrauthCheckAuthenticatedValue (AArch64::X16, AArch64::X17, AUTKey,
1944
+ AArch64PAuth::AuthCheckMethod::XPAC,
1893
1945
ShouldTrap, EndSym);
1894
1946
}
1895
1947
@@ -2260,11 +2312,34 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
2260
2312
OutStreamer->emitLabel (LOHLabel);
2261
2313
}
2262
2314
2315
+ // With Pointer Authentication, it may be needed to explicitly check the
2316
+ // authenticated value in LR when performing a tail call.
2317
+ // Otherwise, the callee may re-sign the invalid return address,
2318
+ // introducing a signing oracle.
2319
+ auto CheckLRInTailCall = [this ](Register CallDestinationReg) {
2320
+ if (!AArch64FI->shouldSignReturnAddress (*MF))
2321
+ return ;
2322
+
2323
+ auto LRCheckMethod = STI->getAuthenticatedLRCheckMethod (*MF);
2324
+ if (LRCheckMethod == AArch64PAuth::AuthCheckMethod::None)
2325
+ return ;
2326
+
2327
+ Register ScratchReg =
2328
+ CallDestinationReg == AArch64::X16 ? AArch64::X17 : AArch64::X16;
2329
+ AArch64PACKey::ID Key =
2330
+ AArch64FI->shouldSignWithBKey () ? AArch64PACKey::IB : AArch64PACKey::IA;
2331
+ emitPtrauthCheckAuthenticatedValue (
2332
+ AArch64::LR, ScratchReg, Key, LRCheckMethod,
2333
+ /* ShouldTrap=*/ true , /* OnFailure=*/ nullptr );
2334
+ };
2335
+
2263
2336
AArch64TargetStreamer *TS =
2264
2337
static_cast <AArch64TargetStreamer *>(OutStreamer->getTargetStreamer ());
2265
2338
// Do any manual lowerings.
2266
2339
switch (MI->getOpcode ()) {
2267
2340
default :
2341
+ assert (!AArch64InstrInfo::isTailCallReturnInst (*MI) &&
2342
+ " Unhandled tail call instruction" );
2268
2343
break ;
2269
2344
case AArch64::HINT: {
2270
2345
// CurrentPatchableFunctionEntrySym can be CurrentFnBegin only for
@@ -2404,6 +2479,8 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
2404
2479
? AArch64::X17
2405
2480
: AArch64::X16;
2406
2481
2482
+ CheckLRInTailCall (MI->getOperand (0 ).getReg ());
2483
+
2407
2484
unsigned DiscReg = AddrDisc;
2408
2485
if (Disc) {
2409
2486
if (AddrDisc != AArch64::NoRegister) {
@@ -2434,13 +2511,17 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
2434
2511
case AArch64::TCRETURNrix17:
2435
2512
case AArch64::TCRETURNrinotx16:
2436
2513
case AArch64::TCRETURNriALL: {
2514
+ CheckLRInTailCall (MI->getOperand (0 ).getReg ());
2515
+
2437
2516
MCInst TmpInst;
2438
2517
TmpInst.setOpcode (AArch64::BR);
2439
2518
TmpInst.addOperand (MCOperand::createReg (MI->getOperand (0 ).getReg ()));
2440
2519
EmitToStreamer (*OutStreamer, TmpInst);
2441
2520
return ;
2442
2521
}
2443
2522
case AArch64::TCRETURNdi: {
2523
+ CheckLRInTailCall (AArch64::NoRegister);
2524
+
2444
2525
MCOperand Dest;
2445
2526
MCInstLowering.lowerOperand (MI->getOperand (0 ), Dest);
2446
2527
MCInst TmpInst;
0 commit comments