@@ -152,9 +152,13 @@ class AArch64AsmPrinter : public AsmPrinter {
152
152
void emitPtrauthCheckAuthenticatedValue (Register TestedReg,
153
153
Register ScratchReg,
154
154
AArch64PACKey::ID Key,
155
+ AArch64PAuth::AuthCheckMethod Method,
155
156
bool ShouldTrap,
156
157
const MCSymbol *OnFailure);
157
158
159
+ // Check authenticated LR before tail calling.
160
+ void emitPtrauthTailCallHardening (const MachineInstr *TC);
161
+
158
162
// Emit the sequence for AUT or AUTPAC.
159
163
void emitPtrauthAuthResign (const MachineInstr *MI);
160
164
@@ -1751,7 +1755,8 @@ unsigned AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc,
1751
1755
// / of proceeding to the next instruction (only if ShouldTrap is false).
1752
1756
void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue (
1753
1757
Register TestedReg, Register ScratchReg, AArch64PACKey::ID Key,
1754
- bool ShouldTrap, const MCSymbol *OnFailure) {
1758
+ AArch64PAuth::AuthCheckMethod Method, bool ShouldTrap,
1759
+ const MCSymbol *OnFailure) {
1755
1760
// Insert a sequence to check if authentication of TestedReg succeeded,
1756
1761
// such as:
1757
1762
//
@@ -1777,38 +1782,70 @@ void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
1777
1782
// Lsuccess:
1778
1783
// ...
1779
1784
//
1780
- // This sequence is expensive, but we need more information to be able to
1781
- // do better.
1782
- //
1783
- // We can't TBZ the poison bit because EnhancedPAC2 XORs the PAC bits
1784
- // on failure.
1785
- // We can't TST the PAC bits because we don't always know how the address
1786
- // space is setup for the target environment (and the bottom PAC bit is
1787
- // based on that).
1788
- // Either way, we also don't always know whether TBI is enabled or not for
1789
- // the specific target environment.
1785
+ // See the documentation on AuthCheckMethod enumeration constants for
1786
+ // the specific code sequences that can be used to perform the check.
1787
+ using AArch64PAuth::AuthCheckMethod;
1790
1788
1791
- unsigned XPACOpc = getXPACOpcodeForKey (Key);
1789
+ if (Method == AuthCheckMethod::None)
1790
+ return ;
1791
+ if (Method == AuthCheckMethod::DummyLoad) {
1792
+ EmitToStreamer (MCInstBuilder (AArch64::LDRWui)
1793
+ .addReg (getWRegFromXReg (ScratchReg))
1794
+ .addReg (TestedReg)
1795
+ .addImm (0 ));
1796
+ assert (ShouldTrap && !OnFailure && " DummyLoad always traps on error" );
1797
+ return ;
1798
+ }
1792
1799
1793
1800
MCSymbol *SuccessSym = createTempSymbol (" auth_success_" );
1801
+ if (Method == AuthCheckMethod::XPAC || Method == AuthCheckMethod::XPACHint) {
1802
+ // mov Xscratch, Xtested
1803
+ emitMovXReg (ScratchReg, TestedReg);
1794
1804
1795
- // mov Xscratch, Xtested
1796
- emitMovXReg (ScratchReg, TestedReg);
1797
-
1798
- // xpac(i|d) Xscratch
1799
- EmitToStreamer (MCInstBuilder (XPACOpc).addReg (ScratchReg).addReg (ScratchReg));
1805
+ if (Method == AuthCheckMethod::XPAC) {
1806
+ // xpac(i|d) Xscratch
1807
+ unsigned XPACOpc = getXPACOpcodeForKey (Key);
1808
+ EmitToStreamer (
1809
+ MCInstBuilder (XPACOpc).addReg (ScratchReg).addReg (ScratchReg));
1810
+ } else {
1811
+ // xpaclri
1812
+
1813
+ // Note that this method applies XPAC to TestedReg instead of ScratchReg.
1814
+ assert (TestedReg == AArch64::LR &&
1815
+ " XPACHint mode is only compatible with checking the LR register" );
1816
+ assert ((Key == AArch64PACKey::IA || Key == AArch64PACKey::IB) &&
1817
+ " XPACHint mode is only compatible with I-keys" );
1818
+ EmitToStreamer (MCInstBuilder (AArch64::XPACLRI));
1819
+ }
1800
1820
1801
- // cmp Xtested, Xscratch
1802
- EmitToStreamer (MCInstBuilder (AArch64::SUBSXrs)
1803
- .addReg (AArch64::XZR)
1804
- .addReg (TestedReg)
1805
- .addReg (ScratchReg)
1806
- .addImm (0 ));
1821
+ // cmp Xtested, Xscratch
1822
+ EmitToStreamer (MCInstBuilder (AArch64::SUBSXrs)
1823
+ .addReg (AArch64::XZR)
1824
+ .addReg (TestedReg)
1825
+ .addReg (ScratchReg)
1826
+ .addImm (0 ));
1807
1827
1808
- // b.eq Lsuccess
1809
- EmitToStreamer (MCInstBuilder (AArch64::Bcc)
1810
- .addImm (AArch64CC::EQ)
1811
- .addExpr (MCSymbolRefExpr::create (SuccessSym, OutContext)));
1828
+ // b.eq Lsuccess
1829
+ EmitToStreamer (
1830
+ MCInstBuilder (AArch64::Bcc)
1831
+ .addImm (AArch64CC::EQ)
1832
+ .addExpr (MCSymbolRefExpr::create (SuccessSym, OutContext)));
1833
+ } else if (Method == AuthCheckMethod::HighBitsNoTBI) {
1834
+ // eor Xscratch, Xtested, Xtested, lsl #1
1835
+ EmitToStreamer (MCInstBuilder (AArch64::EORXrs)
1836
+ .addReg (ScratchReg)
1837
+ .addReg (TestedReg)
1838
+ .addReg (TestedReg)
1839
+ .addImm (1 ));
1840
+ // tbz Xscratch, #62, Lsuccess
1841
+ EmitToStreamer (
1842
+ MCInstBuilder (AArch64::TBZX)
1843
+ .addReg (ScratchReg)
1844
+ .addImm (62 )
1845
+ .addExpr (MCSymbolRefExpr::create (SuccessSym, OutContext)));
1846
+ } else {
1847
+ llvm_unreachable (" Unsupported check method" );
1848
+ }
1812
1849
1813
1850
if (ShouldTrap) {
1814
1851
assert (!OnFailure && " Cannot specify OnFailure with ShouldTrap" );
@@ -1822,9 +1859,26 @@ void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
1822
1859
// Note that this can introduce an authentication oracle (such as based on
1823
1860
// the high bits of the re-signed value).
1824
1861
1825
- // FIXME: Can we simply return the AUT result, already in TestedReg?
1826
- // mov Xtested, Xscratch
1827
- emitMovXReg (TestedReg, ScratchReg);
1862
+ // FIXME: The XPAC method can be optimized by applying XPAC to TestedReg
1863
+ // instead of ScratchReg, thus eliminating one `mov` instruction.
1864
+ // Both XPAC and XPACHint can be further optimized by not using a
1865
+ // conditional branch jumping over an unconditional one.
1866
+
1867
+ switch (Method) {
1868
+ case AuthCheckMethod::XPACHint:
1869
+ // LR is already XPAC-ed at this point.
1870
+ break ;
1871
+ case AuthCheckMethod::XPAC:
1872
+ // mov Xtested, Xscratch
1873
+ emitMovXReg (TestedReg, ScratchReg);
1874
+ break ;
1875
+ default :
1876
+ // If Xtested was not XPAC-ed so far, emit XPAC here.
1877
+ // xpac(i|d) Xtested
1878
+ unsigned XPACOpc = getXPACOpcodeForKey (Key);
1879
+ EmitToStreamer (
1880
+ MCInstBuilder (XPACOpc).addReg (TestedReg).addReg (TestedReg));
1881
+ }
1828
1882
1829
1883
if (OnFailure) {
1830
1884
// b Lend
@@ -1839,6 +1893,30 @@ void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
1839
1893
OutStreamer->emitLabel (SuccessSym);
1840
1894
}
1841
1895
1896
+ // With Pointer Authentication, it may be needed to explicitly check the
1897
+ // authenticated value in LR before performing a tail call.
1898
+ // Otherwise, the callee may re-sign the invalid return address,
1899
+ // introducing a signing oracle.
1900
+ void AArch64AsmPrinter::emitPtrauthTailCallHardening (const MachineInstr *TC) {
1901
+ if (!AArch64FI->shouldSignReturnAddress (*MF))
1902
+ return ;
1903
+
1904
+ auto LRCheckMethod = STI->getAuthenticatedLRCheckMethod (*MF);
1905
+ if (LRCheckMethod == AArch64PAuth::AuthCheckMethod::None)
1906
+ return ;
1907
+
1908
+ const AArch64RegisterInfo *TRI = STI->getRegisterInfo ();
1909
+ Register ScratchReg =
1910
+ TC->readsRegister (AArch64::X16, TRI) ? AArch64::X17 : AArch64::X16;
1911
+ assert (!TC->readsRegister (ScratchReg, TRI) &&
1912
+ " Neither x16 nor x17 is available as a scratch register" );
1913
+ AArch64PACKey::ID Key =
1914
+ AArch64FI->shouldSignWithBKey () ? AArch64PACKey::IB : AArch64PACKey::IA;
1915
+ emitPtrauthCheckAuthenticatedValue (
1916
+ AArch64::LR, ScratchReg, Key, LRCheckMethod,
1917
+ /* ShouldTrap=*/ true , /* OnFailure=*/ nullptr );
1918
+ }
1919
+
1842
1920
void AArch64AsmPrinter::emitPtrauthAuthResign (const MachineInstr *MI) {
1843
1921
const bool IsAUTPAC = MI->getOpcode () == AArch64::AUTPAC;
1844
1922
@@ -1850,7 +1928,7 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
1850
1928
// ; sign x16 (if AUTPAC)
1851
1929
// Lend: ; if not trapping on failure
1852
1930
//
1853
- // with the checking sequence chosen depending on whether we should check
1931
+ // with the checking sequence chosen depending on whether/how we should check
1854
1932
// the pointer and whether we should trap on failure.
1855
1933
1856
1934
// By default, auth/resign sequences check for auth failures.
@@ -1910,6 +1988,7 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
1910
1988
EndSym = createTempSymbol (" resign_end_" );
1911
1989
1912
1990
emitPtrauthCheckAuthenticatedValue (AArch64::X16, AArch64::X17, AUTKey,
1991
+ AArch64PAuth::AuthCheckMethod::XPAC,
1913
1992
ShouldTrap, EndSym);
1914
1993
}
1915
1994
@@ -2194,6 +2273,7 @@ void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr &MI) {
2194
2273
: AArch64PACKey::DA);
2195
2274
2196
2275
emitPtrauthCheckAuthenticatedValue (AArch64::X16, AArch64::X17, AuthKey,
2276
+ AArch64PAuth::AuthCheckMethod::XPAC,
2197
2277
/* ShouldTrap=*/ true ,
2198
2278
/* OnFailure=*/ nullptr );
2199
2279
}
@@ -2326,6 +2406,7 @@ void AArch64AsmPrinter::LowerLOADgotAUTH(const MachineInstr &MI) {
2326
2406
(AuthOpcode == AArch64::AUTIA ? AArch64PACKey::IA : AArch64PACKey::DA);
2327
2407
2328
2408
emitPtrauthCheckAuthenticatedValue (AuthResultReg, AArch64::X17, AuthKey,
2409
+ AArch64PAuth::AuthCheckMethod::XPAC,
2329
2410
/* ShouldTrap=*/ true ,
2330
2411
/* OnFailure=*/ nullptr );
2331
2412
@@ -2395,6 +2476,8 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
2395
2476
// Do any manual lowerings.
2396
2477
switch (MI->getOpcode ()) {
2397
2478
default :
2479
+ assert (!AArch64InstrInfo::isTailCallReturnInst (*MI) &&
2480
+ " Unhandled tail call instruction" );
2398
2481
break ;
2399
2482
case AArch64::HINT: {
2400
2483
// CurrentPatchableFunctionEntrySym can be CurrentFnBegin only for
@@ -2538,6 +2621,8 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
2538
2621
? AArch64::X17
2539
2622
: AArch64::X16;
2540
2623
2624
+ emitPtrauthTailCallHardening (MI);
2625
+
2541
2626
unsigned DiscReg = AddrDisc;
2542
2627
if (Disc) {
2543
2628
if (AddrDisc != AArch64::NoRegister) {
@@ -2568,13 +2653,17 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
2568
2653
case AArch64::TCRETURNrix17:
2569
2654
case AArch64::TCRETURNrinotx16:
2570
2655
case AArch64::TCRETURNriALL: {
2656
+ emitPtrauthTailCallHardening (MI);
2657
+
2571
2658
MCInst TmpInst;
2572
2659
TmpInst.setOpcode (AArch64::BR);
2573
2660
TmpInst.addOperand (MCOperand::createReg (MI->getOperand (0 ).getReg ()));
2574
2661
EmitToStreamer (*OutStreamer, TmpInst);
2575
2662
return ;
2576
2663
}
2577
2664
case AArch64::TCRETURNdi: {
2665
+ emitPtrauthTailCallHardening (MI);
2666
+
2578
2667
MCOperand Dest;
2579
2668
MCInstLowering.lowerOperand (MI->getOperand (0 ), Dest);
2580
2669
MCInst TmpInst;
0 commit comments