Skip to content

Commit 9e86636

Browse files
committed
[CodeGen][AArch64] ptrauth intrinsic to safely construct relative pointer for swift coroutines
A ptrauth intrinsic for swift co-routine support that allows creation of signed pointer from offset stored at address relative to the pointer. Following C-like pseudo code (ignoring keys,discriminators) explains its operation: let rawptr = PACauth(inputptr); return PACsign( rawptr + *(int32*)(rawptr+addend) ) What: Authenticate a signed pointer, load a 32bit value at offset 'addend' from pointer, add this value to pointer, sign this new pointer. builtin: __builtin_ptrauth_auth_load_relative_and_sign intrinsic: ptrauth_auth_resign_load_relative
1 parent 5f1141d commit 9e86636

File tree

10 files changed

+722
-87
lines changed

10 files changed

+722
-87
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4643,6 +4643,12 @@ def PtrauthAuthAndResign : Builtin {
46434643
let Prototype = "void*(void*,int,void*,int,void*)";
46444644
}
46454645

4646+
def PtrauthAuthLoadRelativeAndSign : Builtin {
4647+
let Spellings = ["__builtin_ptrauth_auth_load_relative_and_sign"];
4648+
let Attributes = [CustomTypeChecking, NoThrow];
4649+
let Prototype = "void*(void*,int,void*,int,void*,ptrdiff_t)";
4650+
}
4651+
46464652
def PtrauthAuth : Builtin {
46474653
let Spellings = ["__builtin_ptrauth_auth"];
46484654
let Attributes = [CustomTypeChecking, NoThrow];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5584,12 +5584,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
55845584

55855585
case Builtin::BI__builtin_ptrauth_auth:
55865586
case Builtin::BI__builtin_ptrauth_auth_and_resign:
5587+
case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
55875588
case Builtin::BI__builtin_ptrauth_blend_discriminator:
55885589
case Builtin::BI__builtin_ptrauth_sign_generic_data:
55895590
case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
55905591
case Builtin::BI__builtin_ptrauth_strip: {
55915592
// Emit the arguments.
5592-
SmallVector<llvm::Value *, 5> Args;
5593+
SmallVector<llvm::Value *, 6> Args;
55935594
for (auto argExpr : E->arguments())
55945595
Args.push_back(EmitScalarExpr(argExpr));
55955596

@@ -5600,6 +5601,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
56005601

56015602
switch (BuiltinID) {
56025603
case Builtin::BI__builtin_ptrauth_auth_and_resign:
5604+
case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
56035605
if (Args[4]->getType()->isPointerTy())
56045606
Args[4] = Builder.CreatePtrToInt(Args[4], IntPtrTy);
56055607
[[fallthrough]];
@@ -5627,6 +5629,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
56275629
return Intrinsic::ptrauth_auth;
56285630
case Builtin::BI__builtin_ptrauth_auth_and_resign:
56295631
return Intrinsic::ptrauth_resign;
5632+
case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
5633+
return Intrinsic::ptrauth_resign_load_relative;
56305634
case Builtin::BI__builtin_ptrauth_blend_discriminator:
56315635
return Intrinsic::ptrauth_blend;
56325636
case Builtin::BI__builtin_ptrauth_sign_generic_data:

clang/lib/Headers/ptrauth.h

Lines changed: 25 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,31 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
165165
__builtin_ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \
166166
__new_data)
167167

168+
/* Authenticate a pointer using one scheme, load 32bit value at offset addend
169+
from the pointer, and add this value to the pointer, sign using specified
170+
scheme.
171+
172+
If the result is subsequently authenticated using the new scheme, that
173+
authentication is guaranteed to fail if and only if the initial
174+
authentication failed.
175+
176+
The value must be an expression of pointer type.
177+
The key must be a constant expression of type ptrauth_key.
178+
The extra data must be an expression of pointer or integer type;
179+
if an integer, it will be coerced to ptrauth_extra_data_t.
180+
The addend must be an immediate ptrdiff_t value.
181+
The result will have the same type as the original value.
182+
183+
This operation is guaranteed to not leave the intermediate value
184+
available for attack before it is re-signed.
185+
186+
Do not pass a null pointer to this function. A null pointer
187+
will not successfully authenticate. */
188+
#define ptrauth_auth_load_relative_and_sign(__value, __old_key, __old_data, \
189+
__new_key, __new_data, __addend) \
190+
__builtin_ptrauth_auth_load_relative_and_sign( \
191+
__value, __old_key, __old_data, __new_key, __new_data, __addend)
192+
168193
/* Authenticate a pointer using one scheme and resign it as a C
169194
function pointer.
170195
@@ -259,78 +284,6 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
259284
/* The value is ptrauth_string_discriminator("init_fini") */
260285
#define __ptrauth_init_fini_discriminator 0xd9d4
261286

262-
#else
263-
264-
#define ptrauth_strip(__value, __key) \
265-
({ \
266-
(void)__key; \
267-
__value; \
268-
})
269-
270-
#define ptrauth_blend_discriminator(__pointer, __integer) \
271-
({ \
272-
(void)__pointer; \
273-
(void)__integer; \
274-
((ptrauth_extra_data_t)0); \
275-
})
276-
277-
#define ptrauth_sign_constant(__value, __key, __data) \
278-
({ \
279-
(void)__key; \
280-
(void)__data; \
281-
__value; \
282-
})
283-
284-
#define ptrauth_sign_unauthenticated(__value, __key, __data) \
285-
({ \
286-
(void)__key; \
287-
(void)__data; \
288-
__value; \
289-
})
290-
291-
#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \
292-
__new_data) \
293-
({ \
294-
(void)__old_key; \
295-
(void)__old_data; \
296-
(void)__new_key; \
297-
(void)__new_data; \
298-
__value; \
299-
})
300-
301-
#define ptrauth_auth_function(__value, __old_key, __old_data) \
302-
({ \
303-
(void)__old_key; \
304-
(void)__old_data; \
305-
__value; \
306-
})
307-
308-
#define ptrauth_auth_data(__value, __old_key, __old_data) \
309-
({ \
310-
(void)__old_key; \
311-
(void)__old_data; \
312-
__value; \
313-
})
314-
315-
#define ptrauth_string_discriminator(__string) \
316-
({ \
317-
(void)__string; \
318-
((ptrauth_extra_data_t)0); \
319-
})
320-
321-
#define ptrauth_type_discriminator(__type) ((ptrauth_extra_data_t)0)
322-
323-
#define ptrauth_sign_generic_data(__value, __data) \
324-
({ \
325-
(void)__value; \
326-
(void)__data; \
327-
((ptrauth_generic_signature_t)0); \
328-
})
329-
330-
331-
#define ptrauth_cxx_vtable_pointer(key, address_discrimination, \
332-
extra_discrimination...)
333-
334287
#endif /* __has_feature(ptrauth_intrinsics) */
335288

336289
#endif /* __PTRAUTH_H */

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,6 +2818,19 @@ def int_ptrauth_resign : Intrinsic<[llvm_i64_ty],
28182818
[IntrNoMem, ImmArg<ArgIndex<1>>,
28192819
ImmArg<ArgIndex<3>>]>;
28202820

2821+
// Authenticate a signed pointer, load 32bit value at offset from pointer, add
2822+
// both, and sign it. The second (key) and third (discriminator) arguments
2823+
// specify the signing schema used for authenticating. The fourth and fifth
2824+
// arguments specify the schema used for signing. The sixth argument is addend
2825+
// added to pointer to load the relative offset. The signature must be valid.
2826+
// This is a combined form of int_ptrauth_resign for relative pointers
2827+
def int_ptrauth_resign_load_relative
2828+
: Intrinsic<[llvm_i64_ty],
2829+
[llvm_i64_ty, llvm_i32_ty, llvm_i64_ty, llvm_i32_ty,
2830+
llvm_i64_ty, llvm_i64_ty],
2831+
[IntrReadMem, ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<3>>,
2832+
ImmArg<ArgIndex<5>>]>;
2833+
28212834
// Strip the embedded signature out of a signed pointer.
28222835
// The second argument specifies the key.
28232836
// This behaves like @llvm.ptrauth.auth, but doesn't require the signature to

llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class AArch64AsmPrinter : public AsmPrinter {
168168
// Check authenticated LR before tail calling.
169169
void emitPtrauthTailCallHardening(const MachineInstr *TC);
170170

171-
// Emit the sequence for AUT or AUTPAC.
171+
// Emit the sequence for AUT, AUTPAC, or AUTRELLOADPAC.
172172
void emitPtrauthAuthResign(const MachineInstr *MI);
173173

174174
// Emit the sequence to compute the discriminator.
@@ -2066,8 +2066,9 @@ void AArch64AsmPrinter::emitPtrauthTailCallHardening(const MachineInstr *TC) {
20662066
}
20672067

20682068
void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
2069-
const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC;
2070-
2069+
const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC ||
2070+
MI->getOpcode() == AArch64::AUTRELLOADPAC;
2071+
const bool HasLoad = MI->getOpcode() == AArch64::AUTRELLOADPAC;
20712072
// We expand AUT/AUTPAC into a sequence of the form
20722073
//
20732074
// ; authenticate x16
@@ -2142,11 +2143,75 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
21422143
}
21432144

21442145
// We already emitted unchecked and checked-but-non-trapping AUTs.
2145-
// That left us with trapping AUTs, and AUTPACs.
2146+
// That left us with trapping AUTs, and AUTPA/AUTRELLOADPACs.
21462147
// Trapping AUTs don't need PAC: we're done.
21472148
if (!IsAUTPAC)
21482149
return;
21492150

2151+
if (HasLoad) {
2152+
int64_t Addend = MI->getOperand(6).getImm();
2153+
// incoming rawpointer in X16, X17 is not live at this point.
2154+
// LDSRWpre x17, x16, simm9 ; note: x16+simm9 used later.
2155+
if (isInt<9>(Addend)) {
2156+
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWpre)
2157+
.addReg(AArch64::X16)
2158+
.addReg(AArch64::X17)
2159+
.addReg(AArch64::X16)
2160+
.addImm(/*simm9:*/ Addend));
2161+
} else {
2162+
// x16 = x16 + Addend computation has 2 variants
2163+
if (isUInt<24>(Addend)) {
2164+
// variant 1: add x16, x16, Addend >> shift12 ls shift12
2165+
// This can take upto 2 instructions.
2166+
for (int BitPos = 0; BitPos != 24 && (Addend >> BitPos); BitPos += 12) {
2167+
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXri)
2168+
.addReg(AArch64::X16)
2169+
.addReg(AArch64::X16)
2170+
.addImm((Addend >> BitPos) & 0xfff)
2171+
.addImm(AArch64_AM::getShifterImm(
2172+
AArch64_AM::LSL, BitPos)));
2173+
}
2174+
} else {
2175+
// variant 2: accumulate constant in X17 16 bits at a time, and add to
2176+
// X16 This can take 2-5 instructions.
2177+
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::MOVZXi)
2178+
.addReg(AArch64::X17)
2179+
.addImm(Addend & 0xffff)
2180+
.addImm(AArch64_AM::getShifterImm(
2181+
AArch64_AM::LSL, 0)));
2182+
2183+
for (int Offset = 16; Offset < 64; Offset += 16) {
2184+
uint16_t Fragment = static_cast<uint16_t>(Addend >> Offset);
2185+
if (!Fragment)
2186+
continue;
2187+
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::MOVKXi)
2188+
.addReg(AArch64::X17)
2189+
.addReg(AArch64::X17)
2190+
.addImm(Fragment)
2191+
.addImm(/*shift:*/ Offset));
2192+
}
2193+
// addx x16, x16, x17
2194+
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs)
2195+
.addReg(AArch64::X16)
2196+
.addReg(AArch64::X16)
2197+
.addReg(AArch64::X17)
2198+
.addImm(0));
2199+
}
2200+
// ldrsw x17,x16(0)
2201+
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWui)
2202+
.addReg(AArch64::X17)
2203+
.addReg(AArch64::X16)
2204+
.addImm(0));
2205+
}
2206+
// addx x16, x16, x17
2207+
EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs)
2208+
.addReg(AArch64::X16)
2209+
.addReg(AArch64::X16)
2210+
.addReg(AArch64::X17)
2211+
.addImm(0));
2212+
2213+
} /* HasLoad == true */
2214+
21502215
auto PACKey = (AArch64PACKey::ID)MI->getOperand(3).getImm();
21512216
uint64_t PACDisc = MI->getOperand(4).getImm();
21522217
unsigned PACAddrDisc = MI->getOperand(5).getReg();
@@ -2864,6 +2929,7 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
28642929

28652930
case AArch64::AUT:
28662931
case AArch64::AUTPAC:
2932+
case AArch64::AUTRELLOADPAC:
28672933
emitPtrauthAuthResign(MI);
28682934
return;
28692935

llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,12 +1544,15 @@ void AArch64DAGToDAGISel::SelectPtrauthAuth(SDNode *N) {
15441544

15451545
void AArch64DAGToDAGISel::SelectPtrauthResign(SDNode *N) {
15461546
SDLoc DL(N);
1547-
// IntrinsicID is operand #0
1548-
SDValue Val = N->getOperand(1);
1549-
SDValue AUTKey = N->getOperand(2);
1550-
SDValue AUTDisc = N->getOperand(3);
1551-
SDValue PACKey = N->getOperand(4);
1552-
SDValue PACDisc = N->getOperand(5);
1547+
// IntrinsicID is operand #0, if W_CHAIN it is #1
1548+
int OffsetBase = N->getOpcode() == ISD::INTRINSIC_W_CHAIN ? 1 : 0;
1549+
SDValue Val = N->getOperand(OffsetBase + 1);
1550+
SDValue AUTKey = N->getOperand(OffsetBase + 2);
1551+
SDValue AUTDisc = N->getOperand(OffsetBase + 3);
1552+
SDValue PACKey = N->getOperand(OffsetBase + 4);
1553+
SDValue PACDisc = N->getOperand(OffsetBase + 5);
1554+
uint32_t IntNum = N->getConstantOperandVal(OffsetBase + 0);
1555+
bool HasLoad = IntNum == Intrinsic::ptrauth_resign_load_relative;
15531556

15541557
unsigned AUTKeyC = cast<ConstantSDNode>(AUTKey)->getZExtValue();
15551558
unsigned PACKeyC = cast<ConstantSDNode>(PACKey)->getZExtValue();
@@ -1568,11 +1571,22 @@ void AArch64DAGToDAGISel::SelectPtrauthResign(SDNode *N) {
15681571
SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
15691572
AArch64::X16, Val, SDValue());
15701573

1571-
SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, PACKey,
1572-
PACConstDisc, PACAddrDisc, X16Copy.getValue(1)};
1574+
if (HasLoad) {
1575+
SDValue Addend = N->getOperand(OffsetBase + 6);
1576+
SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc,
1577+
PACKey, PACConstDisc, PACAddrDisc,
1578+
Addend, X16Copy.getValue(1)};
15731579

1574-
SDNode *AUTPAC = CurDAG->getMachineNode(AArch64::AUTPAC, DL, MVT::i64, Ops);
1575-
ReplaceNode(N, AUTPAC);
1580+
SDNode *AUTRELLOADPAC = CurDAG->getMachineNode(AArch64::AUTRELLOADPAC, DL,
1581+
MVT::i64, MVT::Other, Ops);
1582+
ReplaceNode(N, AUTRELLOADPAC);
1583+
} else {
1584+
SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, PACKey,
1585+
PACConstDisc, PACAddrDisc, X16Copy.getValue(1)};
1586+
1587+
SDNode *AUTPAC = CurDAG->getMachineNode(AArch64::AUTPAC, DL, MVT::i64, Ops);
1588+
ReplaceNode(N, AUTPAC);
1589+
}
15761590
}
15771591

15781592
bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) {
@@ -5769,6 +5783,9 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) {
57695783
{AArch64::BF2CVT_2ZZ_BtoH, AArch64::F2CVT_2ZZ_BtoH}))
57705784
SelectCVTIntrinsicFP8(Node, 2, Opc);
57715785
return;
5786+
case Intrinsic::ptrauth_resign_load_relative:
5787+
SelectPtrauthResign(Node);
5788+
return;
57725789
}
57735790
} break;
57745791
case ISD::INTRINSIC_WO_CHAIN: {

llvm/lib/Target/AArch64/AArch64InstrInfo.td

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,6 +2159,26 @@ let Predicates = [HasPAuth] in {
21592159
let Uses = [X16];
21602160
}
21612161

2162+
// Similiar to AUTPAC, except a 32bit value is loaded at Addend offset from
2163+
// pointer and this value is added to the pointer before signing. This
2164+
// directly manipulates x16/x17, which are the only registers the OS
2165+
// guarantees are safe to use for sensitive operations.
2166+
def AUTRELLOADPAC
2167+
: Pseudo<(outs),
2168+
(ins i32imm:$AUTKey, i64imm:$AUTDisc, GPR64:$AUTAddrDisc,
2169+
i32imm:$PACKey, i64imm:$PACDisc, GPR64noip:$PACAddrDisc,
2170+
i64imm:$Addend),
2171+
[]>,
2172+
Sched<[WriteI, ReadI]> {
2173+
let isCodeGenOnly = 1;
2174+
let hasSideEffects = 1;
2175+
let mayStore = 0;
2176+
let mayLoad = 1;
2177+
let Size = 84;
2178+
let Defs = [X16, X17, NZCV];
2179+
let Uses = [X16];
2180+
}
2181+
21622182
// Materialize a signed global address, with adrp+add and PAC.
21632183
def MOVaddrPAC : Pseudo<(outs),
21642184
(ins i64imm:$Addr, i32imm:$Key,

0 commit comments

Comments
 (0)